ng-maintain-utils
Description
A private collection of utilities for developing tools to help maintain (AngularJS-related) GitHub repositories.
Usage
You should generally not use it. You would use tools built on top of it, for example:
I may use it for building other tools (see above). Here is a brief overview of what's in the box:
-
AbstractCli
: Can serve as a base-class for creating aCli
object that can orchestrate the execution of some type of work, based on a list of "raw" command-line arguments. It can display version info (with--version
), show usage instructions (with--usage
), outline the commands that need to be executed to complete the task at hand (with--instructions
) - a sort of "dry-run", report the beginning/end of execution, etc.It exposes the following (public) methods:
-
getPhases(): Phase[]
(abstract): This method must be overwritten and return an array ofPhase
objects (to be used for displaying instructions in the "dry-run" mode). -
run(rawArgs: string[], doWork?: ({[key: string]: string}) => any): Promise
: Parse the arguments and take appropriate action (see above).
It also provides a number of "protected" methods, that can be overwritten by sub-classes:
_displayExperimentalTool(): void
_displayHeader(headerTmpl: string, input: {[key: string]: string}): void
_displayInstructions(phases: Phase[], input: {[key: string]: string}): void
_displayUsage(usageMessage: string): void
_displayVersionInfo(): void
_getAndValidateInput(rawArgs: string[], argSpecs: ArgSpec[]): Promise<{[key: string]: string}>
_insertEmptyLine<T>(value: T, isRejection?: boolean): T|Promise<T>
_theHappyEnd<T>(value: T): T
_theUnhappyEnd(err: any): Promise<any>
Requires:
-
config:
Config
-
-
ArgSpec
/ArgSpec.Unnamed
: Represents the specification for a command-line argument. When applied on a parsed arguments object (such as the ones returned byUtils#parseArgs()
) it will extract the corresponding argument's value (either by name (ArgSpec
) or by position (ArgSpec.Unnamed
)), fall back to a default value if necessary, vaidate the value and assign it to the specifiedinput
object under the appropriatekey
.Requires:
-
index:
number
(ArgSpec.Unnamed
only) -
key:
string
-
validator:
(value: any) => boolean
-
errorCode:
string
-
defaultValue?:
string|boolean
-
index:
-
CleanUper
: A utility to help coordinate arbitrary tasks with their associated clean-up process.The general idea is this:
- Schedule a task to clean up after
something
. - Do
something
. - ...possibly do other things here...
- If anything goes wrong, the app will be able to clean up (or show instructions to the user).
- When cleaning up after
something
is no longer necessary, unschedule the clean-up task.
Provides the following methods:
-
cleanUp(listOnly: boolean): Promise
: Perform all clean-up tasks (or just list them). (Either way, the clean-up task queue is emptied.) -
getCleanUpPhase(): Phase
: Returns a clean-upPhase
object (suitable forUiUtils#phase()
). -
hasTasks(): boolean
: Returns whether or not there are any clean-up tasks scheduled. -
registerTask(description: string, cb: () => Promise): TaskId
: Register a task with theCleanUper
. You can use the returned, uniqueTaskId
for scheduling/unscheduling the task. -
schedule(taskId: TaskId): void
: Schedule a clean-up task. -
unschedule(taskId: TaskId): void
: Schedule (the last instance of) a clean-up task. -
withTask(taskId: TaskId, fn: () => any): Promise
: ScheduletaskId
and executefn
(can also return a promise). If all goes well, unscheduletaskId
. If an error occurs, leavetaskId
in the clean-up task queue.
Requires:
-
logger:
Logger
- Schedule a task to clean up after
-
Config
: Creates aConfig
object based on the specifiedmessages
andargSpecs
(falling back to some default values if necessary). It exposes the following properties:-
argSpecs
: A (possibly empty) array ofArgSpec
objects. -
defaults
: A{[argument: string]: string|number}
map of default values per command-line argument keys. (Automatically extracted forargSpecs
.) -
messages
: A (possibly nested){[messageKey: string]: string}
map with at least the following messages:usage
instructionsHeaderTmpl
headerTmpl
-
errors
:ERROR_unexpected
-
warnings
:WARN_experimentalTool
-
versionInfo
: A{name: string, version: string}
map with values retrieved from the main module'spackage.json
(i.e. the firstpackage.json
to be found starting from the main file's directory and moving upwards).
Requires:
-
messages:
{[messageKey: string]: string}
-
argSpecs:
ArgSpec[]
-
-
GitUtils
: A collection ofGit
-related command-wrappers and utilities. Mainly spawnsGit
commands in a separate process and (promises to) return the output. Support for commands is added in an "as-needed" basis. Currently, the available commands/utilities include:abortAm(): Promise
abortRebase(): Promise
checkout(branch: string): Promise
clean(mode?: string = 'interactive'): Promise
countCommitsSince(commit: string): Promise<number>
createBranch(branch: string): Promise
deleteBranch(branch: string, force?: boolean): Promise
diff(commit: string, noColor?: boolean): Promise
diffWithHighlight(commit: string): Promise
diffWithHighlight2(commit: string): Promise
getCommitMessage(commit: string): Promise<string>
getLastCommitMessage(): Promise<string>
log(oneline?: boolean, count?: number, noDecorate?: boolean): Promise
mergePullRequest(prUrl: string): Promise
pull(branch: string, rebase?: boolean): Promise
push(branch: string): Promise
rebase(commit: string|number, interactive?: boolean): Promise
reset(commit: string, hard?: boolean): Promise
setLastCommitMessage(message: string): Promise
updateLastCommitMessage(getNewMessage: (oldMessage: string) => string): Promise
Requires:
-
cleanUper:
CleanUper
-
utils:
Utils
-
GitUtils.DiffHighlighter
: Can be used to enhance a diff by highlighting areas of interest. The general implementation is loosely based on the idea of diff-highlight, although the matching heuristics and coloring (among other things) are different.It exposes the underlying streams via:
getInputStream(): stream.PassThrough
getOutputStream(): stream.PassThrough
Requires:
-
styles?:
{ lineRemoved?: (text: string) => string, lineAdded?: (text: string) => string, areaRemoved?: (text: string) => string, areaAdded?: (text: string) => string }
-
GitUtils.DiffHighlighter2
: An alternative, API-compatible implementation ofGitUtils.DiffHighlighter
. The highlighting is more accurate, as it is able to only highlight the regions that have changed. The main drawback is that it fails to show removed/added empty lines, because of its dependency onGit
s--word-diff=plain
option.Similar to
GitUtils.DiffHighlighter
, it exposes the underlying streams via:getInputStream(): stream.PassThrough
getOutputStream(): stream.PassThrough
Requires:
-
styles?:
{ lineRemoved?: (text: string) => string, lineAdded?: (text: string) => string, areaRemoved?: (text: string) => string, areaAdded?: (text: string) => string }
-
Logger
: A simple helper providing minimal logging utilities. This is mostly used in order to make it easier to test logging behavior without affectingconsole
methods. Provides the following methods:-
debug()
: Delegate toconsole.debug()
. -
error()
: Delegate toconsole.error()
. -
info()
: Delegate toconsole.info()
. -
log()
: Delegate toconsole.log()
. -
warn()
: Delegate toconsole.warn()
.
-
-
Phase
: A simple wrapper for "phase" entities (with validation). A "phase" is a description of a unit of work, including an ID, a short description, a list of the tasks involved and an error message (or code) specific to this "phase".Requires:
-
id:
string
-
description:
string
-
instructions?:
string[]
-
error?:
string
(Can be either an error message or an error code.)
-
id:
-
UiUtils
: A collection of utilities useful for interacting with the user, including:-
askQuestion()
: Prompt the user with a question and (promise to) return the answer. -
askYesOrNoQuestion()
: Prompt the user with a yes-or-no question (e.g. a confirmation) and (promise to) resolve (for "yes") or reject (for "no"). -
offerToCleanUp()
: Requests confirmation to perform the scheduled clean-up tasks. If the user turns the offer down, it will just list the pending tasks instead. -
phase()
: It will report the beginning and end of a "phase" (seePhase
), do some work and properly handle possible errors (by means ofreportAndRejectFnGen()
). -
reportAndRejectFnGen()
: Generates a callback that will report the specified error (plus any extra error provided during invokation), will offer to clean up (if there are pending tasks and not configured otherwise) and return a rejection.
Requires:
-
logger:
Logger
-
cleanUper:
CleanUper
-
errorMessages:
{[errorCode: string]: string}
-
-
Utils
: A collection of low-level, specific-purpose utilities, including:-
asPromised()
: Convert callback-based functions to promise-based. -
interpolate()
: Replace{{...}}
placeholders in a string with values. -
parseArgs()
: Parse command-line arguments (and remove surrounding quotes). -
resetOutputStyleOnExit()
: Ensure the output style is reset when a process exists. -
spawnAsPromised()
: Spawn a sub-shell to run a (series of) command(s) with support for piping. -
waitAsPromised()
:setTimeout()
wrapped in a promise.
-
Testing
The following test-types/modes are available:
-
Code-linting:
npm run lint
Lint JavaScript files using ESLint. -
Unit tests:
npm run test-unit
Run all the unit tests once. These tests are quick and suitable to be run on every change. -
E2E tests:
npm run test-e2e
Run all the end-to-end tests once. These test may hit actual API endpoints or perform expensive I/O operations and are considerably slower than unit tests. -
All tests:
npm test
/npm run test
Run all of the above tests (code-linting, unit tests, e2e tests). This command is automatically run beforenpm version
andnpm publish
. -
"Watch" mode:
npm run test-watch
Watch all files and rerun the unit tests whenever something changes. For performance reasons, code-linting and e2e tests are omitted.