diff --git a/API.md b/API.md index 2d41ff3..b069b65 100644 --- a/API.md +++ b/API.md @@ -774,6 +774,8 @@ Returns the apiVersion and kind for "Task". ### BuilderOptions +The options for builders for the `buildXX()` methods. + #### Initializer ```typescript @@ -786,18 +788,37 @@ const builderOptions: BuilderOptions = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | -| buildDependencies | boolean | *No description.* | +| includeDependencies | boolean | If true, all the dependent objects are generated with the build. | +| includeRuns | boolean | If true, the builder will also synth associated runs. | --- -##### `buildDependencies`Required +##### `includeDependencies`Optional ```typescript -public readonly buildDependencies: boolean; +public readonly includeDependencies: boolean; ``` - *Type:* boolean +If true, all the dependent objects are generated with the build. + +This is +designed to run on as minimal cluster as possible, with as few pre steps +as possible. + +--- + +##### `includeRuns`Optional + +```typescript +public readonly includeRuns: boolean; +``` + +- *Type:* boolean + +If true, the builder will also synth associated runs. + --- ### NamedResource @@ -2405,6 +2426,8 @@ public readonly name: string; ### TaskStepBuilder +Creates a `Step` in a `Task`. + #### Initializers ```typescript @@ -2423,6 +2446,7 @@ new TaskStepBuilder() | **Name** | **Description** | | --- | --- | | buildTaskStep | *No description.* | +| fromScriptData | If supplied, uses the provided script data as-is for the script value. | | fromScriptObject | If supplied, uses the cdk8s `ApiObject` supplied as the body of the `script` for the `Task`. | | fromScriptUrl | If supplied, uses the content found at the given URL for the `script` value of the step. | | withArgs | The args to use with the `command`. | @@ -2440,6 +2464,25 @@ new TaskStepBuilder() public buildTaskStep(): TaskStep ``` +##### `fromScriptData` + +```typescript +public fromScriptData(data: string): TaskStepBuilder +``` + +If supplied, uses the provided script data as-is for the script value. + +Use this when you have the script data from a source other than a file or +an object. Use the other methods, such as `fromScriptUrl` (when the script +is in a file) or `scriptFromObject` (when the script is a CDK8s object) +rather than resolving those yourself. + +###### `data`Required + +- *Type:* string + +--- + ##### `fromScriptObject` ```typescript @@ -2577,8 +2620,7 @@ The `workingDir` of the `Task`. | command | string[] | Gets the command used for the `Step` on the `Task`. | | image | string | The name of the container `image` used to execute the `Step` of the `Task`. | | name | string | The name of the `Step` of the `Task`. | -| scriptObj | cdk8s.ApiObject | Gets the object that is used for the `script` value, if there is one defined. | -| scriptUrl | string | Gets the URL from which the script data should be loaded, if it is defined. | +| scriptData | string | *No description.* | | workingDir | string | *No description.* | --- @@ -2631,28 +2673,14 @@ The name of the `Step` of the `Task`. --- -##### `scriptObj`Optional - -```typescript -public readonly scriptObj: ApiObject; -``` - -- *Type:* cdk8s.ApiObject - -Gets the object that is used for the `script` value, if there is one defined. - ---- - -##### `scriptUrl`Optional +##### `scriptData`Optional ```typescript -public readonly scriptUrl: string; +public readonly scriptData: string; ``` - *Type:* string -Gets the URL from which the script data should be loaded, if it is defined. - --- ##### `workingDir`Optional @@ -2668,6 +2696,8 @@ public readonly workingDir: string; ### WorkspaceBuilder +Builds the Workspaces for use by Tasks and Pipelines. + #### Initializers ```typescript @@ -2726,9 +2756,9 @@ public withName(name: string): WorkspaceBuilder | **Name** | **Type** | **Description** | | --- | --- | --- | -| description | string | *No description.* | -| logicalID | string | *No description.* | -| name | string | *No description.* | +| description | string | Gets the description of the workspace. | +| logicalID | string | Gets the logical ID of the `Workspace`. | +| name | string | Gets the name of the workspace. | --- @@ -2740,6 +2770,8 @@ public readonly description: string; - *Type:* string +Gets the description of the workspace. + --- ##### `logicalID`Optional @@ -2750,6 +2782,8 @@ public readonly logicalID: string; - *Type:* string +Gets the logical ID of the `Workspace`. + --- ##### `name`Optional @@ -2760,6 +2794,8 @@ public readonly name: string; - *Type:* string +Gets the name of the workspace. + --- diff --git a/src/builders.ts b/src/builders.ts index 43ddcff..281b868 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -4,33 +4,70 @@ import * as fs from 'fs'; -import { ApiObject, Yaml } from 'cdk8s'; +import { Yaml } from 'cdk8s'; import { Construct } from 'constructs'; import { buildParam } from './common'; import { Pipeline, PipelineParam, PipelineTask, PipelineTaskWorkspace, PipelineWorkspace } from './pipelines'; import { Task, TaskEnvValueSource, TaskParam, TaskProps, TaskSpecParam, TaskStep, TaskStepEnv, TaskWorkspace } from './tasks'; +/** + * The options for builders for the `buildXX()` methods. + */ export interface BuilderOptions { - readonly buildDependencies: boolean; + /** + * If true, all the dependent objects are generated with the build. This is + * designed to run on as minimal cluster as possible, with as few pre steps + * as possible. + */ + readonly includeDependencies?: boolean; + /** + * If true, the builder will also synth associated runs. + */ + readonly includeRuns?: boolean; } +/** + * The default options for the builders. + */ +export const DefaultBuilderOptions: BuilderOptions = { + includeDependencies: false, + includeRuns: false, +}; + +/** + * Builds the Workspaces for use by Tasks and Pipelines. + */ export class WorkspaceBuilder { private _logicalID: string; private _name?: string; private _description?: string; + /** + * Creates the `WorkspaceBuilder`, using the given `id` as the logical ID for + * the workspace. + * @param id + */ constructor(id: string) { this._logicalID = id; } + /** + * Gets the logical ID of the `Workspace`. + */ public get logicalID(): string | undefined { return this._logicalID; } + /** + * Gets the name of the workspace. + */ public get name(): string | undefined { return this._name; } + /** + * Gets the description of the workspace. + */ public get description(): string { return this._description || ''; } @@ -155,20 +192,105 @@ export class ParameterBuilder { /** * Returns true if this parameter expects input at the pipeline level. */ - public get requiresPipelineParameter() : boolean { + public get requiresPipelineParameter(): boolean { return this._requiresPipelineParam; } } +/** + * Resolves the `script` through different means. + */ +interface ScriptResolver { + /** + * Gets the body of the script. + * @returns string The script. + */ + scriptData(): string; +} + +/** + * Resolves the provided object into a YAML string. + */ +class ObjScriptResolver implements ScriptResolver { + readonly _obj: any; + + /** + * Creates an instance of the `ObjScriptResolver`. + * @param obj The object to serialize to YAML for the script. + */ + constructor(obj: any) { + this._obj = obj; + } + + /** + * Gets the body of the script as a YAML representation of the object. + */ + public scriptData(): string { + return Yaml.stringify(this._obj); + } +} + +/** + * Gets the content from the provided URL and returns it as the script data. + */ +class UrlScriptResolver implements ScriptResolver { + readonly _url: string; + + /** + * Creates an instance of the `UrlScriptResolver` with the provided URL. + * @param url + */ + constructor(url: string) { + this._url = url; + } + + /** + * Gets the body of the script from the provided URL. + * @return string Script data. + */ + public scriptData(): string { + const data = fs.readFileSync(this._url, { + encoding: 'utf8', + flag: 'r', + }); + + return data.replace(/\n/g, '\\n'); + } +} + +/** + * Gets the content from the static value provided. + */ +class StaticScriptResolver implements ScriptResolver { + readonly _script: string; + + /** + * Creates an instance of the `StaticScriptResolver`. + * @param data + */ + constructor(data: string) { + this._script = data; + } + + /** + * Returns the static value provided. + */ + public scriptData(): string { + return this._script; + } +} + +/** + * Creates a `Step` in a `Task`. + */ export class TaskStepBuilder { - private _url?: string; - private _obj?: any; private _name?: string; private _dir?: string; private _image?: string; private _cmd?: string[]; private _args?: string[]; private _env?: TaskStepEnv[]; + private _script?: ScriptResolver; /** * @@ -192,19 +314,8 @@ export class TaskStepBuilder { return this._image; } - /** - * Gets the URL from which the script data should be loaded, if it is defined. - */ - public get scriptUrl(): string | undefined { - return this._url; - } - - /** - * Gets the object that is used for the `script` value, if there is one - * defined. - */ - public get scriptObj(): ApiObject | undefined { - return this._obj; + public get scriptData(): string | undefined { + return this._script?.scriptData(); } /** @@ -267,7 +378,7 @@ export class TaskStepBuilder { * @param url */ public fromScriptUrl(url: string): TaskStepBuilder { - this._url = url; + this._script = new UrlScriptResolver(url); return this; } @@ -281,7 +392,22 @@ export class TaskStepBuilder { * @param obj */ public fromScriptObject(obj: any): TaskStepBuilder { - this._obj = obj; + this._script = new ObjScriptResolver(obj); + return this; + } + + /** + * If supplied, uses the provided script data as-is for the script value. + * + * Use this when you have the script data from a source other than a file or + * an object. Use the other methods, such as `fromScriptUrl` (when the script + * is in a file) or `scriptFromObject` (when the script is a CDK8s object) + * rather than resolving those yourself. + * + * @param data + */ + public fromScriptData(data: string): TaskStepBuilder { + this._script = new StaticScriptResolver(data); return this; } @@ -306,40 +432,14 @@ export class TaskStepBuilder { } public buildTaskStep(): TaskStep | undefined { - if (this.scriptUrl) { - if (this.scriptObj) { - throw new Error('Cannot specify both a URL source and an object source for the script.'); - } - // Load the script from the URL location and use it - const data = fs.readFileSync(this.scriptUrl, { - encoding: 'utf8', - flag: 'r', - }); - - const lines = data.replace(/\n/g, '\\n'); - if (data) { - return { - name: this.name, - image: this.image, - script: lines, - workingDir: this.workingDir, - env: this._env, - }; - } - } else if (this.scriptObj) { - if (this.scriptUrl) { - throw new Error('Cannot specify both an object source and a URL source for the script'); - } - const yamlData = Yaml.stringify(this.scriptObj); - if (yamlData) { - return { - name: this.name, - image: this.image, - script: yamlData, - workingDir: this.workingDir, - env: this._env, - }; - } + if (this._script) { + return { + name: this.name, + image: this.image, + script: this.scriptData, + workingDir: this.workingDir, + env: this._env, + }; } else { return { name: this.name, @@ -352,8 +452,6 @@ export class TaskStepBuilder { } return undefined; } - - } /** @@ -557,7 +655,7 @@ export class PipelineBuilder { * Builds the actual [Pipeline]() from the settings configured using the * fluid syntax. */ - public buildPipeline(opts: BuilderOptions = { buildDependencies: false }): void { + public buildPipeline(opts: BuilderOptions = DefaultBuilderOptions): void { // TODO: validate the object const pipelineParams = new Map(); @@ -613,7 +711,7 @@ export class PipelineBuilder { workspaces: taskWorkspaces, }); - if (opts.buildDependencies) { + if (opts.includeDependencies) { // Build the task if the user has asked for the dependencies to be // built along with the pipeline. t.buildTask(); diff --git a/test/__snapshots__/taskbuilder.test.ts.snap b/test/__snapshots__/taskbuilder.test.ts.snap index 629431a..f6daa11 100644 --- a/test/__snapshots__/taskbuilder.test.ts.snap +++ b/test/__snapshots__/taskbuilder.test.ts.snap @@ -149,6 +149,33 @@ request and populate the pr workspace with the state of the pull request, includ ] `; +exports[`TaskBuilderTest ScriptDataBuilder 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1beta1", + "kind": "Task", + "metadata": { + "name": "ansible-runner", + }, + "spec": { + "description": "Task to run Ansible playbooks using Ansible Runner", + "params": [], + "steps": [ + { + "env": undefined, + "image": "docker.io/hello-world", + "name": "requirements", + "script": "#!/usr/bin/env bash +echo this is my script data", + "workingDir": undefined, + }, + ], + "workspaces": [], + }, + }, +] +`; + exports[`TaskBuilderTest TaskBuilderBasic 1`] = ` [ { diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index b028438..c807975 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -19,7 +19,7 @@ class MyTestChart extends Chart { .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) - .buildPipeline({ buildDependencies: true }); + .buildPipeline({ includeDependencies: true }); } } diff --git a/test/taskbuilder.test.ts b/test/taskbuilder.test.ts index 72017b4..392395f 100644 --- a/test/taskbuilder.test.ts +++ b/test/taskbuilder.test.ts @@ -100,6 +100,22 @@ class TestBasicTaskBuildFromObject extends Chart { } } +class TestBasicTaskBuildFromScriptData extends Chart { + + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + new TaskBuilder(this, 'my-task') + .withName('ansible-runner') + .withDescription('Task to run Ansible playbooks using Ansible Runner') + .withStep(new TaskStepBuilder() + .withName('requirements') + .withImage('docker.io/hello-world') + .fromScriptData('#!/usr/bin/env bash\necho this is my script data')) + .buildTask(); + } +} + class TestPullRequestTaskBuild extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); @@ -168,4 +184,10 @@ describe('TaskBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + test('ScriptDataBuilder', () => { + const app = Testing.app(); + const chart = new TestBasicTaskBuildFromScriptData(app, 'apply-object'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); });