mirror of
https://github.com/appium/appium.git
synced 2026-04-24 04:18:49 -05:00
feat(docutils): add support for deploying with mike
This is enabled via `appium-docs build --deploy`. Instead of invoking `mkdocs` directly, `mike` will be invoked. Implementation is in `lib/builder/deploy.ts`. It's very similar to the MkDocs implementation in `lib/builder/site.ts`; just with new/different options. I marked the `Mike` class as deprecated, since the new one is fancy
This commit is contained in:
@@ -0,0 +1,188 @@
|
||||
/**
|
||||
* Functions for running `mike`
|
||||
*
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {exec, SubProcess, TeenProcessExecOptions} from 'teen_process';
|
||||
import {
|
||||
DEFAULT_DEPLOY_BRANCH,
|
||||
DEFAULT_DEPLOY_REMOTE,
|
||||
DEFAULT_SERVE_HOST,
|
||||
DEFAULT_SERVE_PORT,
|
||||
NAME_BIN,
|
||||
NAME_MKDOCS_YML,
|
||||
} from '../constants';
|
||||
import {DocutilsError} from '../error';
|
||||
import {findMkDocsYml, whichMike} from '../fs';
|
||||
import logger from '../logger';
|
||||
import {argify, stopwatch, TeenProcessSubprocessStartOpts} from '../util';
|
||||
|
||||
const log = logger.withTag('builder:deploy');
|
||||
|
||||
/**
|
||||
* Runs `mike serve`
|
||||
* @param args Extra args to `mike build`
|
||||
* @param opts Extra options for `teen_process.Subprocess.start`
|
||||
* @param mikePath Path to `mike` executable
|
||||
*/
|
||||
async function doServe(
|
||||
args: string[] = [],
|
||||
{startDetector, detach, timeoutMs}: TeenProcessSubprocessStartOpts = {},
|
||||
mikePath?: string
|
||||
) {
|
||||
mikePath = mikePath ?? (await whichMike());
|
||||
const finalArgs = ['serve', ...args];
|
||||
log.debug('Launching %s with args: %O', mikePath, finalArgs);
|
||||
const proc = new SubProcess(mikePath, finalArgs);
|
||||
return await proc.start(startDetector, detach, timeoutMs);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `mike build`
|
||||
* @param args Extra args to `mike build`
|
||||
* @param opts Extra options to `teen_process.exec`
|
||||
* @param mikePath Path to `mike` executable
|
||||
*/
|
||||
async function doDeploy(args: string[] = [], opts: TeenProcessExecOptions = {}, mikePath?: string) {
|
||||
mikePath = mikePath ?? (await whichMike());
|
||||
const finalArgs = ['deploy', ...args];
|
||||
log.debug('Launching %s with args: %O', mikePath, finalArgs);
|
||||
return await exec(mikePath, finalArgs, opts);
|
||||
}
|
||||
|
||||
/**
|
||||
* Runs `mike build` or `mike serve`
|
||||
* @param opts
|
||||
*/
|
||||
export async function deploy({
|
||||
mkDocsYml: mkDocsYmlPath,
|
||||
cwd = process.cwd(),
|
||||
serve = false,
|
||||
push = false,
|
||||
branch = DEFAULT_DEPLOY_BRANCH,
|
||||
remote = DEFAULT_DEPLOY_REMOTE,
|
||||
prefix,
|
||||
message,
|
||||
deployVersion,
|
||||
alias,
|
||||
rebase = true,
|
||||
port = DEFAULT_SERVE_PORT,
|
||||
host = DEFAULT_SERVE_HOST,
|
||||
serveOpts,
|
||||
execOpts,
|
||||
}: DeployOpts = {}) {
|
||||
const stop = stopwatch('deploy');
|
||||
mkDocsYmlPath = mkDocsYmlPath ?? (await findMkDocsYml(cwd));
|
||||
if (!mkDocsYmlPath) {
|
||||
throw new DocutilsError(
|
||||
`Could not find ${NAME_MKDOCS_YML} from ${cwd}; run "${NAME_BIN} init" to create it`
|
||||
);
|
||||
}
|
||||
|
||||
const mikeOpts = {
|
||||
'config-file': mkDocsYmlPath,
|
||||
push,
|
||||
remote,
|
||||
branch,
|
||||
prefix,
|
||||
message,
|
||||
deployVersion,
|
||||
alias,
|
||||
rebase,
|
||||
port,
|
||||
host,
|
||||
};
|
||||
const mikeArgs = argify(mikeOpts);
|
||||
if (serve) {
|
||||
// unsure about how SIGHUP is handled here
|
||||
await doServe(mikeArgs, serveOpts);
|
||||
} else {
|
||||
await doDeploy(mikeArgs, execOpts);
|
||||
|
||||
log.success('Mike finished deployment into branch %s (%dms)', branch, stop());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Options for {@linkcode deploy}.
|
||||
*/
|
||||
export interface DeployOpts {
|
||||
/**
|
||||
* Path to `mike.yml`
|
||||
*/
|
||||
mkDocsYml?: string;
|
||||
|
||||
/**
|
||||
* Current working directory
|
||||
* @defaultValue `process.cwd()`
|
||||
*/
|
||||
cwd?: string;
|
||||
|
||||
/**
|
||||
* Path to `package.json`
|
||||
*
|
||||
* Used to find `mike.yml` if unspecified.
|
||||
*/
|
||||
packageJson?: string;
|
||||
|
||||
/**
|
||||
* If `true`, run `mike serve` instead of `mike build`
|
||||
*/
|
||||
serve?: boolean;
|
||||
|
||||
/**
|
||||
* If `true`, push `branch` to `remote`
|
||||
*/
|
||||
push?: boolean;
|
||||
/**
|
||||
* Branch to commit to
|
||||
* @defaultValue gh-pages
|
||||
*/
|
||||
branch?: string;
|
||||
/**
|
||||
* Remote to push to
|
||||
* @defaultValue origin
|
||||
*/
|
||||
remote?: string;
|
||||
/**
|
||||
* Subdirectory within `branch` to deploy to
|
||||
*/
|
||||
prefix?: string;
|
||||
/**
|
||||
* Commit message
|
||||
*/
|
||||
message?: string;
|
||||
/**
|
||||
* Version (dir) to deploy build to
|
||||
*/
|
||||
deployVersion?: string;
|
||||
/**
|
||||
* Alias for the build (e.g., `latest`); triggers alias update
|
||||
*/
|
||||
alias?: string;
|
||||
/**
|
||||
* If `true`, rebase `branch` before pushing
|
||||
*/
|
||||
rebase?: boolean;
|
||||
/**
|
||||
* Port to serve on
|
||||
* @defaultValue 8000
|
||||
*/
|
||||
port?: number;
|
||||
/**
|
||||
* Host to serve on
|
||||
* @defaultValue localhost
|
||||
*/
|
||||
host?: string;
|
||||
|
||||
/**
|
||||
* Extra options for {@linkcode teen_process.exec}
|
||||
*/
|
||||
execOpts?: TeenProcessExecOptions;
|
||||
|
||||
/**
|
||||
* Extra options for {@linkcode teen_process.Subprocess.start}
|
||||
*/
|
||||
serveOpts?: TeenProcessSubprocessStartOpts;
|
||||
}
|
||||
@@ -1,2 +1,3 @@
|
||||
export * from './deploy';
|
||||
export * from './site';
|
||||
export * from './reference';
|
||||
|
||||
@@ -1,12 +1,16 @@
|
||||
import {CommandModule, InferredOptionTypes, Options} from 'yargs';
|
||||
import {buildReferenceDocs, buildSite} from '../../builder';
|
||||
import {buildReferenceDocs, buildSite, deploy} from '../../builder';
|
||||
import {NAME_BIN} from '../../constants';
|
||||
import logger from '../../logger';
|
||||
import {updateNav} from '../../nav';
|
||||
import {stopwatch} from '../../util';
|
||||
|
||||
const log = logger.withTag('build');
|
||||
|
||||
const NAME_GROUP_BUILD = 'Build API:';
|
||||
const NAME_GROUP_BUILD = 'Build Options:';
|
||||
const NAME_GROUP_DEPLOY = 'Deployment Options:';
|
||||
const NAME_GROUP_SERVE = 'Serve Options:';
|
||||
const NAME_GROUP_BUILD_PATHS = 'Paths:';
|
||||
|
||||
const opts = {
|
||||
reference: {
|
||||
@@ -24,7 +28,7 @@ const opts = {
|
||||
'site-dir': {
|
||||
alias: 'd',
|
||||
describe: 'HTML output directory',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
@@ -35,7 +39,7 @@ const opts = {
|
||||
'package-json': {
|
||||
defaultDescription: './package.json',
|
||||
describe: 'Path to package.json',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -52,7 +56,16 @@ const opts = {
|
||||
'tsconfig-json': {
|
||||
defaultDescription: './tsconfig.json',
|
||||
describe: 'Path to tsconfig.json',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
},
|
||||
'mkdocs-yml': {
|
||||
defaultDescription: './mkdocs.yml',
|
||||
description: 'Path to mkdocs.yml',
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
@@ -61,25 +74,116 @@ const opts = {
|
||||
'typedoc-json': {
|
||||
defaultDescription: './typedoc.json',
|
||||
describe: 'Path to typedoc.json',
|
||||
group: NAME_GROUP_BUILD,
|
||||
group: NAME_GROUP_BUILD_PATHS,
|
||||
nargs: 1,
|
||||
normalize: true,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
},
|
||||
'reference-header': {
|
||||
describe: 'Navigation header for API reference',
|
||||
default: 'Reference',
|
||||
all: {
|
||||
describe: 'Output all reference docs (not just Appium comands)',
|
||||
group: NAME_GROUP_BUILD,
|
||||
implies: 'site',
|
||||
type: 'boolean',
|
||||
},
|
||||
deploy: {
|
||||
describe: 'Commit HTML output',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'boolean',
|
||||
implies: 'site',
|
||||
},
|
||||
push: {
|
||||
describe: 'Push after deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'boolean',
|
||||
implies: 'deploy',
|
||||
},
|
||||
branch: {
|
||||
alias: 'b',
|
||||
describe: 'Branch to commit to',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'string',
|
||||
requiresArg: true,
|
||||
nargs: 1,
|
||||
defaultDescription: 'gh-pages',
|
||||
},
|
||||
remote: {
|
||||
alias: 'r',
|
||||
describe: 'Remote to push to',
|
||||
implies: ['deploy', 'push'],
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'string',
|
||||
requiresArg: true,
|
||||
nargs: 1,
|
||||
defaultDescription: 'origin',
|
||||
},
|
||||
prefix: {
|
||||
describe: 'Subdirectory within <branch> to commit to',
|
||||
implies: ['deploy', 'branch'],
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
type: 'string',
|
||||
},
|
||||
'no-reference-header': {
|
||||
describe: 'Do not add a navigation header for API reference',
|
||||
group: NAME_GROUP_BUILD,
|
||||
message: {
|
||||
alias: 'm',
|
||||
describe: 'Commit message',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
},
|
||||
'deploy-version': {
|
||||
describe: 'Version (directory) to deploy build to',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
defaultDescription: '(derived from package.json)',
|
||||
},
|
||||
alias: {
|
||||
describe: 'Alias for the build (e.g., "latest"); triggers alias update',
|
||||
implies: 'deploy',
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
defaultDescription: 'latest',
|
||||
},
|
||||
rebase: {
|
||||
describe: 'Rebase <branch> with remote before deploy',
|
||||
implies: ['deploy', 'branch', 'remote'],
|
||||
group: NAME_GROUP_DEPLOY,
|
||||
type: 'boolean',
|
||||
},
|
||||
serve: {
|
||||
describe: 'Start development server',
|
||||
group: NAME_GROUP_SERVE,
|
||||
type: 'boolean',
|
||||
},
|
||||
port: {
|
||||
alias: 'p',
|
||||
describe: 'Development server port',
|
||||
group: NAME_GROUP_SERVE,
|
||||
type: 'number',
|
||||
defaultDescription: '8000',
|
||||
implies: 'serve',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
},
|
||||
host: {
|
||||
alias: 'h',
|
||||
describe: 'Development server host',
|
||||
group: NAME_GROUP_SERVE,
|
||||
type: 'string',
|
||||
nargs: 1,
|
||||
requiresArg: true,
|
||||
implies: 'serve',
|
||||
defaultDescription: 'localhost',
|
||||
},
|
||||
} as const;
|
||||
|
||||
opts as Record<string, Options>;
|
||||
@@ -88,7 +192,17 @@ type BuildOptions = InferredOptionTypes<typeof opts>;
|
||||
const buildCommand: CommandModule<{}, BuildOptions> = {
|
||||
command: 'build',
|
||||
describe: 'Build Appium extension documentation',
|
||||
builder: opts,
|
||||
builder: (yargs) =>
|
||||
yargs.options(opts).check((argv) => {
|
||||
// either this method doesn't provide camel-cased props, or the types are wrong.
|
||||
if (argv.deploy === true && argv['site-dir']) {
|
||||
log.error(
|
||||
`--site-dir is unsupported when running "${NAME_BIN} deploy"; use --prefix if needd, but remember that the default behavior is to deploy to the root of the branch (${argv.branch}) instead of a subdirectory`
|
||||
);
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}),
|
||||
async handler(args) {
|
||||
const stop = stopwatch('build');
|
||||
log.debug('Build command called with args: %O', args);
|
||||
@@ -98,12 +212,16 @@ const buildCommand: CommandModule<{}, BuildOptions> = {
|
||||
'Cannot use both --no-site (--site=false) and --no-reference (--reference=false)'
|
||||
);
|
||||
}
|
||||
if (args.site) {
|
||||
if (args.reference) {
|
||||
await buildReferenceDocs(args);
|
||||
}
|
||||
if (args.reference) {
|
||||
if (args.site) {
|
||||
await updateNav(args);
|
||||
await buildSite(args);
|
||||
if (args.deploy) {
|
||||
await deploy(args);
|
||||
} else {
|
||||
await buildSite(args);
|
||||
}
|
||||
}
|
||||
log.success('Done! (total: %dms)', stop());
|
||||
},
|
||||
|
||||
@@ -2,7 +2,6 @@
|
||||
* Constants used across various modules in this package
|
||||
* @module
|
||||
*/
|
||||
|
||||
import {LogLevel} from 'consola';
|
||||
import {readFileSync} from 'node:fs';
|
||||
import {fs} from '@appium/support';
|
||||
@@ -49,6 +48,11 @@ export const NAME_SCHEMA = '$schema';
|
||||
*/
|
||||
export const NAME_MKDOCS = 'mkdocs';
|
||||
|
||||
/**
|
||||
* Name of the `mike` executable
|
||||
*/
|
||||
export const NAME_MIKE = 'mike';
|
||||
|
||||
/**
|
||||
* Name of the `typedoc` executable
|
||||
*/
|
||||
@@ -109,6 +113,26 @@ export const REQUIREMENTS_TXT_PATH = path.join(PKG_ROOT_DIR, NAME_REQUIREMENTS_T
|
||||
*/
|
||||
export const DEFAULT_REL_TYPEDOC_OUT_PATH = path.join('docs', 'reference');
|
||||
|
||||
/**
|
||||
* The default branch to deploy to
|
||||
*/
|
||||
export const DEFAULT_DEPLOY_BRANCH = 'gh-pages';
|
||||
|
||||
/**
|
||||
* The default remote to push the deployed branch to
|
||||
*/
|
||||
export const DEFAULT_DEPLOY_REMOTE = 'origin';
|
||||
|
||||
/**
|
||||
* The default port for serving docs
|
||||
*/
|
||||
export const DEFAULT_SERVE_PORT = 8000;
|
||||
|
||||
/**
|
||||
* The default host for serving docs
|
||||
*/
|
||||
export const DEFAULT_SERVE_HOST = 'localhost';
|
||||
|
||||
/**
|
||||
* Mapping of `@appium/docutils`' log levels to `consola` log levels
|
||||
*/
|
||||
@@ -119,3 +143,8 @@ export const LogLevelMap = {
|
||||
info: LogLevel.Info,
|
||||
debug: LogLevel.Debug,
|
||||
} as const;
|
||||
|
||||
/**
|
||||
* Default site nav header text
|
||||
*/
|
||||
export const DEFAULT_NAV_HEADER = 'Reference';
|
||||
|
||||
@@ -20,6 +20,7 @@ import {
|
||||
NAME_MKDOCS,
|
||||
NAME_NPM,
|
||||
NAME_PYTHON,
|
||||
NAME_MIKE,
|
||||
} from './constants';
|
||||
import {DocutilsError} from './error';
|
||||
import {MkDocsYml} from './model';
|
||||
@@ -217,6 +218,11 @@ export const whichNpm = _.partial(cachedWhich, NAME_NPM);
|
||||
*/
|
||||
export const whichPython = _.partial(cachedWhich, NAME_PYTHON);
|
||||
|
||||
/**
|
||||
* Finds `mike` executable
|
||||
*/
|
||||
export const whichMike = _.partial(cachedWhich, NAME_MIKE);
|
||||
|
||||
/**
|
||||
* Reads an `mkdocs.yml` file, merges inherited configs, and returns the result. The result is cached.
|
||||
*
|
||||
|
||||
@@ -6,6 +6,9 @@ const DEFAULT_REMOTE = 'origin';
|
||||
const DEFAULT_BRANCH = 'gh-pages';
|
||||
const MIKE_VER_STRING = 'mike 1.';
|
||||
|
||||
/**
|
||||
* @deprecated Use the `deploy` export from `@appium/docutils`
|
||||
*/
|
||||
export class Mike {
|
||||
/** @type {string} */ remote;
|
||||
/** @type {string} */ branch;
|
||||
|
||||
Reference in New Issue
Block a user