docs(appium): add scripts for publishing docs via gh-pages
ADD
packages/appium/docs/scripts/build-docs.js
+// for simplicity this file is not transpiled and is run directly via an npm script
+/* eslint-disable promise/prefer-await-to-callbacks */
+/* eslint-disable promise/prefer-await-to-then */
+
+const { Mike } = require('@appium/docutils');
+const { log, LANGS, DOCS_DIR, DOCS_BRANCH, DOCS_PREFIX,
+ DOCS_REMOTE, LATEST_ALIAS } = require('./utils');
+const copyAssets = require('./copy-assets');
+const path = require('path');
+const semver = require('semver');
+const { version } = require('../../package.json');
+
+const branch = process.env.APPIUM_DOCS_BRANCH || DOCS_BRANCH;
+const prefix = process.env.APPIUM_DOCS_PREFIX || DOCS_PREFIX;
+const remote = process.env.APPIUM_DOCS_PREFIX || DOCS_REMOTE;
+
+const shouldPush = !!process.env.APPIUM_DOCS_PUBLISH;
+
+async function main () {
+ log.info(`Building Appium docs and committing to ${DOCS_BRANCH}`);
+
+ await copyAssets();
+
+ const semVersion = semver.parse(version);
+ const majMinVer = `${semVersion.major}.${semVersion.minor}`;
+
+ for (const lang of LANGS) {
+ log.info(`Building docs for language '${lang}' and version ${majMinVer}`);
+ const configFile = path.join(DOCS_DIR, `mkdocs-${lang}.yml`);
+ const m = new Mike({branch, prefix: path.join(prefix, lang), remote, configFile});
+
+ const docsAlreadyExisted = (await m.list()).length >= 1;
+
+ const deployOpts = {
+ version: majMinVer,
+ alias: LATEST_ALIAS,
+ shouldRebase: shouldPush,
+ shouldPush,
+ commit: `docs(appium): auto-build docs for appium@${majMinVer}, language ${lang}`
+ };
+ await m.deploy(deployOpts);
+
+ if (!docsAlreadyExisted) {
+ log.info(`Docs did not already exist so setting the latest alias to default`);
+ await m.setDefault(LATEST_ALIAS);
+ }
+ log.info(`Docs built`);
+ }
+}
+
+if (require.main === module) {
+ main().catch((err) => {
+ throw err;
+ });
+}
+
+module.exports = main;

RENAME
packages/appium/docs/copy-assets.js
packages/appium/docs/scripts/copy-assets.js
-const path = require('path');
-const { fs, logger } = require('@appium/support');
-
-const log = logger.getLogger('Docs');
+// for simplicity this file is not transpiled and is run directly via an npm script
+/* eslint-disable promise/prefer-await-to-callbacks */
+/* eslint-disable promise/prefer-await-to-then */
-const DOCS_DIR = path.resolve(__dirname);
-const ASSETS_DIR = path.join(DOCS_DIR, 'assets');
-const LANGS = ['en', 'ja'];
+const { fs } = require('@appium/support');
+const { log, LANGS, DOCS_DIR, ASSETS_DIR } = require('./utils');
+const path = require('path');
async function main () {
log.info('Copying generic assets to docs language dirs');
@@ -20,6 +19,10 @@ async function main () {
}
}
+if (require.main === module) {
main().catch((err) => {
throw err;
});
+}
+
+module.exports = main;

ADD
packages/appium/docs/scripts/utils.js
+// for simplicity this file is not transpiled and is run directly via an npm script
+//
+const { logger } = require('@appium/support');
+const path = require('path');
+
+const log = logger.getLogger('Docs');
+
+const DOCS_REMOTE = 'origin';
+const DOCS_BRANCH = 'gh-pages';
+const DOCS_PREFIX = 'docs';
+const DOCS_DIR = path.resolve(__dirname, '..');
+const LATEST_ALIAS = 'latest';
+const ASSETS_DIR = path.join(DOCS_DIR, 'assets');
+const LANGS = ['en', 'ja'];
+
+module.exports = {
+ log,
+ DOCS_DIR,
+ ASSETS_DIR,
+ LANGS,
+ DOCS_BRANCH,
+ DOCS_PREFIX,
+ DOCS_REMOTE,
+ LATEST_ALIAS,
+};

MODIFY
packages/appium/package.json
packages/appium/package.json
"build": "babel lib --root-mode=upward --out-dir=build/lib",
"dev": "npm run build -- --watch",
"fix": "npm run lint -- --fix",
- "clean:docs": "rimraf ./docs/site",
- "build:docs": "npm run clean:docs && run-s build:docs:*",
- "build:docs:assets": "node docs/copy-assets.js",
- "build:docs:en": "mkdocs build -f ./docs/mkdocs-en.yml",
- "build:docs:ja": "mkdocs build -f ./docs/mkdocs-ja.yml",
+ "build:docs": "node docs/scripts/build-docs.js",
+ "build:docs:assets": "node docs/scripts/copy-assets.js",
"dev:docs": "npm run build:docs:assets && npm run dev:docs:en",
"dev:docs:en": "mkdocs serve -f ./docs/mkdocs-en.yml",
"dev:docs:ja": "mkdocs serve -f ./docs/mkdocs-ja.yml",
+ "publish:docs": "APPIUM_DOCS_PUBLISH=1 npm run build:docs",
"postinstall": "node ./scripts/postinstall.js",
"lint": "eslint -c ../../.eslintrc --ignore-path ../../.eslintignore .",
"test": "npm run test:unit",

