feat(techdocs-common): add custom docker image support
Signed-off-by: Andrew Thauer <athauer@wealthsimple.com>
This commit is contained in:
@@ -226,6 +226,17 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
config: Config;
|
||||
});
|
||||
// (undocumented)
|
||||
static fromConfig(
|
||||
config: Config,
|
||||
{
|
||||
containerRunner,
|
||||
logger,
|
||||
}: {
|
||||
containerRunner: ContainerRunner;
|
||||
logger: Logger_2;
|
||||
},
|
||||
): Promise<TechdocsGenerator>;
|
||||
// (undocumented)
|
||||
run({
|
||||
inputDir,
|
||||
outputDir,
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
/*
|
||||
* Copyright 2020 Spotify AB
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import { ConfigReader } from '@backstage/config';
|
||||
import { readGeneratorConfig } from './techdocs';
|
||||
|
||||
const mockLogger = {
|
||||
warn: jest.fn(),
|
||||
};
|
||||
|
||||
describe('readGeneratorConfig', () => {
|
||||
beforeEach(() => {
|
||||
jest.resetAllMocks();
|
||||
});
|
||||
|
||||
const logger = mockLogger as any;
|
||||
|
||||
it('defaults to runIn docker', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generator: {},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'docker',
|
||||
dockerImage: undefined,
|
||||
pullImage: undefined,
|
||||
});
|
||||
});
|
||||
|
||||
it('should read local config', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generator: {
|
||||
runIn: 'local',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'local',
|
||||
});
|
||||
});
|
||||
|
||||
it('should read docker config', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generator: {
|
||||
runIn: 'docker',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'docker',
|
||||
});
|
||||
});
|
||||
|
||||
it('should read custom docker image', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generator: {
|
||||
runIn: 'docker',
|
||||
dockerImage: 'my-org/techdocs',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'docker',
|
||||
dockerImage: 'my-org/techdocs',
|
||||
});
|
||||
});
|
||||
|
||||
it('should read config disabling docker pull', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generator: {
|
||||
runIn: 'docker',
|
||||
dockerImage: 'my-org/techdocs',
|
||||
pullImage: false,
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'docker',
|
||||
dockerImage: 'my-org/techdocs',
|
||||
pullImage: false,
|
||||
});
|
||||
});
|
||||
|
||||
describe('with legacy techdocs.generators.techdocs config', () => {
|
||||
it('should read legacy docker option', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generators: {
|
||||
techdocs: 'docker',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'docker',
|
||||
});
|
||||
});
|
||||
|
||||
it('legacy option should log warning', () => {
|
||||
const config = new ConfigReader({
|
||||
techdocs: {
|
||||
generators: {
|
||||
techdocs: 'local',
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(readGeneratorConfig(config, logger)).toEqual({
|
||||
runIn: 'local',
|
||||
});
|
||||
expect(logger.warn).toHaveBeenCalledWith(
|
||||
`The 'techdocs.generators.techdocs' configuration key is deprecated and will be removed in the future. Please use 'techdocs.generator' instead.`,
|
||||
);
|
||||
});
|
||||
});
|
||||
});
|
||||
@@ -26,14 +26,14 @@ import {
|
||||
storeEtagMetadata,
|
||||
validateMkdocsYaml,
|
||||
} from './helpers';
|
||||
import { GeneratorBase, GeneratorRunOptions } from './types';
|
||||
import {
|
||||
GeneratorBase,
|
||||
GeneratorConfig,
|
||||
GeneratorRunInType,
|
||||
GeneratorRunOptions,
|
||||
} from './types';
|
||||
|
||||
type TechdocsGeneratorOptions = {
|
||||
// This option enables users to configure if they want to use TechDocs container
|
||||
// or generate without the container.
|
||||
// This is used to avoid running into Docker in Docker environment.
|
||||
runGeneratorIn: string;
|
||||
};
|
||||
const defaultDockerImage = 'spotify/techdocs';
|
||||
|
||||
const createStream = (): [string[], PassThrough] => {
|
||||
const log = [] as Array<string>;
|
||||
@@ -50,7 +50,17 @@ const createStream = (): [string[], PassThrough] => {
|
||||
export class TechdocsGenerator implements GeneratorBase {
|
||||
private readonly logger: Logger;
|
||||
private readonly containerRunner: ContainerRunner;
|
||||
private readonly options: TechdocsGeneratorOptions;
|
||||
private readonly options: GeneratorConfig;
|
||||
|
||||
static async fromConfig(
|
||||
config: Config,
|
||||
{
|
||||
containerRunner,
|
||||
logger,
|
||||
}: { containerRunner: ContainerRunner; logger: Logger },
|
||||
) {
|
||||
return new TechdocsGenerator({ logger, containerRunner, config });
|
||||
}
|
||||
|
||||
constructor({
|
||||
logger,
|
||||
@@ -62,10 +72,7 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
config: Config;
|
||||
}) {
|
||||
this.logger = logger;
|
||||
this.options = {
|
||||
runGeneratorIn:
|
||||
config.getOptionalString('techdocs.generators.techdocs') ?? 'docker',
|
||||
};
|
||||
this.options = readGeneratorConfig(config, logger);
|
||||
this.containerRunner = containerRunner;
|
||||
}
|
||||
|
||||
@@ -98,7 +105,7 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
};
|
||||
|
||||
try {
|
||||
switch (this.options.runGeneratorIn) {
|
||||
switch (this.options.runIn) {
|
||||
case 'local':
|
||||
await runCommand({
|
||||
command: 'mkdocs',
|
||||
@@ -114,7 +121,7 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
break;
|
||||
case 'docker':
|
||||
await this.containerRunner.runContainer({
|
||||
imageName: 'spotify/techdocs',
|
||||
imageName: this.options.dockerImage ?? defaultDockerImage,
|
||||
args: ['build', '-d', '/output'],
|
||||
logStream,
|
||||
mountDirs,
|
||||
@@ -122,6 +129,7 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
// Set the home directory inside the container as something that applications can
|
||||
// write to, otherwise they will just fail trying to write to /
|
||||
envVars: { HOME: '/tmp' },
|
||||
pullImage: this.options.pullImage,
|
||||
});
|
||||
this.logger.info(
|
||||
`Successfully generated docs from ${inputDir} into ${outputDir} using techdocs-container`,
|
||||
@@ -129,7 +137,7 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
break;
|
||||
default:
|
||||
throw new Error(
|
||||
`Invalid config value "${this.options.runGeneratorIn}" provided in 'techdocs.generators.techdocs'.`,
|
||||
`Invalid config value "${this.options.runIn}" provided in 'techdocs.generators.techdocs'.`,
|
||||
);
|
||||
}
|
||||
} catch (error) {
|
||||
@@ -163,3 +171,27 @@ export class TechdocsGenerator implements GeneratorBase {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
export function readGeneratorConfig(
|
||||
config: Config,
|
||||
logger: Logger,
|
||||
): GeneratorConfig {
|
||||
const legacyGeneratorType = config.getOptionalString(
|
||||
'techdocs.generators.techdocs',
|
||||
) as GeneratorRunInType;
|
||||
|
||||
if (legacyGeneratorType) {
|
||||
logger.warn(
|
||||
`The 'techdocs.generators.techdocs' configuration key is deprecated and will be removed in the future. Please use 'techdocs.generator' instead.`,
|
||||
);
|
||||
}
|
||||
|
||||
return {
|
||||
runIn:
|
||||
legacyGeneratorType ??
|
||||
config.getOptionalString('techdocs.generator.runIn') ??
|
||||
'docker',
|
||||
dockerImage: config.getOptionalString('techdocs.generator.dockerImage'),
|
||||
pullImage: config.getOptionalBoolean('techdocs.generator.pullImage'),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -17,6 +17,18 @@ import { Entity } from '@backstage/catalog-model';
|
||||
import { Writable } from 'stream';
|
||||
import { ParsedLocationAnnotation } from '../../helpers';
|
||||
|
||||
// Determines where the generator will be run
|
||||
export type GeneratorRunInType = 'docker' | 'local';
|
||||
|
||||
/**
|
||||
* The techdocs generator configurations options.
|
||||
*/
|
||||
export type GeneratorConfig = {
|
||||
runIn: GeneratorRunInType;
|
||||
dockerImage?: string;
|
||||
pullImage?: boolean;
|
||||
};
|
||||
|
||||
/**
|
||||
* The values that the generator will receive.
|
||||
*
|
||||
|
||||
Reference in New Issue
Block a user