From c3db19b14eb23098e898426a875ee27675be49f9 Mon Sep 17 00:00:00 2001 From: Brandon Ly Date: Thu, 26 Oct 2023 12:37:03 -0500 Subject: [PATCH] [DOP-4033]: Replicate shared.mk steps within Autobuilder code (#917) * [DOP-4033]: Add initial commands * [DOP-4033]: Create helpers for cli commands * [DOP-4033]: Add default args * [DOP-4033]: Move directories * [DOP-4033]: Update spawn to work async and add code for getPatchId command * [DOP-4033]: Refactor executeCliCommand * [DOP-4033]: Wrap up nextgenparse refactor * [DOP-4033]: Move commands directory out of enhanced * [DOP-4033]: Add next gen html command * [DOP-4033]: Use correct arg for createEnvProdFile * [DOP-4033]: Add git hash * [DOP-4033]: Move commands into shared directory * [DOP-4033]: Refactor * [DOP-4033]: Create base job for standard builds ie jobs with makefiles that don't override shared,mk * [DOP-4033]: Add the ability to append to file using executeCliCommand * [DOP-4033]: Add logic to write to .env.production file * [DOP-4033]: Add more logic to write to files and start working creating test env * [DOP-4033]: Add local testing app and make test build faster * [DOP-4033]: Add test code to confirm commands work, and remove base dockerfile * [DOP-4033]: Add next-gen-stage, and add support for arm64 dockerfile * [DOP-4033]: Remove extra args from dockerfile * [DOP-4033]: Add oas-page-build and persistence module * [DOP-4033]: Add bundle path for oaspagebuild * [DOP-4033]: Work on logic for deploy and ability to pipe output from one command to another * [DOP-4033]: Work on logic for deploy and ability to pipe output from one command to another * [DOP-4033]: Work on logic for deploy and ability to pipe output from one command to another * [DOP-4033]: Finish up pipe logic * [DOP-4033]: Add next-gen-deploy * [DOP-4033]: Remove unused imports * [DOP-4033]: Remove standard job handler * [DOP-4033]: Add execution helper * [DOP-4033]: Refactor localApp * [DOP-4033]: Add stage job * [DOP-4033]: Update persistence module * [DOP-4033]: Add next-gen-deploy * [DOP-4033]: Update next-gen-deploy to work * [DOP-4033]: Add comments * [DOP-4033]: Add more comments * [DOP-4033]: Remove index.ts * [DOP-4033]: Refactor to use index file * [DOP-4033]: Use correct values for nextgenstage * [DOP-4033]: Respond to review comments * [DOP-4033]: Use PATH_PREFIX * [DOP-4033]: Remove param0 * [DOP-4033]: Respond to review feedback * [DOP-4033]: Respond to review feedback * [DOP-4033]: Remove extra log * [DOP-4033]: Add empty string to join to remove commas * [DOP-4033]: Use console.log * [DOP-4033]: Check array length * [DOP-4033]: Add error handling for dependency helper --- Dockerfile.local | 101 +++++++ package.json | 1 + src/commands/index.ts | 17 ++ .../src/helpers/dependency-helpers.ts | 65 ++++ src/commands/src/helpers/index.ts | 279 ++++++++++++++++++ src/commands/src/shared/next-gen-deploy.ts | 38 +++ src/commands/src/shared/next-gen-html.ts | 11 + src/commands/src/shared/next-gen-parse.ts | 22 ++ src/commands/src/shared/next-gen-stage.ts | 46 +++ src/commands/src/shared/oas-page-build.ts | 28 ++ src/commands/src/shared/persistence-module.ts | 33 +++ src/entrypoints/localApp.ts | 77 +++++ 12 files changed, 718 insertions(+) create mode 100644 Dockerfile.local create mode 100644 src/commands/index.ts create mode 100644 src/commands/src/helpers/dependency-helpers.ts create mode 100644 src/commands/src/helpers/index.ts create mode 100644 src/commands/src/shared/next-gen-deploy.ts create mode 100644 src/commands/src/shared/next-gen-html.ts create mode 100644 src/commands/src/shared/next-gen-parse.ts create mode 100644 src/commands/src/shared/next-gen-stage.ts create mode 100644 src/commands/src/shared/oas-page-build.ts create mode 100644 src/commands/src/shared/persistence-module.ts create mode 100644 src/entrypoints/localApp.ts diff --git a/Dockerfile.local b/Dockerfile.local new file mode 100644 index 000000000..944e81b29 --- /dev/null +++ b/Dockerfile.local @@ -0,0 +1,101 @@ +FROM arm64v8/ubuntu:20.04 +ARG NPM_BASE_64_AUTH +ARG NPM_EMAIL +ARG SNOOTY_PARSER_VERSION=0.14.9 +ARG SNOOTY_FRONTEND_VERSION=0.14.18 +ARG MUT_VERSION=0.10.7 +ARG REDOC_CLI_VERSION=1.2.2 +ARG NPM_BASE_64_AUTH +ARG NPM_EMAIL +ENV DEBIAN_FRONTEND=noninteractive + +# helper libraries for docs builds +RUN apt-get update && apt-get install -y vim git unzip zip + +# get node 18 +# https://gist.github.com/RinatMullayanov/89687a102e696b1d4cab +RUN apt-get install --yes curl +RUN curl --location https://deb.nodesource.com/setup_18.x | bash - +RUN apt-get install --yes nodejs +RUN apt-get install --yes build-essential +RUN apt-get install --yes python3-pip libxml2-dev libxslt-dev python-dev pkg-config + +WORKDIR /app + +RUN python3 -m pip install poetry + +# install snooty parser +RUN git clone -b v${SNOOTY_PARSER_VERSION} --depth 1 https://github.com/mongodb/snooty-parser.git \ + && cd snooty-parser \ + && python3 -m poetry install \ + && make package \ + && mv dist/snooty /opt/ + +# install mut + +RUN git clone -b v${MUT_VERSION} --depth 1 https://github.com/mongodb/mut.git \ + && cd mut \ + && python3 -m poetry install \ + && make package \ + && mv dist/mut /opt/ + +RUN curl -L -o redoc.zip https://github.com/mongodb-forks/redoc/archive/refs/tags/v${REDOC_CLI_VERSION}.zip \ + && unzip redoc.zip \ + && mv redoc-${REDOC_CLI_VERSION} redoc/ + +ENV PATH="${PATH}:/opt/snooty:/opt/mut:/app/.local/bin" + +# setup user and root directory +RUN useradd -ms /bin/bash docsworker +RUN chmod 755 -R /app +RUN chown -Rv docsworker /app +USER docsworker + +# install snooty frontend and docs-tools +RUN git clone -b v${SNOOTY_FRONTEND_VERSION} --depth 1 https://github.com/mongodb/snooty.git \ + && cd snooty \ + && npm ci --legacy-peer-deps --omit=dev + +RUN mkdir -p modules/persistence && chmod 755 modules/persistence +COPY modules/persistence/package*.json ./modules/persistence/ +RUN cd ./modules/persistence \ + && npm ci --legacy-peer-deps + +RUN mkdir -p modules/oas-page-builder && chmod 755 modules/oas-page-builder +COPY modules/oas-page-builder/package*.json ./modules/oas-page-builder/ +RUN cd ./modules/oas-page-builder \ + && npm ci --legacy-peer-deps + +# Root project build +COPY package*.json ./ +RUN npm ci --legacy-peer-deps +# Build persistence module + +COPY --chown=docsworker modules/persistence/tsconfig*.json ./modules/persistence +COPY --chown=docsworker modules/persistence/src ./modules/persistence/src/ +COPY --chown=docsworker modules/persistence/index.ts ./modules/persistence + +RUN cd ./modules/persistence \ + && npm run build + +# Build modules +# OAS Page Builder +COPY --chown=docsworker modules/oas-page-builder/tsconfig*.json ./modules/oas-page-builder +COPY --chown=docsworker modules/oas-page-builder/src ./modules/oas-page-builder/src/ +COPY --chown=docsworker modules/oas-page-builder/index.ts ./modules/oas-page-builder + +RUN cd ./modules/oas-page-builder \ + && npm run build + +COPY tsconfig*.json ./ +COPY config config/ +COPY api api/ +COPY src src/ + +RUN npm run build:esbuild + +RUN mkdir repos && chmod 755 repos + +EXPOSE 3000 + +CMD ["node", "--enable-source-maps", "dist/entrypoints/localApp.js"] \ No newline at end of file diff --git a/package.json b/package.json index 524693ade..4c349508a 100644 --- a/package.json +++ b/package.json @@ -8,6 +8,7 @@ "start": "node index.js", "clean": "node maintain.js", "build": "tsc", + "build:esbuild": "esbuild src/entrypoints/localApp.ts --bundle --platform=node --outdir=./dist/entrypoints --allow-overwrite --sourcemap", "format": "npm run prettier -- --check", "format:fix": "npm run prettier -- --write", "lint": "eslint --ext .ts .", diff --git a/src/commands/index.ts b/src/commands/index.ts new file mode 100644 index 000000000..77b25f7a6 --- /dev/null +++ b/src/commands/index.ts @@ -0,0 +1,17 @@ +import { prepareBuildAndGetDependencies } from './src/helpers/dependency-helpers'; +import { nextGenDeploy } from './src/shared/next-gen-deploy'; +import { nextGenHtml } from './src/shared/next-gen-html'; +import { nextGenParse } from './src/shared/next-gen-parse'; +import { nextGenStage } from './src/shared/next-gen-stage'; +import { oasPageBuild } from './src/shared/oas-page-build'; +import { persistenceModule } from './src/shared/persistence-module'; + +export { + nextGenParse, + nextGenHtml, + nextGenStage, + persistenceModule, + oasPageBuild, + nextGenDeploy, + prepareBuildAndGetDependencies, +}; diff --git a/src/commands/src/helpers/dependency-helpers.ts b/src/commands/src/helpers/dependency-helpers.ts new file mode 100644 index 000000000..fce60dbf1 --- /dev/null +++ b/src/commands/src/helpers/dependency-helpers.ts @@ -0,0 +1,65 @@ +import path from 'path'; +import fs from 'fs'; +import { executeCliCommand, getCommitBranch, getCommitHash, getPatchId, getRepoDir } from '.'; +import { promisify } from 'util'; + +const existsAsync = promisify(fs.exists); +const writeFileAsync = promisify(fs.writeFile); + +async function cloneRepo(repoName: string) { + await executeCliCommand({ + command: 'git', + args: ['clone', `https://github.com/mongodb/${repoName}`], + options: { cwd: `${process.cwd()}/repos` }, + }); +} +async function createEnvProdFile(repoDir: string, projectName: string, baseUrl: string, prefix = '') { + const prodFileName = `${process.cwd()}/snooty/.env.production`; + + try { + await writeFileAsync( + prodFileName, + `GATSBY_SITE=${projectName} + GATSBY_MANIFEST_PATH=${repoDir}/bundle.zip + GATSBY_PARSER_USER=${process.env.USER} + GATSBY_BASE_URL=${baseUrl} + PATH_PREFIX=${prefix}`, + 'utf8' + ); + } catch (e) { + console.error(`ERROR! Could not write to .env.production`); + throw e; + } +} + +export async function prepareBuildAndGetDependencies(repoName: string, projectName: string, baseUrl: string) { + // before we get build dependencies, we need to clone the repo + await cloneRepo(repoName); + + const repoDir = getRepoDir(repoName); + + // doing these in parallel + const commandPromises = [ + getCommitHash(repoDir), + getCommitBranch(repoDir), + getPatchId(repoDir), + existsAsync(path.join(process.cwd(), 'config/redirects')), + createEnvProdFile(repoDir, projectName, baseUrl), + ]; + + try { + const dependencies = await Promise.all(commandPromises); + + return { + commitHash: dependencies[0] as string, + commitBranch: dependencies[1] as string, + patchId: dependencies[2] as string | undefined, + hasRedirects: dependencies[3] as boolean, + bundlePath: `${repoDir}/bundle.zip`, + repoDir, + }; + } catch (error) { + console.error('ERROR! Could not get build dependencies'); + throw error; + } +} diff --git a/src/commands/src/helpers/index.ts b/src/commands/src/helpers/index.ts new file mode 100644 index 000000000..4089b2178 --- /dev/null +++ b/src/commands/src/helpers/index.ts @@ -0,0 +1,279 @@ +import { SpawnOptions, spawn } from 'child_process'; +import { promisify } from 'util'; +import fs from 'fs'; +import path from 'path'; +import { Writable } from 'stream'; + +const openAsync = promisify(fs.open); +const closeAsync = promisify(fs.close); +const existsAsync = promisify(fs.exists); + +const EPIPE_CODE = 'EPIPE'; +const EPIPE_ERRNO = -32; +const EPIPE_SYSCALL = 'write'; + +export class ExecuteCommandError extends Error { + data: unknown; + constructor(message: string, data: unknown) { + super(message); + this.data = data; + } +} + +interface CliCommandParams { + command: string; + args?: readonly string[]; + options?: SpawnOptions; + writeStream?: fs.WriteStream; + writeTarget?: Writable; +} + +export interface CliCommandResponse { + outputText: string; + errorText: string; +} + +interface StdinError { + errno: number; + code: string; + syscall: string; +} +/** + * Method to replicate piping output from one command to another e.g. `yes | mut-publish public` + * @param {CliCommandParams} cmdFromParams The command we want to pipe output from to another command + * @param {CliCommandParams} cmdToParams The command that receives input from another command + * @returns {CliCommandResponse} The `CliCommandResponse` from the cmdTo command + */ +export async function executeAndPipeCommands( + cmdFromParams: CliCommandParams, + cmdToParams: CliCommandParams +): Promise { + return new Promise((resolve, reject) => { + let hasRejected = false; + + const cmdFrom = spawn(cmdFromParams.command, cmdFromParams.args || [], cmdFromParams.options || {}); + const cmdTo = spawn(cmdToParams.command, cmdToParams.args || [], cmdToParams.options || {}); + + cmdFrom.stdout?.on('data', (data: Buffer) => { + // For some commands, the command that is being written to + // will end before the first command finishes. In some cases, + // we do want this to happen. For example, the cli command `yes` will + // infinitely output yes to the terminal as a way of automatically responding + // to prompts from the subsequent command. Once the second command completes, + // we don't want `yes` to continue to run, so we kill the command. + if (!cmdTo.stdin?.writable) { + cmdFrom.kill(); + return; + } + + // this is where we pipe data from the first command to the second command. + cmdTo.stdin?.write(data); + }); + + cmdTo.stdin?.on('error', (err: StdinError) => { + // the error event for the cmdTo stdin gets called whenever it closes prematurely, + // but this is expected in certain situations e.g. when using the `yes` command. + // If this condition is met, we know that this expected and ignore it otherwise we throw. + // If we don't check, we get an unhandled error exception. + if (err.code === EPIPE_CODE && err.syscall === EPIPE_SYSCALL && err.errno === EPIPE_ERRNO) { + console.log('stdin done'); + return; + } + + reject(new ExecuteCommandError('The first command stdin (cmdTo) failed', err)); + hasRejected = true; + }); + + cmdFrom.stdout?.on('error', (err) => { + console.log('error on cmdFrom out', err); + }); + + cmdFrom.on('error', (err) => { + reject(new ExecuteCommandError('The first command (cmdTo) failed', err)); + hasRejected = true; + }); + + const outputText: string[] = []; + const errorText: string[] = []; + + cmdTo.stdout?.on('data', (data: Buffer) => { + outputText.push(data.toString()); + }); + + cmdTo.stderr?.on('data', (data: Buffer) => { + errorText.push(data.toString()); + }); + + cmdTo.on('error', (err) => { + reject(new ExecuteCommandError('The second command failed', err)); + }); + + cmdTo.on('exit', (exitCode) => { + // previous command errored out, return so we don't + // accidentally resolve if the second command somehow still + // exits without error + if (hasRejected) return; + + if (exitCode !== 0) { + console.error(`ERROR! The command ${cmdToParams.command} closed with an exit code other than 0: ${exitCode}.`); + console.error('Arguments provided: ', cmdToParams.args); + console.error('Options provided: ', cmdToParams.options); + + if (outputText.length) { + console.log('output', outputText.join('')); + } + + if (errorText.length) { + console.error('error', errorText.join('')); + } + + reject(new ExecuteCommandError('The command failed', { exitCode, outputText, errorText })); + return; + } + + resolve({ + outputText: outputText.join(''), + errorText: errorText.join(''), + }); + }); + }); +} + +/** + * A promisified way to execute CLI commands. This approach uses spawn instead of exec, which + * is a safer way of executing CLI commands. Also, spawn allows us to stream input and output in real-time. + * @param {string} command The CLI command we want to execute + * @param {string[] | undefined} args Arguments we want to provide to the command + * @param {SpawnOptions | undefined} options Options to configure the spawn function + * @param {fs.WriteStream | undefined} writeStream A writable stream object to pipe output to. + * For example, we can `mimic ls >> directory.txt` by creating a `WriteStream` object to write to + * `directory.txt`, and then provide the `WriteStream` so that we can pipe the output from the `ls` + * command to the `WriteStream`. + * @returns {Promise} An object containing the CLI output from `stdout` and `stderr`. + * stdout is the `outputText` property, and `stderr` is the `errorText` property. + */ +export async function executeCliCommand({ + command, + args = [], + options = {}, + writeStream, +}: CliCommandParams): Promise { + return new Promise((resolve, reject) => { + const outputText: string[] = []; + const errorText: string[] = []; + + const executedCommand = spawn(command, args, options); + + if (writeStream) executedCommand.stdout?.pipe(writeStream); + executedCommand.stdout?.on('data', (data: Buffer) => { + outputText.push(data.toString()); + }); + + executedCommand.stderr?.on('data', (data: Buffer) => { + errorText.push(data.toString()); + }); + + executedCommand.on('error', (err) => { + reject(new ExecuteCommandError('The command failed', err)); + }); + + executedCommand.on('close', (exitCode) => { + if (writeStream) writeStream.end(); + + if (exitCode !== 0) { + console.error(`ERROR! The command ${command} closed with an exit code other than 0: ${exitCode}.`); + console.error('Arguments provided: ', args); + console.error('Options provided: ', options); + + if (outputText.length) { + console.log(outputText.join('')); + } + + if (errorText.length) { + console.error(errorText.join('')); + } + + reject(new ExecuteCommandError('The command failed', exitCode)); + return; + } + + resolve({ + outputText: outputText.join(''), + errorText: errorText.join(''), + }); + }); + }); +} + +export interface ExecuteIOCommandParams { + command: string; + filePath: string; + args?: string[]; +} + +/** + * This function is equivalent to a double redirect + * e.g. echo "Hello!" >> hello.txt + */ +export async function executeAndWriteToFile({ command, filePath, args }: ExecuteIOCommandParams) { + const writeStream = fs.createWriteStream(filePath, { + flags: 'a+', + }); + + const result = await executeCliCommand({ command, args, writeStream }); + + return result; +} + +export async function readFileAndExec({ + command, + filePath, + args, +}: ExecuteIOCommandParams): Promise { + const fileId = await openAsync(filePath, 'r'); + const response = await executeCliCommand({ + command, + args, + options: { stdio: [fileId, process.stdout, process.stderr] }, + }); + + await closeAsync(fileId); + + return response; +} + +export async function getPatchId(repoDir: string): Promise { + const filePath = path.join(repoDir, 'myPatch.patch'); + try { + const { outputText: gitPatchId } = await readFileAndExec({ command: 'git', filePath, args: ['patch-id'] }); + + return gitPatchId.slice(0, 7); + } catch (err) { + console.warn('No patch ID found'); + } +} + +export async function getCommitBranch(repoDir: string): Promise { + // equivalent to git rev-parse --abbrev-ref HEAD + const response = await executeCliCommand({ + command: 'git', + args: ['rev-parse', '--abbrev-ref', 'HEAD'], + options: { cwd: repoDir }, + }); + + return response.outputText; +} + +export async function getCommitHash(repoDir: string): Promise { + // equivalent to git rev-parse --short HEAD + const response = await executeCliCommand({ + command: 'git', + args: ['rev-parse', '--short', 'HEAD'], + options: { cwd: repoDir }, + }); + + return response.outputText; +} + +export const checkIfPatched = async (repoDir: string) => !existsAsync(path.join(repoDir, 'myPatch.patch')); +export const getRepoDir = (repoName: string) => path.join(process.cwd(), `repos/${repoName}`); diff --git a/src/commands/src/shared/next-gen-deploy.ts b/src/commands/src/shared/next-gen-deploy.ts new file mode 100644 index 000000000..d980e1d1f --- /dev/null +++ b/src/commands/src/shared/next-gen-deploy.ts @@ -0,0 +1,38 @@ +import { executeAndPipeCommands, executeCliCommand } from '../helpers'; + +interface NextGenDeployParams { + bucket: string; + mutPrefix: string; + gitBranch: string; + hasConfigRedirects: boolean; + url: string; +} + +export async function nextGenDeploy({ bucket, mutPrefix, gitBranch, hasConfigRedirects, url }: NextGenDeployParams) { + if (hasConfigRedirects && (gitBranch === 'main' || gitBranch === 'master')) { + // equivalent to: mut-redirects config/redirects -o public/.htaccess + await executeCliCommand({ command: 'mut-redirects', args: ['config/redirects', '-o', 'public/.htaccess'] }); + } + + // equivalent to: yes | mut-publish public ${BUCKET} --prefix="${MUT_PREFIX}" --deploy --deployed-url-prefix=${URL} --json --all-subdirectories ${ARGS}; + const { outputText } = await executeAndPipeCommands( + { command: 'yes' }, + { + command: 'mut-publish', + args: [ + 'public', + bucket, + `--prefix=${mutPrefix}`, + '--deploy', + `--deployed-url-prefix=${url}`, + '--json', + '--all-subdirectories', + ], + options: { + cwd: `${process.cwd()}/snooty`, + }, + } + ); + + return `${outputText}\n Hosted at ${url}/${mutPrefix}`; +} diff --git a/src/commands/src/shared/next-gen-html.ts b/src/commands/src/shared/next-gen-html.ts new file mode 100644 index 000000000..226a14a97 --- /dev/null +++ b/src/commands/src/shared/next-gen-html.ts @@ -0,0 +1,11 @@ +import { executeCliCommand } from '../helpers'; + +export async function nextGenHtml() { + const result = await executeCliCommand({ + command: 'npm', + args: ['run', 'build'], + options: { cwd: `${process.cwd()}/snooty` }, + }); + + return result; +} diff --git a/src/commands/src/shared/next-gen-parse.ts b/src/commands/src/shared/next-gen-parse.ts new file mode 100644 index 000000000..fc1847ec9 --- /dev/null +++ b/src/commands/src/shared/next-gen-parse.ts @@ -0,0 +1,22 @@ +import { CliCommandResponse, executeCliCommand } from '../helpers'; + +const RSTSPEC_FLAG = '--rstspec=https://raw.githubusercontent.com/mongodb/snooty-parser/latest/snooty/rstspec.toml'; + +interface NextGenParseParams { + repoDir: string; + commitHash: string; + patchId?: string; +} +export async function nextGenParse({ repoDir, patchId, commitHash }: NextGenParseParams): Promise { + const commandArgs = ['build', repoDir, '--output', `${repoDir}/bundle.zip`, RSTSPEC_FLAG]; + + if (patchId) { + commandArgs.push('--commit'); + commandArgs.push(commitHash); + + commandArgs.push('--patch'); + commandArgs.push(patchId); + } + + return executeCliCommand({ command: 'snooty', args: commandArgs }); +} diff --git a/src/commands/src/shared/next-gen-stage.ts b/src/commands/src/shared/next-gen-stage.ts new file mode 100644 index 000000000..ff1acffdf --- /dev/null +++ b/src/commands/src/shared/next-gen-stage.ts @@ -0,0 +1,46 @@ +import { executeCliCommand } from '../helpers'; + +const DOCS_WORKER_USER = 'docsworker-xlarge'; +interface StageParams { + repoDir: string; + mutPrefix: string; + projectName: string; + bucket: string; + url: string; + patchId?: string; + commitBranch: string; + commitHash: string; +} + +export async function nextGenStage({ + mutPrefix, + projectName, + bucket, + url, + patchId, + commitBranch, + commitHash, +}: StageParams) { + let hostedAtUrl = `${url}/${mutPrefix}/${DOCS_WORKER_USER}/${commitBranch}/`; + let prefix = mutPrefix; + + const commandArgs = ['public', bucket, '--stage']; + + if (patchId && projectName === mutPrefix) { + prefix = `${commitHash}/${patchId}/${mutPrefix}`; + hostedAtUrl = `${url}/${commitHash}/${patchId}/${mutPrefix}/${DOCS_WORKER_USER}/${commitBranch}/`; + } + + commandArgs.push(`--prefix="${prefix}"`); + + const { outputText } = await executeCliCommand({ + command: 'mut-publish', + args: commandArgs, + options: { + cwd: `${process.cwd()}/snooty`, + }, + }); + + const resultMessage = `${outputText}\n Hosted at ${hostedAtUrl}`; + return resultMessage; +} diff --git a/src/commands/src/shared/oas-page-build.ts b/src/commands/src/shared/oas-page-build.ts new file mode 100644 index 000000000..5950a27f2 --- /dev/null +++ b/src/commands/src/shared/oas-page-build.ts @@ -0,0 +1,28 @@ +import { executeCliCommand } from '../helpers'; + +interface OasPageBuildParams { + bundlePath: string; + repoDir: string; + siteUrl: string; +} + +export async function oasPageBuild({ bundlePath, repoDir, siteUrl }: OasPageBuildParams) { + const { outputText } = await executeCliCommand({ + command: 'node', + args: [ + `${process.cwd()}/modules/oas-page-builder/dist/index.js`, + '--bundle', + bundlePath, + '--repo', + repoDir, + '--output', + `${repoDir}/public`, + '--redoc', + `${process.cwd()}/redoc/cli/index.js`, + '--site-url', + siteUrl, + ], + }); + + return outputText; +} diff --git a/src/commands/src/shared/persistence-module.ts b/src/commands/src/shared/persistence-module.ts new file mode 100644 index 000000000..e1b7851e9 --- /dev/null +++ b/src/commands/src/shared/persistence-module.ts @@ -0,0 +1,33 @@ +import { executeCliCommand } from '../helpers'; + +interface PersistenceModuleParams { + bundlePath: string; + jobId?: string; + repoOwner?: string; +} +export async function persistenceModule({ + bundlePath, + jobId, + repoOwner = 'docs-builder-bot', +}: PersistenceModuleParams) { + const args = [ + `${process.cwd()}/modules/persistence/dist/index.js`, + '--unhandled-rejections=strict', + '--path', + bundlePath, + '--githubUser', + repoOwner, + ]; + + if (jobId) { + args.push('--jobId'); + args.push(jobId); + } + + const { outputText } = await executeCliCommand({ + command: 'node', + args, + }); + + return outputText; +} diff --git a/src/entrypoints/localApp.ts b/src/entrypoints/localApp.ts new file mode 100644 index 000000000..46e32e530 --- /dev/null +++ b/src/entrypoints/localApp.ts @@ -0,0 +1,77 @@ +import { + nextGenDeploy, + nextGenHtml, + nextGenParse, + nextGenStage, + oasPageBuild, + persistenceModule, + prepareBuildAndGetDependencies, +} from '../commands'; + +async function localApp() { + // TODO: Fetch this from repos_branches + const repoName = 'docs-java'; + const projectName = 'java'; + const baseUrl = 'https://www.mongodb.com'; + const bucket = 'docs-java-dotcomstg'; + const mutPrefix = 'docs/drivers/java/sync'; + + const { commitHash, patchId, bundlePath, commitBranch, hasRedirects, repoDir } = await prepareBuildAndGetDependencies( + repoName, + projectName, + baseUrl + ); + + console.log('Begin snooty build...'); + const snootyBuildRes = await nextGenParse({ repoDir, commitHash, patchId }); + + console.log(snootyBuildRes.errorText); + + console.log('snooty build complete'); + + console.log('Begin persistence-module'); + const persistenceModuleRes = await persistenceModule({ bundlePath }); + console.log(persistenceModuleRes); + console.log('persistence-module complete'); + + console.log('Begin next-gen-html...'); + + const nextGenHtmlRes = await nextGenHtml(); + console.log(nextGenHtmlRes.outputText); + + console.log('next-gen-html complete'); + + console.log('Begin oas-page-build...'); + const siteUrl = mutPrefix ? `${baseUrl}/${mutPrefix}` : `${baseUrl}`; + const oasPageBuildRes = await oasPageBuild({ repoDir, bundlePath, siteUrl }); + console.log('oas-page-build compelte'); + + console.log(oasPageBuildRes); + console.log('Begin next-gen-stage...'); + + const resultMessage = await nextGenStage({ + patchId, + commitBranch, + repoDir, + projectName, + bucket, + url: baseUrl, + mutPrefix, + commitHash, + }); + console.log(resultMessage); + console.log('next-gen-stage complete'); + + console.log('Begin next-gen-deploy...'); + const deployRes = await nextGenDeploy({ + bucket, + hasConfigRedirects: hasRedirects, + gitBranch: commitBranch, + mutPrefix, + url: baseUrl, + }); + console.log(deployRes); + console.log('next-gen-deploy complete'); +} + +localApp();