MODIFY
packages/docutils/lib/index.js
packages/docutils/lib/index.js
import baseConfig from './jsdoc-config-base';
export * from './mkdocs';
+export * from './mike';
export { baseConfig };

ADD
packages/docutils/lib/logger.js
+import { logger } from '@appium/support';
+
+export default logger.getLogger('Docutils');

ADD
packages/docutils/lib/mike.js
+import { exec } from 'teen_process';
+import log from './logger';
+
+const DEFAULT_REMOTE = 'origin';
+const DEFAULT_BRANCH = 'gh-pages';
+const MIKE_VER_STRING = 'mike 1.';
+
+export class Mike {
+ /** @type string */ remote;
+ /** @type string */ branch;
+ /** @type string? */ prefix;
+ /** @type string */ configFile;
+ /** @type boolean */ _mikeVerified = false;
+
+ constructor (/** @type MikeOpts */opts) {
+ this.remote = opts.remote || DEFAULT_REMOTE;
+ this.branch = opts.branch || DEFAULT_BRANCH;
+ this.prefix = opts.prefix;
+ this.configFile = opts.configFile;
+ }
+
+ /**
+ * Throw an error if the 'mike' binary cannot be found
+ *
+ * @throws Error
+ */
+ async verifyMike () {
+ if (this._mikeVerified) {
+ return;
+ }
+ try {
+ const {stdout} = await this.exec('--version', [], false);
+ if (!stdout.includes(MIKE_VER_STRING)) {
+ throw new Error('Mike was installed but was not version 1.x');
+ }
+ } catch (err) {
+ throw new Error(`Could not verify appropriate mike binary exists: ${err}`);
+ }
+ this._mikeVerified = true;
+ }
+
+ /**
+ * Get an array of args based on the class members that can be used with Mike-related subprocess
+ * execution
+ *
+ * @param {string} cmdName - the name of the mike command to run
+ * @param {string[]} cmdArgs - an array of command-specific arguments
+ *
+ * @returns string[]
+ */
+ getMikeArgs (cmdName, cmdArgs) {
+ return [
+ cmdName, ...cmdArgs,
+ '--config-file', this.configFile,
+ '--remote', this.remote,
+ '--branch', this.branch,
+ '--prefix', this.prefix,
+ ];
+ }
+
+ /**
+ * Exec mike as a subprocess
+ *
+ * @param {string} mikeCmd - the mike command to run
+ * @param {string[]} [mikeArgs=[]] - the arguments to pass to the mike command
+ * @param {boolean?} [verify=true] - whether to verify mike exists first
+ *
+ * @returns Promise<import('teen_process').ExecResult<string>>
+ */
+ async exec (mikeCmd, mikeArgs = [], verify = true) {
+ if (verify) {
+ await this.verifyMike();
+ }
+ const args = this.getMikeArgs(mikeCmd, mikeArgs);
+ log.debug(`Running mike ${args.join(' ')}`);
+ return await exec('mike', args);
+ }
+
+ /**
+ * Return the list of mike deploys
+ *
+ * @returns string[]
+ */
+ async list () {
+ const {stdout} = await this.exec('list');
+ return stdout.split('\n').map((s) => s.trim()).filter(Boolean);
+ }
+
+ /**
+ * Set the default version or alias
+ *
+ * @param {string} alias - the version or alias
+ */
+ async setDefault (alias) {
+ await this.exec('set-default', [alias]);
+ }
+
+ /**
+ * Deploy docs to the branch
+ *
+ * @param {MikeDeployOpts} opts - the deploy options
+ */
+ async deploy (opts) {
+ const args = [opts.version];
+ if (opts.alias) {
+ args.push(opts.alias, '--update-aliases');
+ }
+ if (opts.shouldPush) {
+ args.push('--push');
+ }
+ if (opts.shouldRebase) {
+ args.push('--rebase');
+ }
+ if (opts.commit) {
+ args.push('--message', opts.commit);
+ }
+ await this.exec('deploy', args);
+ }
+
+}
+
+/**
+ * @typedef MikeOpts - options for instantiating a Mike object
+ * @property {string} [remote="origin"] - the git remote to push docs to
+ * @property {string} [branch="gh-pages"] - the git branch to push docs to
+ * @property {string?} prefix - the path prefix on the branch if any
+ * @property {string} configFile - the mkdocs config file to use
+ */
+
+/**
+ * @typedef MikeDeployOpts - options for deploying docs with Mike
+ * @param {string} version - the version to deploy
+ * @param {string?} alias - the alias to alias (or re-alias) the version
+ * @param {string?} commit - the commit message to use as an override
+ * @param {boolean?} shouldPush - whether mike should push the commits to the remote
+ * @param {boolean?} shouldRebase - whether mike should rebase
+ */

docs(appium): add scripts for publishing docs via gh-pages