bump-cli
Version:
The Bump CLI is used to interact with your API documentation hosted on Bump.sh by using the API of developers.bump.sh
162 lines (154 loc) • 7.89 kB
JavaScript
import { ux } from '@oclif/core';
import { CLIError } from '@oclif/core/errors';
import chalk from 'chalk';
import { fileArg } from '../args.js';
import { BaseCommand } from '../base-command.js';
import { DefinitionDirectory } from '../core/definition-directory.js';
import { Deploy as CoreDeploy } from '../core/deploy.js';
import { isDir } from '../core/utils/file.js';
import { confirm as promptConfirm } from '../core/utils/prompts.js';
import { API } from '../definition.js';
import * as flagsBuilder from '../flags.js';
export default class Deploy extends BaseCommand {
static args = { file: fileArg };
static description = 'Create a new version of your documentation from the given file or URL.';
static examples = [
`Deploy a new version of ${chalk.underline('an existing documentation')}
${chalk.dim('$ bump deploy FILE --doc <your_doc_id_or_slug> --token <your_doc_token>')}
* Let's deploy on Bump.sh... done
* Your new documentation version will soon be ready
`,
`Deploy a new version of ${chalk.underline('an existing documentation attached to a hub')}
${chalk.dim('$ bump deploy FILE --doc <doc_slug> --hub <your_hub_id_or_slug> --token <your_doc_token>')}
* Let's deploy on Bump.sh... done
* Your new documentation version will soon be ready
`,
`Deploy a whole directory of ${chalk.underline('API definitions files to a hub')}
${chalk.dim('$ bump deploy DIR --filename-pattern *-{slug}-api --hub <hub_slug> --token <hub_token>')}
We've found 2 valid API definitions to deploy
└─ DIR
└─ source-my-service-api.yml (OpenAPI spec version 3.1.0)
└─ source-my-jobs-service-api.yml (AsyncAPI spec version 2.6.0)
Let's deploy those documentations to your <hub_slug> hub on Bump.sh
* Your new documentation version will soon be ready
Let's deploy a new version to your my-service documentation on Bump.sh... done
* Your new documentation version will soon be ready
Let's deploy a new version to your my-jobs-service documentation on Bump.sh... done
`,
`${chalk.underline('Validate a new documentation version')} before deploying it
${chalk.dim('$ bump deploy FILE --dry-run --doc <doc_slug> --token <your_doc_token>')}
* Let's validate on Bump.sh... done
* Definition is valid
`,
];
static flags = {
'auto-create': flagsBuilder.autoCreate(),
branch: flagsBuilder.branch(),
doc: flagsBuilder.doc(),
'doc-name': flagsBuilder.docName(),
'dry-run': flagsBuilder.dryRun(),
'filename-pattern': flagsBuilder.filenamePattern(),
hub: flagsBuilder.hub(),
interactive: flagsBuilder.interactive(),
overlay: flagsBuilder.overlay(),
preview: flagsBuilder.preview(),
token: flagsBuilder.token(),
};
async deployDirectory(dir, dryRun, token, hub, autoCreate, interactive, filenamePattern, documentationName, branch, overlays) {
const definitionDirectory = new DefinitionDirectory(dir, filenamePattern);
await definitionDirectory.readDefinitions();
await ux.action.pauseAsync(async () => {
definitionDirectory.stdoutDefinitions();
// In “interactive” mode we ask the user if he wants to add more
// definitions to deploy. He is thus presented a form to select
// some files from the target directory.
if (interactive) {
let confirm = true;
if (definitionDirectory.definitionsExists()) {
confirm = await promptConfirm('Do you want to add more files to deploy?');
}
if (confirm) {
await ux.action.pauseAsync(async () => {
await definitionDirectory.interactiveSelection();
});
}
}
});
if (definitionDirectory.definitionsExists()) {
await ux.action.pauseAsync(async () => {
definitionDirectory.stdoutDefinitions();
if (interactive) {
await definitionDirectory.sequentialMap(async (definition) => {
await definitionDirectory.renameToConvention(definition);
});
}
});
ux.action.status = `...to your ${hub} hub on Bump.sh`;
await definitionDirectory.sequentialMap(async (definition) => {
await this.deploySingleFile(definition.definition, dryRun, definition.slug, token, hub, autoCreate, definition.slug || documentationName, branch, overlays);
});
}
else {
throw new CLIError(`No documentation found in ${dir} with the pattern '${filenamePattern}'.\nYou should check with the ${chalk.dim('--filename-pattern')} flag to select your files from your naming convention.\nIf you don't have a naming convention we can help naming your API definition files:\nTry the ${chalk.dim('--interactive')} flag for that.`);
}
}
async deploySingleFile(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay, temporary) {
ux.action.status = `...a new version to your ${documentation} documentation`;
const response = await new CoreDeploy(this.bump).run(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay, temporary);
if (dryRun) {
ux.stdout(ux.colorize('green', 'Definition is valid'));
}
else if (response) {
process.stdout.write(ux.colorize('green', `Your ${documentation} documentation...`));
ux.stdout(ux.colorize('green', `has received a new ${temporary ? 'preview' : 'deployment'} which will soon be ready at:`));
ux.stdout(ux.colorize('underline', response.doc_public_url));
}
else {
ux.warn(`Your ${documentation} documentation has not changed`);
}
}
/*
Oclif doesn't type parsed args & flags correctly and especially
required-ness which is not known by the compiler, thus the use of
the non-null assertion '!' in this command.
See https://github.com/oclif/oclif/issues/301 for details
*/
async run() {
const { args, flags } = await this.parse(Deploy);
const [dryRun, documentation, token, hub, autoCreate, interactive, filenamePattern, documentationName, branch, overlay, temporary,] = [
flags['dry-run'],
flags.doc,
flags.token,
flags.hub,
flags['auto-create'],
flags.interactive,
/* Flags.filenamePattern has a default value, so it's always defined. But
* oclif types doesn't detect it */
flags['filename-pattern'],
flags['doc-name'],
flags.branch,
flags.overlay,
/* when --preview is provided, generate temporary version */
flags.preview,
];
const action = dryRun ? 'validate' : temporary ? 'preview' : 'deploy';
ux.action.start(`Let's ${action} on Bump.sh`);
if (isDir(args.file)) {
if (hub) {
await this.deployDirectory(args.file, dryRun, token, hub, autoCreate, interactive, filenamePattern, documentationName, branch, overlay);
}
else {
throw new CLIError('Missing required flag --hub when deploying an entire directory');
}
}
else if (documentation) {
const api = await API.load(args.file);
this.d(`${args.file} looks like an ${api.specName} spec version ${api.version}`);
await this.deploySingleFile(api, dryRun, documentation, token, hub, autoCreate, documentationName, branch, overlay, temporary);
}
else {
throw new CLIError('Missing required flag --doc=<slug>');
}
ux.action.stop();
}
}