diff --git a/plugins/techdocs-backend/package.json b/plugins/techdocs-backend/package.json index 476b321e1e..b9c2953d28 100644 --- a/plugins/techdocs-backend/package.json +++ b/plugins/techdocs-backend/package.json @@ -25,6 +25,7 @@ "@backstage/config": "^0.1.1-alpha.21", "@types/dockerode": "^2.5.34", "@types/express": "^4.17.6", + "command-exists-promise": "^2.0.2", "default-branch": "^1.0.8", "dockerode": "^3.2.1", "express": "^4.17.1", diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts index 9f33823444..d68bb74aa6 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/helpers.ts @@ -18,6 +18,7 @@ import { Entity } from '@backstage/catalog-model'; import { Writable, PassThrough } from 'stream'; import Docker from 'dockerode'; import { SupportedGeneratorKey } from './types'; +import { spawn } from 'child_process'; // TODO: Implement proper support for more generators. export function getGeneratorKey(entity: Entity): SupportedGeneratorKey { @@ -38,6 +39,13 @@ type RunDockerContainerOptions = { createOptions?: Docker.ContainerCreateOptions; }; +export type RunCommandOptions = { + command: string; + args: string[]; + options: object; + logStream?: Writable; +}; + export async function runDockerContainer({ imageName, args, @@ -96,3 +104,41 @@ export async function runDockerContainer({ return { error, statusCode }; } + +/** + * + * @param options the options object + * @param options.command the command to run + * @param options.args the arguments to pass the command + * @param options.options options used in spawn + * @param options.logStream the log streamer to capture log messages + */ +export const runCommand = async ({ + command, + args, + options, + logStream = new PassThrough(), +}: RunCommandOptions) => { + await new Promise((resolve, reject) => { + const process = spawn(command, args, options); + + process.stdout.on('data', stream => { + logStream.write(stream); + }); + + process.stderr.on('data', stream => { + logStream.write(stream); + }); + + process.on('error', error => { + return reject(error); + }); + + process.on('close', code => { + if (code !== 0) { + return reject(`Command ${command} failed, exit code: ${code}`); + } + return resolve(); + }); + }); +}; diff --git a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts index 40cd7590b6..cb89b42e38 100644 --- a/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts +++ b/plugins/techdocs-backend/src/techdocs/stages/generate/techdocs.ts @@ -24,7 +24,9 @@ import { GeneratorRunOptions, GeneratorRunResult, } from './types'; -import { runDockerContainer } from './helpers'; +import { runDockerContainer, runCommand } from './helpers'; + +const commandExists = require('command-exists-promise'); export class TechdocsGenerator implements GeneratorBase { private readonly logger: Logger; @@ -46,17 +48,29 @@ export class TechdocsGenerator implements GeneratorBase { ); try { - await runDockerContainer({ - imageName: 'spotify/techdocs', - args: ['build', '-d', '/result'], - logStream, - docsDir: directory, - resultDir, - dockerClient, - }); - this.logger.info( - `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, - ); + const mkdocsInstalled = await commandExists('mkdocs'); + if (mkdocsInstalled) { + await runCommand({ + command: 'mkdocs', + args: ['build', '-d', resultDir, '-v'], + options: { + cwd: directory, + }, + logStream, + }); + } else { + await runDockerContainer({ + imageName: 'spotify/techdocs', + args: ['build', '-d', '/result'], + logStream, + docsDir: directory, + resultDir, + dockerClient, + }); + this.logger.info( + `[TechDocs]: Successfully generated docs from ${directory} into ${resultDir}`, + ); + } } catch (error) { this.logger.debug( `[TechDocs]: Failed to generate docs from ${directory} into ${resultDir}`,