From 4577b011b775d589e3d71b26a0b96a0655fc8dfd Mon Sep 17 00:00:00 2001 From: Nathan Good Date: Wed, 28 Feb 2024 09:35:05 -0600 Subject: [PATCH 01/30] Updated documentation for using the library. Signed-off-by: Nathan Good --- API.md | 160 +++++++++++++++++++++++++++++++++++++++++++++--- README.md | 107 +++++++++++++++++++++++++++++++- src/builders.ts | 66 ++++++++++++++++++-- 3 files changed, 317 insertions(+), 16 deletions(-) diff --git a/API.md b/API.md index 6e94a25..cc570b5 100644 --- a/API.md +++ b/API.md @@ -3,13 +3,118 @@ This is a construct for creating [Pipelines](https://tekton.dev/docs/getting-started/pipelines/) using [cdk8s](https://cdk8s.io/docs/latest/). +In Cloud Development Kit (CDK) terminology, a _construct_ is a code object (class +or application) that can be _synthesized_ to output. In AWS's CDK, that output +is CloudFormation. Here, in a cdk8s construct, that output is YAML that can be +applied on a Kubernetes or OpenShift cluster. Constructs can be used in other +constructs, just like how code classes can by used by other classes. + +This library allows you to create your own Tekton pipeline constructs, which can +then in turn be synthesized into Task, Pipeline, and PipelineRun YAML. + +## Installing prerequisites + +The commands here will use `npx`, using Node 18.x and NPM 9.x. You will also +need `yarn` 1.x (1.22.21 was used here) installed. + +## Using the library to create your own pipeline constructs + +The `projen` command was used to create this construct library and is the easiest +way to create a new construct. The documentation here will show how to use `projen` +to generate your pipeline construct. + +### Creating your project + +To create your pipeline project, run the following command, where `my-pipeline-project` +is the name you want to give your project's directory: + +```bash +$ mkdir my-pipeline-project +$ cd my-pipeline-project +$ npx projen new cdk8s-app-ts +``` + +This will generate a TypeScript project with the cdk8s `constructs` libraries +with the correct structure. + +> ***Why TypeScript and not some other language (e.g., Python)? Because TypeScript can be used to generate all the others.*** + +The command will also initialize a git repository and make an initial commit. + + +### Adding this library to your project + +When using `projen`, modify the _.projenrc.ts_ file to add the libraries +and then run the `npx projen` command--with no additional arguments--to +re-generate the _package.json_ and _yarn.lock_ files and any other files +in the project. When using `projen`, only modify the _.projenrc.ts_ file +and the files in the _src_ folder. + +Modify the _.projenrc.ts_ file and add the following lines to `deps` JSON +element: + +```typescript +const project = new cdk8s.Cdk8sTypeScriptApp({ + // snipped, leave content as-is... + deps: [ + 'cdk8s-pipelines', + 'cdk8s-pipelines-lib', + ], + // snipped, leave content as-is... +}); +``` + +Save the file after you have made the additions and then run the `npx projen` +command to re-generate the project files. + +### Modifying the main Chart + +The _src/main.ts_ file contains the main code that you will modify for your +Pipeline construct. Like any other TypeScript project, you can create classes +and functions in other files and import them for use. + +By default, the template includes a class called `MyChart` that extends from +the cdk8s core `Chart` class. You can rename this class to something a bit +more meaningful, such as `InstallXYZPipelineChart`. + +The `constructor` function contains the code that will create the chart. Here, +replace the sample code with something that looks like this: + +```typescript +export class MyChart extends Chart { + constructor(scope: Construct, id: string, props: ChartProps = { }) { + super(scope, id, props); + + new PipelineBuilder(this, 'my-pipeline') + .withName('clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(new TaskBuilder(this, 'fetch-source') + .withName('git-clone') + .withWorkspace(new WorkspaceBuilder('output').withName('task-output')) + .withStringParam(new ParameterBuilder('url').withPiplineParameter('url').withDescription('the URL for the thing'))) + .buildPipeline(); + } +} +``` + +Start with the imports shown here and add as needed: + +```typescript +import { App, Chart, ChartProps } from 'cdk8s'; +import { ParameterBuilder, PipelineBuilder, TaskBuilder, WorkspaceBuilder } from 'cdk8s-pipelines'; +import { Construct } from 'constructs'; +``` + ## Examples +Shown here is an example of using one of the primitive Tekton objects--a +[Pipeline](https://tekton.dev/docs/pipelines/) using cdk8s-pipelines. + ```typescript const pipeline = new Pipeline(this, 'my-pipeline'); ``` -## Pipeline objects and builders +### Pipeline objects and builders Tekton [Pipelines](https://tekton.dev/docs/pipelines/), [Tasks](https://tekton.dev/docs/pipelines/tasks/), @@ -2530,6 +2635,8 @@ public readonly logicalID: string; Builds the parameters for use by Tasks and Pipelines. +> [https://tekton.dev/docs/pipelines/pipelines/#specifying-parameters](https://tekton.dev/docs/pipelines/pipelines/#specifying-parameters) + #### Initializers ```typescript @@ -2658,11 +2765,11 @@ Sets the value for the parameter. | **Name** | **Type** | **Description** | | --- | --- | --- | -| description | string | *No description.* | +| description | string | Gets the description of the parameter. | | requiresPipelineParameter | boolean | Returns true if this parameter expects input at the pipeline level. | | defaultValue | string | *No description.* | | logicalID | string | Gets the logicalID for the `ParameterBuilder`, which is used by the underlying construct. | -| name | string | *No description.* | +| name | string | Gets the name of the parameter. | | type | string | Gets the type of the parameter. | | value | string | Gets the value of the parameter. | @@ -2676,6 +2783,8 @@ public readonly description: string; - *Type:* string +Gets the description of the parameter. + --- ##### `requiresPipelineParameter`Required @@ -2720,6 +2829,8 @@ public readonly name: string; - *Type:* string +Gets the name of the parameter. + --- ##### `type`Optional @@ -3406,16 +3517,16 @@ new TaskStepBuilder() | **Name** | **Description** | | --- | --- | -| buildTaskStep | *No description.* | +| buildTaskStep | When called, builds the `Step`. | | 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. | | fromScriptUrlToResults | If supplied, uses the content found at the given URL for the `script` value of the step and writes its output to the `results`. | | withArgs | The args to use with the `command`. | | withCommand | The name of the command to use when running the `Step` of the `Task`. | -| withEnv | *No description.* | +| withEnv | Sets the environment variable for the Step. | | withImage | The name of the image to use when executing the `Step` on the `Task`. | -| withName | *No description.* | +| withName | Sets the name of the `Step` of the `Task`. | | withWorkingDir | The `workingDir` of the `Task`. | --- @@ -3426,6 +3537,11 @@ new TaskStepBuilder() public buildTaskStep(): TaskStep ``` +When called, builds the `Step`. + +This will be called the `TaskBuilder`; +do not call it directly. + ##### `fromScriptData` ```typescript @@ -3545,16 +3661,24 @@ If public withEnv(name: string, valueFrom: TaskEnvValueSource): TaskStepBuilder ``` +Sets the environment variable for the Step. + +> [valueFrom ()](valueFrom ()) + ###### `name`Required - *Type:* string +The name of the `env` to add or set. + --- ###### `valueFrom`Required - *Type:* TaskEnvValueSource +The source of the value for the variable. + --- ##### `withImage` @@ -3577,10 +3701,14 @@ The name of the image to use when executing the `Step` on the `Task`. public withName(name: string): TaskStepBuilder ``` +Sets the name of the `Step` of the `Task`. + ###### `name`Required - *Type:* string +Name of the `Step`. + --- ##### `withWorkingDir` @@ -3606,8 +3734,8 @@ 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`. | -| scriptData | string | *No description.* | -| workingDir | string | *No description.* | +| scriptData | string | The body of the script. | +| workingDir | string | Gets the working directory of the `Step` of the `Task`. | --- @@ -3667,6 +3795,8 @@ public readonly scriptData: string; - *Type:* string +The body of the script. + --- ##### `workingDir`Optional @@ -3677,6 +3807,10 @@ public readonly workingDir: string; - *Type:* string +Gets the working directory of the `Step` of the `Task`. + +> [https://tekton.dev/docs/pipelines/tasks/#defining-steps](https://tekton.dev/docs/pipelines/tasks/#defining-steps) + --- @@ -3684,6 +3818,8 @@ public readonly workingDir: string; Builds the Workspaces for use by Tasks and Pipelines. +> [https://tekton.dev/docs/pipelines/workspaces/](https://tekton.dev/docs/pipelines/workspaces/) + #### Initializers ```typescript @@ -3708,8 +3844,8 @@ new WorkspaceBuilder(id: string) | **Name** | **Description** | | --- | --- | -| withDescription | *No description.* | -| withName | *No description.* | +| withDescription | Sets the description of the workspace. | +| withName | Sets the name of the workspace. | --- @@ -3719,6 +3855,8 @@ new WorkspaceBuilder(id: string) public withDescription(desc: string): WorkspaceBuilder ``` +Sets the description of the workspace. + ###### `desc`Required - *Type:* string @@ -3731,6 +3869,8 @@ public withDescription(desc: string): WorkspaceBuilder public withName(name: string): WorkspaceBuilder ``` +Sets the name of the workspace. + ###### `name`Required - *Type:* string diff --git a/README.md b/README.md index 7131c33..0a07ff9 100644 --- a/README.md +++ b/README.md @@ -3,13 +3,118 @@ This is a construct for creating [Pipelines](https://tekton.dev/docs/getting-started/pipelines/) using [cdk8s](https://cdk8s.io/docs/latest/). +In Cloud Development Kit (CDK) terminology, a _construct_ is a code object (class +or application) that can be _synthesized_ to output. In AWS's CDK, that output +is CloudFormation. Here, in a cdk8s construct, that output is YAML that can be +applied on a Kubernetes or OpenShift cluster. Constructs can be used in other +constructs, just like how code classes can by used by other classes. + +This library allows you to create your own Tekton pipeline constructs, which can +then in turn be synthesized into Task, Pipeline, and PipelineRun YAML. + +## Installing prerequisites + +The commands here will use `npx`, using Node 18.x and NPM 9.x. You will also +need `yarn` 1.x (1.22.21 was used here) installed. + +## Using the library to create your own pipeline constructs + +The `projen` command was used to create this construct library and is the easiest +way to create a new construct. The documentation here will show how to use `projen` +to generate your pipeline construct. + +### Creating your project + +To create your pipeline project, run the following command, where `my-pipeline-project` +is the name you want to give your project's directory: + +```bash +$ mkdir my-pipeline-project +$ cd my-pipeline-project +$ npx projen new cdk8s-app-ts +``` + +This will generate a TypeScript project with the cdk8s `constructs` libraries +with the correct structure. + +> ***Why TypeScript and not some other language (e.g., Python)? Because TypeScript can be used to generate all the others.*** + +The command will also initialize a git repository and make an initial commit. + + +### Adding this library to your project + +When using `projen`, modify the _.projenrc.ts_ file to add the libraries +and then run the `npx projen` command--with no additional arguments--to +re-generate the _package.json_ and _yarn.lock_ files and any other files +in the project. When using `projen`, only modify the _.projenrc.ts_ file +and the files in the _src_ folder. + +Modify the _.projenrc.ts_ file and add the following lines to `deps` JSON +element: + +```typescript +const project = new cdk8s.Cdk8sTypeScriptApp({ + // snipped, leave content as-is... + deps: [ + 'cdk8s-pipelines', + 'cdk8s-pipelines-lib', + ], + // snipped, leave content as-is... +}); +``` + +Save the file after you have made the additions and then run the `npx projen` +command to re-generate the project files. + +### Modifying the main Chart + +The _src/main.ts_ file contains the main code that you will modify for your +Pipeline construct. Like any other TypeScript project, you can create classes +and functions in other files and import them for use. + +By default, the template includes a class called `MyChart` that extends from +the cdk8s core `Chart` class. You can rename this class to something a bit +more meaningful, such as `InstallXYZPipelineChart`. + +The `constructor` function contains the code that will create the chart. Here, +replace the sample code with something that looks like this: + +```typescript +export class MyChart extends Chart { + constructor(scope: Construct, id: string, props: ChartProps = { }) { + super(scope, id, props); + + new PipelineBuilder(this, 'my-pipeline') + .withName('clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(new TaskBuilder(this, 'fetch-source') + .withName('git-clone') + .withWorkspace(new WorkspaceBuilder('output').withName('task-output')) + .withStringParam(new ParameterBuilder('url').withPiplineParameter('url').withDescription('the URL for the thing'))) + .buildPipeline(); + } +} +``` + +Start with the imports shown here and add as needed: + +```typescript +import { App, Chart, ChartProps } from 'cdk8s'; +import { ParameterBuilder, PipelineBuilder, TaskBuilder, WorkspaceBuilder } from 'cdk8s-pipelines'; +import { Construct } from 'constructs'; +``` + ## Examples +Shown here is an example of using one of the primitive Tekton objects--a +[Pipeline](https://tekton.dev/docs/pipelines/) using cdk8s-pipelines. + ```typescript const pipeline = new Pipeline(this, 'my-pipeline'); ``` -## Pipeline objects and builders +### Pipeline objects and builders Tekton [Pipelines](https://tekton.dev/docs/pipelines/), [Tasks](https://tekton.dev/docs/pipelines/tasks/), diff --git a/src/builders.ts b/src/builders.ts index 639d054..15be3d1 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -79,6 +79,8 @@ export const DefaultBuilderOptions: BuilderOptions = { /** * Builds the Workspaces for use by Tasks and Pipelines. + * + * @see https://tekton.dev/docs/pipelines/workspaces/ */ export class WorkspaceBuilder { private readonly _logicalID: string; @@ -115,11 +117,19 @@ export class WorkspaceBuilder { return this._description || ''; } + /** + * Sets the name of the workspace. + * @param name + */ public withName(name: string): WorkspaceBuilder { this._name = name; return this; } + /** + * Sets the description of the workspace. + * @param desc + */ public withDescription(desc: string): WorkspaceBuilder { this._description = desc; return this; @@ -129,6 +139,8 @@ export class WorkspaceBuilder { /** * Builds the parameters for use by Tasks and Pipelines. + * + * @see https://tekton.dev/docs/pipelines/pipelines/#specifying-parameters */ export class ParameterBuilder { private readonly _logicalID: string; @@ -152,10 +164,16 @@ export class ParameterBuilder { return this._logicalID; } + /** + * Gets the name of the parameter. + */ public get name(): string | undefined { return this._name; } + /** + * Gets the description of the parameter. + */ public get description(): string { return this._description || ''; } @@ -308,15 +326,28 @@ class UrlScriptResolver implements ScriptResolver { } } +/** + * Gets the content from the provided URL and returns it as the script data, + * but prints the output of running the script to the given results. + */ class UrlScriptToResultsResolver implements ScriptResolver { readonly _resolver: ScriptResolver; readonly _resultsName: string; + /** + * Creates a new instance of the UrlScriptToResultsResolver. + * @param url The Uniform Resource Locator (URL) of the script. + * @param resultsName The name of the results into which the script prints output. + */ constructor(url: string, resultsName: string) { this._resolver = new UrlScriptResolver(url); this._resultsName = resultsName; } + /** + * The body of the script. This will include a `tee `, + * so make sure the script you use here does not already have it. + */ public scriptData(): string { return [this._resolver.scriptData(), `tee ${usingResultsPath(this._resultsName)}`].join(' | '); } @@ -357,10 +388,9 @@ export class TaskStepBuilder { private _script?: ScriptResolver; /** - * + * Creates a new instance of the TaskStepBuilder. */ public constructor() { - } /** @@ -378,6 +408,9 @@ export class TaskStepBuilder { return this._image; } + /** + * The body of the script. + */ public get scriptData(): string | undefined { return this._script?.scriptData(); } @@ -396,10 +429,20 @@ export class TaskStepBuilder { return this._cmd; } + /** + * Gets the working directory of the `Step` of the `Task`. + * + * @see https://tekton.dev/docs/pipelines/tasks/#defining-steps + */ public get workingDir(): string | undefined { return this._dir; } + /** + * Sets the name of the `Step` of the `Task`. + * + * @param name Name of the `Step` + */ public withName(name: string): TaskStepBuilder { this._name = name; return this; @@ -498,6 +541,15 @@ export class TaskStepBuilder { return this; } + /** + * Sets the environment variable for the Step. + * + * @param name The name of the `env` to add or set. + * @param valueFrom The source of the value for the variable. + * + * @see https://tekton.dev/docs/pipelines/podtemplates/ + * @see valueFrom() + */ withEnv(name: string, valueFrom: TaskEnvValueSource): TaskStepBuilder { if (!this._env) { this._env = new Array(); @@ -509,6 +561,10 @@ export class TaskStepBuilder { return this; } + /** + * When called, builds the `Step`. This will be called the `TaskBuilder`; + * do not call it directly. + */ public buildTaskStep(): TaskStep | undefined { if (this._script) { return { @@ -583,7 +639,7 @@ export class TaskBuilder { * @param value */ public withLabel(key: string, value: string): TaskBuilder { - if (! this._labels) { + if (!this._labels) { this._labels = {}; } this._labels[key] = value; @@ -599,7 +655,7 @@ export class TaskBuilder { * @param value The annotation's value. */ public withAnnotation(key: string, value: string): TaskBuilder { - if (! this._annotations) { + if (!this._annotations) { this._annotations = {}; } this._annotations[key] = value; @@ -1069,7 +1125,7 @@ export class PipelineRunBuilder { const workspaces: PipelineWorkspace[] = this._pipeline.workspaces; workspaces.forEach((ws) => { const pws = this._runWorkspaces.find((obj) => obj.name == ws.name); - if (! pws) { + if (!pws) { throw new Error(`Pipeline workspace '${ws.name}' is not defined in PipelineRun '${this._id}'`); } }); From 9432ed3f19c53fe0f1e81ffbdab2c412bfd7ab8d Mon Sep 17 00:00:00 2001 From: Nathan Good Date: Wed, 28 Feb 2024 13:22:25 -0600 Subject: [PATCH 02/30] Added documentation for testing pipelines created from library. Signed-off-by: Nathan Good --- API.md | 21 +++++++++++++++++++++ README.md | 21 +++++++++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/API.md b/API.md index cc570b5..0daa2df 100644 --- a/API.md +++ b/API.md @@ -105,6 +105,27 @@ import { ParameterBuilder, PipelineBuilder, TaskBuilder, WorkspaceBuilder } from import { Construct } from 'constructs'; ``` +## Testing your pipeline locally + +Once you have added code to create the pipeline, use the `npx projen build` +command to compile the TypeScript code and run the `cdk8s synth` command to +create the YAML output. The file will be written to the _dist_ folder and named +_chart-id.yml_, where _chart-id_ the string value passed to your class +that extends `Chart`. For example, in this code: + +```typescript +const app = new App(); +new MyInstallPipeline(app, 'my-install-pipeline'); +app.synth(); +``` + +The output file will be _dist/my-install-pipeline.yml_. + +Apply this file to a Kubernetes or OpenShift cluster using `oc apply -f dist/my-install-pipeline.yml`, +substituting the name of your output file in the command. If using OpenShift +Container Platform (OCP), you must have the OpenShift Pipelines operator installed. +If using Kubernetes, make sure you have [Tekton installed](https://tekton.dev/docs/installation/). + ## Examples Shown here is an example of using one of the primitive Tekton objects--a diff --git a/README.md b/README.md index 0a07ff9..675cec4 100644 --- a/README.md +++ b/README.md @@ -105,6 +105,27 @@ import { ParameterBuilder, PipelineBuilder, TaskBuilder, WorkspaceBuilder } from import { Construct } from 'constructs'; ``` +## Testing your pipeline locally + +Once you have added code to create the pipeline, use the `npx projen build` +command to compile the TypeScript code and run the `cdk8s synth` command to +create the YAML output. The file will be written to the _dist_ folder and named +_chart-id.yml_, where _chart-id_ the string value passed to your class +that extends `Chart`. For example, in this code: + +```typescript +const app = new App(); +new MyInstallPipeline(app, 'my-install-pipeline'); +app.synth(); +``` + +The output file after running `npx projen build` will be _dist/my-install-pipeline.yml_. + +Apply this file to a Kubernetes or OpenShift cluster using `oc apply -f dist/my-install-pipeline.yml`, +substituting the name of your output file in the command. If using OpenShift +Container Platform (OCP), you must have the OpenShift Pipelines operator installed. +If using Kubernetes, make sure you have [Tekton installed](https://tekton.dev/docs/installation/). + ## Examples Shown here is an example of using one of the primitive Tekton objects--a From 317865e0d516ce438139e3ca967993a8ea879eee Mon Sep 17 00:00:00 2001 From: Nathan Good Date: Wed, 28 Feb 2024 13:28:59 -0600 Subject: [PATCH 03/30] Updated API.md documentation from build Signed-off-by: Nathan Good --- API.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/API.md b/API.md index 0daa2df..6765f99 100644 --- a/API.md +++ b/API.md @@ -119,7 +119,7 @@ new MyInstallPipeline(app, 'my-install-pipeline'); app.synth(); ``` -The output file will be _dist/my-install-pipeline.yml_. +The output file after running `npx projen build` will be _dist/my-install-pipeline.yml_. Apply this file to a Kubernetes or OpenShift cluster using `oc apply -f dist/my-install-pipeline.yml`, substituting the name of your output file in the command. If using OpenShift From a3236442ff6a7b92d5baf44694047ec52ad84bc1 Mon Sep 17 00:00:00 2001 From: Nathan Good Date: Wed, 5 Jun 2024 11:17:01 -0500 Subject: [PATCH 04/30] WIP: adding parameters at the pipeline level and references Signed-off-by: Nathan Good --- src/builders.ts | 75 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 57 insertions(+), 18 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 15be3d1..66f2c99 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -58,6 +58,54 @@ const DefaultClusterRoleBindingProps = createRoleBindingProps( 'pipeline', 'default'); +/** + * Resolves the `script` through different means. + */ +interface ValueResolver { + /** + * Gets the string value for the + * @returns string The script. + */ + get value(): string; +} + +export class ConstantStringValueResolver implements ValueResolver { + + private val: string; + + constructor(val: string) { + this.val = val; + } + + public get value(): string { + return this.val; + } +} + +export class PipelineParameterValueResolver implements ValueResolver { + + private val: ParameterBuilder; + + constructor(param: ParameterBuilder) { + this.val = param; + } + + get value(): string { + // TODO: Fix this... needs to return a representation that would be a link + // to the actual parameter + return "$(param.foo)" + } + +} + +export function constant(val: string) : ValueResolver { + return new ConstantStringValueResolver(val); +} + +export function fromPipelineParam(param: ParameterBuilder): ValueResolver { + return new PipelineParameterValueResolver(param); +} + /** * The options for builders for the `buildXX()` methods. */ @@ -147,7 +195,7 @@ export class ParameterBuilder { private _name?: string; private _description?: string; private _type?: string; - private _value?: string; + private _value?: ValueResolver; private _defaultValue?: string; private _requiresPipelineParam: boolean; @@ -216,11 +264,13 @@ export class ParameterBuilder { * Sets the value for the parameter * @param val */ - public withValue(val: string): ParameterBuilder { + public withValue(val: string | ValueResolver): ParameterBuilder { // If you are giving it a value here, then you do not // need the Pipeline parameter for this parameter. this._requiresPipelineParam = false; - this._value = val; + if (typeof(val) === 'string') { + this._value = constant(val); + } return this; } @@ -228,7 +278,7 @@ export class ParameterBuilder { * Gets the value of the parameter */ public get value(): string | undefined { - return this._value; + return this._value?.value; } /** @@ -244,19 +294,6 @@ export class ParameterBuilder { return this._defaultValue; } - /** - * Sets the default value for the parameter. - * @param pipelineParamName - * @param defaultValue - */ - public withPiplineParameter(pipelineParamName: string, defaultValue: string = ''): ParameterBuilder { - this._requiresPipelineParam = true; - this._name = pipelineParamName; - this._defaultValue = defaultValue; - this._value = usingBuildParameter(pipelineParamName); - return this; - } - /** * Returns true if this parameter expects input at the pipeline level. */ @@ -882,6 +919,8 @@ export class PipelineBuilder { pipelineParams.set(p.name!, { name: p.name, type: p.type, + // Fix: https://github.com/cloud-native-toolkit/cdk8s-pipelines/issues/43 + default: p.defaultValue, }); } } @@ -1144,4 +1183,4 @@ export class PipelineRunBuilder { }, }); } -} \ No newline at end of file +} From b1dc25e3639eb02b44aa946d6a2333b7ca9f28eb Mon Sep 17 00:00:00 2001 From: Sneha Jaiswal Date: Thu, 6 Jun 2024 14:46:00 -0400 Subject: [PATCH 05/30] deprecated withName and made name property reference logical ids. WIP: pipeline-level parameters and references --- src/builders.ts | 73 +++++++++++++++++++++++++++++++++++++------------ 1 file changed, 55 insertions(+), 18 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 66f2c99..56bc829 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -59,12 +59,12 @@ const DefaultClusterRoleBindingProps = createRoleBindingProps( 'default'); /** - * Resolves the `script` through different means. + * Resolves the value through different means. */ interface ValueResolver { /** - * Gets the string value for the - * @returns string The script. + * Gets the string value for a parameter + * @returns string The value. */ get value(): string; } @@ -93,7 +93,8 @@ export class PipelineParameterValueResolver implements ValueResolver { get value(): string { // TODO: Fix this... needs to return a representation that would be a link // to the actual parameter - return "$(param.foo)" + //return `$(params.${this.val.logicalID})` + return usingBuildParameter(this.val.logicalID!) } } @@ -142,6 +143,7 @@ export class WorkspaceBuilder { */ constructor(id: string) { this._logicalID = id; + this._name = id; } /** @@ -166,6 +168,7 @@ export class WorkspaceBuilder { } /** + * @deprecated name is set by logicalID * Sets the name of the workspace. * @param name */ @@ -192,7 +195,7 @@ export class WorkspaceBuilder { */ export class ParameterBuilder { private readonly _logicalID: string; - private _name?: string; + private _name: string; private _description?: string; private _type?: string; private _value?: ValueResolver; @@ -201,6 +204,7 @@ export class ParameterBuilder { constructor(id: string) { this._logicalID = id; + this._name = id; this._requiresPipelineParam = false; } @@ -227,6 +231,7 @@ export class ParameterBuilder { } /** + * @deprecated name is set by logicalID * Sets the name of the parameter. * @param name */ @@ -271,6 +276,12 @@ export class ParameterBuilder { if (typeof(val) === 'string') { this._value = constant(val); } + else { + if (val instanceof PipelineParameterValueResolver) { + this._requiresPipelineParam = true; + } + this._value = val; + } return this; } @@ -658,6 +669,7 @@ export class TaskBuilder { public constructor(scope: Construct, id: string) { this._scope = scope; this._id = id; + this._name = id; // These are required, and it's better to just create it rather than // check each time. this._steps = new Array(); @@ -707,6 +719,7 @@ export class TaskBuilder { } /** + * @deprecated name is set by id * Sets the name of the `Task` being built. * @param name */ @@ -851,13 +864,16 @@ export class PipelineBuilder { private _name?: string; private _description?: string; private _tasks?: TaskBuilder[]; + private _params?: Map; public constructor(scope: Construct, id: string) { this._scope = scope; this._id = id; + this._name = id; } /** + * @deprecated name is set by id * Provides the name for the pipeline task and will be * rendered as the `name` property. * @param name @@ -894,6 +910,18 @@ export class PipelineBuilder { return this; } + /** + * Add parameter of type string to the Pipeline + * @param param + */ + public withStringParam(param: ParameterBuilder): PipelineBuilder { + if (!this._params) { + this._params = new Map(); + } + this._params.set(param.logicalID!, param.ofType('string')); + return this; + } + /** * Returns the array of `PipelineParam` objects that represent the parameters * configured for the `Pipeline`. @@ -910,18 +938,23 @@ export class PipelineBuilder { // operation, so we only need to do it if the state of the object has not // changed. const pipelineParams = new Map(); + // First, add all the user-supplied pipeline-level parameters + this._params?.forEach((par) => { + pipelineParams.set(par.logicalID!, { + name: par.logicalID, + type: par.type, + default: par.defaultValue + }); + }); + // Then, check if any Tasks require a missing pipeline-level parameter this._tasks?.forEach((t) => { t.parameters?.forEach(p => { - const pp = pipelineParams.get(p.name!); - if (!pp) { - // Do not add it to the pipeline if there is no need to add it... - if (p.requiresPipelineParameter) { - pipelineParams.set(p.name!, { - name: p.name, - type: p.type, - // Fix: https://github.com/cloud-native-toolkit/cdk8s-pipelines/issues/43 - default: p.defaultValue, - }); + if(p.requiresPipelineParameter) { + // TODO: find better way to retrieve ID of pipeline-level param referenced by task param? + const requiredPipelineParam = p.value!.substring(9, p.value!.length - 1); + const pp = pipelineParams.has(requiredPipelineParam); + if(!pp) { + throw new Error(`Parameter '${p.logicalID}' in Task '${t.name}' expects Pipeline value from parameter '${requiredPipelineParam}', which is not defined.`); } } }); @@ -946,10 +979,10 @@ export class PipelineBuilder { t.workspaces?.forEach((w) => { // Only add the workspace on the pipeline level if it is not already // there... - const ws = pipelineWorkspaces.get(w.name!); + const ws = pipelineWorkspaces.get(w.logicalID!); if (!ws) { - pipelineWorkspaces.set(w.name!, { - name: w.name, + pipelineWorkspaces.set(w.logicalID!, { + name: w.logicalID, description: w.description, }); } @@ -1022,6 +1055,10 @@ export class PipelineBuilder { } } +// Side effect: since withName is deprecated and t.name = t.logicalID, the task's name and the name +// of its taskRef are forced to be identical. +// TODO: make withTaskRef function in TaskBuilder to assign taskRef['name'] +// TODO: compensate tasks with steps instead of a ref in function below function createOrderedPipelineTask(t: TaskBuilder, after: string, params: TaskParam[], ws: TaskWorkspace[]): PipelineTask { if (after) { return { From a20fb1b16e9f94f90e0153af42aa84e6f8a13e5d Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Thu, 6 Jun 2024 16:38:13 -0400 Subject: [PATCH 06/30] Pipeline-level parameters and references from tasks. deprecate withName only for ParameterBuilder --- src/builders.ts | 16 +++------------- test/pipelinebuilder.test.ts | 34 ++++++++++++++++++++++++++++++++++ 2 files changed, 37 insertions(+), 13 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 56bc829..612a449 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -143,7 +143,6 @@ export class WorkspaceBuilder { */ constructor(id: string) { this._logicalID = id; - this._name = id; } /** @@ -168,7 +167,6 @@ export class WorkspaceBuilder { } /** - * @deprecated name is set by logicalID * Sets the name of the workspace. * @param name */ @@ -669,7 +667,6 @@ export class TaskBuilder { public constructor(scope: Construct, id: string) { this._scope = scope; this._id = id; - this._name = id; // These are required, and it's better to just create it rather than // check each time. this._steps = new Array(); @@ -719,7 +716,6 @@ export class TaskBuilder { } /** - * @deprecated name is set by id * Sets the name of the `Task` being built. * @param name */ @@ -869,11 +865,9 @@ export class PipelineBuilder { public constructor(scope: Construct, id: string) { this._scope = scope; this._id = id; - this._name = id; } /** - * @deprecated name is set by id * Provides the name for the pipeline task and will be * rendered as the `name` property. * @param name @@ -979,10 +973,10 @@ export class PipelineBuilder { t.workspaces?.forEach((w) => { // Only add the workspace on the pipeline level if it is not already // there... - const ws = pipelineWorkspaces.get(w.logicalID!); + const ws = pipelineWorkspaces.get(w.name!); if (!ws) { - pipelineWorkspaces.set(w.logicalID!, { - name: w.logicalID, + pipelineWorkspaces.set(w.name!, { + name: w.name, description: w.description, }); } @@ -1055,10 +1049,6 @@ export class PipelineBuilder { } } -// Side effect: since withName is deprecated and t.name = t.logicalID, the task's name and the name -// of its taskRef are forced to be identical. -// TODO: make withTaskRef function in TaskBuilder to assign taskRef['name'] -// TODO: compensate tasks with steps instead of a ref in function below function createOrderedPipelineTask(t: TaskBuilder, after: string, params: TaskParam[], ws: TaskWorkspace[]): PipelineTask { if (after) { return { diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index 4e3bdb5..110940c 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -8,6 +8,8 @@ import { PipelineRunBuilder, TaskBuilder, WorkspaceBuilder, + fromPipelineParam, + constant } from '../src'; class PipelineRunTest extends Chart { @@ -215,6 +217,31 @@ class MyTestChartWithDuplicateTasks extends Chart { } } +class PipelineLevelParamTest extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const pipelineParam = new ParameterBuilder('context') + .withDefaultValue('/some/where/or/other') + + const taskParam = new ParameterBuilder('pathToDockerFile') + .withValue(constant('Dockerfile')) + + const taskParam2 = new ParameterBuilder('pathToContext') + .withValue(fromPipelineParam(pipelineParam)) + + const myTask = new TaskBuilder(this, 'build-skaffold-web') + .withName('build-push') + .withStringParam(taskParam) + .withStringParam(taskParam2) + + new PipelineBuilder(this, 'pipeline-with-parameters') + .withStringParam(pipelineParam) + .withTask(myTask) + .buildPipeline({ includeDependencies: true }); + } +} + describe('PipelineBuilderTest', () => { test('PipelineRunBuilder', () => { const app = Testing.app(); @@ -274,4 +301,11 @@ describe('PipelineBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + + // test('PipelineBuilderWithParameters', () => { + // const app = Testing.app(); + // const chart = new PipelineLevelParamTest(app, 'test-chart'); + // const results = Testing.synth(chart); + // expect(results).toMatchSnapshot(); + // }) }); From 5cb96770561fb950098f4d12a1cf0bc4c542261b Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Thu, 6 Jun 2024 16:54:05 -0400 Subject: [PATCH 07/30] Updated unit tests to reflect new feature --- test/pipelinebuilder.test.ts | 78 ++++++++++++++++++++++++++---------- 1 file changed, 56 insertions(+), 22 deletions(-) diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index 110940c..c4827f7 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -16,17 +16,21 @@ class PipelineRunTest extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') .withWorkspace(new WorkspaceBuilder('output') .withName('shared-data') .withDescription('The files cloned by the task')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('repo-url', '')); + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); const pipeline = new PipelineBuilder(this, 'my-pipeline') .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(myTask); + .withTask(myTask) + .withStringParam(pipelineParam); pipeline.buildPipeline({ includeDependencies: true }); new PipelineRunBuilder(this, 'my-pipeline-run', pipeline) @@ -40,17 +44,21 @@ class PipelineRunTestWithUndefinedWorkspaceError extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') .withWorkspace(new WorkspaceBuilder('output') .withName('shared-data') .withDescription('The files cloned by the task')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('repo-url', '')); + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); const pipeline = new PipelineBuilder(this, 'my-pipeline') .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(myTask); + .withTask(myTask) + .withStringParam(pipelineParam); pipeline.buildPipeline({ includeDependencies: true }); new PipelineRunBuilder(this, 'my-pipeline-run', pipeline) @@ -63,17 +71,21 @@ class PipelineRunTestWithUndefinedParamError extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') .withWorkspace(new WorkspaceBuilder('output') .withName('shared-data') .withDescription('The files cloned by the task')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('repo-url', '')); + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); const pipeline = new PipelineBuilder(this, 'my-pipeline') .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(myTask); + .withTask(myTask) + .withStringParam(pipelineParam); pipeline.buildPipeline({ includeDependencies: true }); new PipelineRunBuilder(this, 'my-pipeline-run', pipeline) @@ -87,17 +99,21 @@ class PipelineRunTestWithError extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') .withWorkspace(new WorkspaceBuilder('output') .withName('shared-data') .withDescription('The files cloned by the task')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('repo-url', '')); + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); const pipeline = new PipelineBuilder(this, 'my-pipeline') .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(myTask); + .withTask(myTask) + .withStringParam(pipelineParam); pipeline.buildPipeline({ includeDependencies: true }); new PipelineRunBuilder(this, 'my-pipeline-run', pipeline) @@ -111,6 +127,15 @@ class MySecondTestChart extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); + const repoParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const nameParam = new ParameterBuilder('your-name') + .withDefaultValue('') + const colorParam = new ParameterBuilder('your-color') + .withDefaultValue('') + const questParam = new ParameterBuilder('your-quest') + .withDefaultValue('') + const myTask = new TaskBuilder(this, 'git-clone') .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output') @@ -122,15 +147,19 @@ class MySecondTestChart extends Chart { .withWorkspace(new WorkspaceBuilder('config') .withName('config-data') .withDescription('The files for configuration for stuff')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('repo-url', '')) - .withStringParam(new ParameterBuilder('name').withPiplineParameter('your-name', '')) - .withStringParam(new ParameterBuilder('color').withPiplineParameter('your-color', '')) - .withStringParam(new ParameterBuilder('quest').withPiplineParameter('your-quest', '')); + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(repoParam))) + .withStringParam(new ParameterBuilder('name').withValue(fromPipelineParam(nameParam))) + .withStringParam(new ParameterBuilder('color').withValue(fromPipelineParam(colorParam))) + .withStringParam(new ParameterBuilder('quest').withValue(fromPipelineParam(questParam))); new PipelineBuilder(this, 'my-test-pipeline-2') .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) + .withStringParam(repoParam) + .withStringParam(nameParam) + .withStringParam(colorParam) + .withStringParam(questParam) .buildPipeline(); } } @@ -142,9 +171,12 @@ class MyTestChartWithDuplicateParams extends Chart { const myWorkspace = new WorkspaceBuilder('output') .withDescription('The files cloned by the task') .withName('shared-data'); + + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') const urlParam = new ParameterBuilder('url') - .withPiplineParameter('repo-url', ''); + .withValue(fromPipelineParam(pipelineParam)); const myTask = new TaskBuilder(this, 'git-clone') .withName('fetch-source') @@ -163,6 +195,7 @@ class MyTestChartWithDuplicateParams extends Chart { .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withTask(myTask2) + .withStringParam(pipelineParam) .buildPipeline(); } } @@ -172,8 +205,11 @@ class MyTestChartWithStaticOverride extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const urlParam = new ParameterBuilder('url') - .withPiplineParameter('repo-url', ''); + .withValue(fromPipelineParam(pipelineParam)); const myTask2 = new TaskBuilder(this, 'cat-readme') .withName('print-readme') @@ -187,6 +223,7 @@ class MyTestChartWithStaticOverride extends Chart { .withName('clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask2) + // pipelineParam not added because myTask2 doesn't reference it anymore .buildPipeline(); } } @@ -199,8 +236,11 @@ class MyTestChartWithDuplicateTasks extends Chart { .withDescription('The files cloned by the task') .withName('shared-data'); + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue('') + const urlParam = new ParameterBuilder('url') - .withPiplineParameter('repo-url', ''); + .withValue(fromPipelineParam(pipelineParam)); const myTask = new TaskBuilder(this, 'git-clone') .withName('fetch-source') @@ -213,6 +253,7 @@ class MyTestChartWithDuplicateTasks extends Chart { .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withTask(myTask) + .withStringParam(pipelineParam) .buildPipeline({ includeDependencies: true }); } } @@ -301,11 +342,4 @@ describe('PipelineBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); - - // test('PipelineBuilderWithParameters', () => { - // const app = Testing.app(); - // const chart = new PipelineLevelParamTest(app, 'test-chart'); - // const results = Testing.synth(chart); - // expect(results).toMatchSnapshot(); - // }) }); From f3b1f9a393bfeef8fd80a3defcd90d7a84eae56b Mon Sep 17 00:00:00 2001 From: Nathan Good Date: Fri, 7 Jun 2024 08:58:49 -0500 Subject: [PATCH 08/30] Bumped version of jsii to current supported version as other was EOL Signed-off-by: Nathan Good --- .projen/deps.json | 4 +- .projenrc.ts | 2 +- package.json | 4 +- yarn.lock | 562 ++++++++++++++++++++++++++++++++++++++++++---- 4 files changed, 529 insertions(+), 43 deletions(-) diff --git a/.projen/deps.json b/.projen/deps.json index 956871a..21ff810 100644 --- a/.projen/deps.json +++ b/.projen/deps.json @@ -77,12 +77,12 @@ }, { "name": "jsii-rosetta", - "version": "~5.2.0", + "version": "~5.4.0", "type": "build" }, { "name": "jsii", - "version": "~5.2.0", + "version": "~5.4.0", "type": "build" }, { diff --git a/.projenrc.ts b/.projenrc.ts index fd80ab7..f2cc7ab 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -6,7 +6,7 @@ const project = new cdk8s.ConstructLibraryCdk8s({ author: 'Nathan Good', authorAddress: 'nathan.good@ibm.com', cdk8sVersion: '2.68.30', - jsiiVersion: '~5.2.0', + jsiiVersion: '~5.4.0', workflowNodeVersion: '18.x', projenrcTs: true, peerDeps: [ diff --git a/package.json b/package.json index 51cf616..9f94fd7 100644 --- a/package.json +++ b/package.json @@ -48,11 +48,11 @@ "eslint-plugin-import": "^2.28.0", "jest": "^29.6.2", "jest-junit": "^15", - "jsii": "~5.2.0", + "jsii": "~5.4.0", "jsii-diff": "^1.86.1", "jsii-docgen": "^9.1.2", "jsii-pacmak": "^1.86.1", - "jsii-rosetta": "~5.2.0", + "jsii-rosetta": "~5.4.0", "npm-check-updates": "^16", "projen": "^0.72.7", "standard-version": "^9", diff --git a/yarn.lock b/yarn.lock index 781b18c..df6d90f 100644 --- a/yarn.lock +++ b/yarn.lock @@ -659,10 +659,10 @@ chalk "^4.1.2" semver "^7.5.4" -"@jsii/check-node@1.93.0": - version "1.93.0" - resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.93.0.tgz#3adcc6012654bb69fb8dc508e757b83ea9cd1708" - integrity sha512-NLn1Js6wEG2hYjH7gE5Q8s/hPlp3I+KhK/T8ykGdYVod7iODnk/0QVSZsk2iEyuw8NzvvgXUDBWreadUIWSz+g== +"@jsii/check-node@1.98.0": + version "1.98.0" + resolved "https://registry.yarnpkg.com/@jsii/check-node/-/check-node-1.98.0.tgz#140860478009834aa35dc8479a26db1a221439d5" + integrity sha512-hI53TMW/fylHyY3CrJvqWvfSPJvBL82GSAB1m2CKNC0yHb0pZHCdBZnLrrr4rgTCQx8kIJjcUc0rQ/Ba3w+GaA== dependencies: chalk "^4.1.2" semver "^7.5.4" @@ -674,12 +674,12 @@ dependencies: ajv "^8.12.0" -"@jsii/spec@^1.93.0": - version "1.94.0" - resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.94.0.tgz#a4584179cd83e50110169a3f5ec1b6ab4ad362f4" - integrity sha512-ur1aUMPsdZgflUIZC4feyJzrkGYzvtiIJxRowkSxr7Ip/sLCKvi61dvImWtJY9ZhEAl7Kiq7I/R32WVyxW0JrQ== +"@jsii/spec@^1.98.0": + version "1.99.0" + resolved "https://registry.yarnpkg.com/@jsii/spec/-/spec-1.99.0.tgz#f71bfcf83e2e48d7894821cc8b52c320f52cbbf4" + integrity sha512-R4E0lFj+C2PpLt2tnexIlQA7Ovy52tL9PRcDy6sUcnJto4iZufexudIm4pjIJPN+bfwimQX5aMjALloRwDixtQ== dependencies: - ajv "^8.12.0" + ajv "^8.13.0" "@nodelib/fs.scandir@2.1.5": version "2.1.5" @@ -1305,6 +1305,16 @@ ajv@^8.12.0: require-from-string "^2.0.2" uri-js "^4.2.2" +ajv@^8.13.0: + version "8.16.0" + resolved "https://registry.yarnpkg.com/ajv/-/ajv-8.16.0.tgz#22e2a92b94f005f7e0f9c9d39652ef0b8f6f0cb4" + integrity sha512-F0twR8U1ZU67JIEtekUcLkXkoO5mMMmgGD8sK/xUFzJ805jxHQl92hImFAqqXMyMYjSPOyUPAwHYhB72g5sTXw== + dependencies: + fast-deep-equal "^3.1.3" + json-schema-traverse "^1.0.0" + require-from-string "^2.0.2" + uri-js "^4.4.1" + ansi-align@^3.0.1: version "3.0.1" resolved "https://registry.yarnpkg.com/ansi-align/-/ansi-align-3.0.1.tgz#0cdf12e111ace773a86e9a1fad1225c43cb19a59" @@ -1399,6 +1409,14 @@ array-buffer-byte-length@^1.0.0: call-bind "^1.0.2" is-array-buffer "^3.0.1" +array-buffer-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz#1e5583ec16763540a27ae52eed99ff899223568f" + integrity sha512-ahC5W1xgou+KTXix4sAO8Ki12Q+jf4i0+tmk3sC+zgcynshkHxzpXdImBehiUYKKKDwvfFiJl1tZt6ewscS1Mg== + dependencies: + call-bind "^1.0.5" + is-array-buffer "^3.0.4" + array-ify@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/array-ify/-/array-ify-1.0.0.tgz#9e528762b4a9066ad163a6962a364418e9626ece" @@ -1468,6 +1486,20 @@ arraybuffer.prototype.slice@^1.0.1: is-array-buffer "^3.0.2" is-shared-array-buffer "^1.0.2" +arraybuffer.prototype.slice@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/arraybuffer.prototype.slice/-/arraybuffer.prototype.slice-1.0.3.tgz#097972f4255e41bc3425e37dc3f6421cf9aefde6" + integrity sha512-bMxMKAjg13EBSVscxTaYA4mRc5t1UAXa2kXiGTNfZ079HIWXEkKmkgFrh/nJqamaLSrXO5H4WFFkPEaLJWbs3A== + dependencies: + array-buffer-byte-length "^1.0.1" + call-bind "^1.0.5" + define-properties "^1.2.1" + es-abstract "^1.22.3" + es-errors "^1.2.1" + get-intrinsic "^1.2.3" + is-array-buffer "^3.0.4" + is-shared-array-buffer "^1.0.2" + arrify@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/arrify/-/arrify-1.0.1.tgz#898508da2226f380df904728456849c1501a4b0d" @@ -1488,6 +1520,13 @@ available-typed-arrays@^1.0.5: resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.5.tgz#92f95616501069d07d10edb2fc37d3e1c65123b7" integrity sha512-DMD0KiN46eipeziST1LPP/STfDU0sufISXmjSgvVsoU2tqxctQeASejWcfNtxYKqETM1UxQ8sp2OrSBWpHY6sw== +available-typed-arrays@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/available-typed-arrays/-/available-typed-arrays-1.0.7.tgz#a5cc375d6a03c2efc87a553f3e0b1522def14846" + integrity sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ== + dependencies: + possible-typed-array-names "^1.0.0" + axios@^0.27.2: version "0.27.2" resolved "https://registry.yarnpkg.com/axios/-/axios-0.27.2.tgz#207658cc8621606e586c85db4b41a750e756d972" @@ -1729,6 +1768,17 @@ call-bind@^1.0.0, call-bind@^1.0.2: function-bind "^1.1.1" get-intrinsic "^1.0.2" +call-bind@^1.0.5, call-bind@^1.0.6, call-bind@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/call-bind/-/call-bind-1.0.7.tgz#06016599c40c56498c18769d2730be242b6fa3b9" + integrity sha512-GHTSNSYICQ7scH7sZ+M2rFopRoLh8t2bLSW6BbgrtLsahOIB5iyAVJf9GjWK3cYTDaMj4XdBpM1cA6pIS0Kv2w== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + set-function-length "^1.2.1" + callsites@^3.0.0: version "3.1.0" resolved "https://registry.yarnpkg.com/callsites/-/callsites-3.1.0.tgz#b3630abd8943432f54b3f0519238e33cd7df2f73" @@ -2004,6 +2054,16 @@ commonmark@^0.30.0: minimist ">=1.2.2" string.prototype.repeat "^0.2.0" +commonmark@^0.31.0: + version "0.31.0" + resolved "https://registry.yarnpkg.com/commonmark/-/commonmark-0.31.0.tgz#4ac57c61f0d7f5ef82d79447a972c61226ef5abc" + integrity sha512-nuDsQ34gjmgAqjyIz6mbRWBW/XPE9wsBempAMBk2V/AA88ekztjTM46oi07J6c6Y/2Y8TdYCZi9L0pIBt/oMZw== + dependencies: + entities "~3.0.1" + mdurl "~1.0.1" + minimist "~1.2.5" + string.prototype.repeat "^1.0.0" + compare-func@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/compare-func/-/compare-func-2.0.0.tgz#fb65e75edbddfd2e568554e8b5b05fff7a51fcb3" @@ -2260,6 +2320,33 @@ dargs@^7.0.0: resolved "https://registry.yarnpkg.com/dargs/-/dargs-7.0.0.tgz#04015c41de0bcb69ec84050f3d9be0caf8d6d5cc" integrity sha512-2iy1EkLdlBzQGvbweYRFxmFath8+K7+AKB0TlhHWkNuH+TmovaMH/Wp7V7R4u7f4SnX3OgLsU9t1NI9ioDnUpg== +data-view-buffer@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-buffer/-/data-view-buffer-1.0.1.tgz#8ea6326efec17a2e42620696e671d7d5a8bc66b2" + integrity sha512-0lht7OugA5x3iJLOWFhWK/5ehONdprk0ISXqVFn/NFrDu+cuc8iADFrGQz5BnRK7LLU3JmkbXSxaqX+/mXYtUA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/data-view-byte-length/-/data-view-byte-length-1.0.1.tgz#90721ca95ff280677eb793749fce1011347669e2" + integrity sha512-4J7wRJD3ABAzr8wP+OcIcqq2dlUKp4DVflx++hs5h5ZKydWMI6/D/fAot+yh6g2tHh8fLFTvNOaVN357NvSrOQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-data-view "^1.0.1" + +data-view-byte-offset@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/data-view-byte-offset/-/data-view-byte-offset-1.0.0.tgz#5e0bbfb4828ed2d1b9b400cd8a7d119bca0ff18a" + integrity sha512-t/Ygsytq+R995EJ5PZlD4Cu56sWa8InXySaViRzw9apusqsOO2bQP+SbYzAhR0pFKoB+43lYy8rWban9JSuXnA== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-data-view "^1.0.1" + date-format@^4.0.14: version "4.0.14" resolved "https://registry.yarnpkg.com/date-format/-/date-format-4.0.14.tgz#7a8e584434fb169a521c8b7aa481f355810d9400" @@ -2346,6 +2433,15 @@ defer-to-connect@^2.0.1: resolved "https://registry.yarnpkg.com/defer-to-connect/-/defer-to-connect-2.0.1.tgz#8016bdb4143e4632b77a3449c6236277de520587" integrity sha512-4tvttepXG1VaYGrRibk5EwJd1t4udunSOVMdLSAL6mId1ix438oPwPZMALY41FCijukO1L0twNcGsdzS7dHgDg== +define-data-property@^1.0.1, define-data-property@^1.1.4: + version "1.1.4" + resolved "https://registry.yarnpkg.com/define-data-property/-/define-data-property-1.1.4.tgz#894dc141bb7d3060ae4366f6a0107e68fbe48c5e" + integrity sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A== + dependencies: + es-define-property "^1.0.0" + es-errors "^1.3.0" + gopd "^1.0.1" + define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: version "1.2.0" resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.0.tgz#52988570670c9eacedd8064f4a990f2405849bd5" @@ -2354,6 +2450,15 @@ define-properties@^1.1.3, define-properties@^1.1.4, define-properties@^1.2.0: has-property-descriptors "^1.0.0" object-keys "^1.1.1" +define-properties@^1.2.1: + version "1.2.1" + resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.2.1.tgz#10781cc616eb951a80a034bafcaa7377f6af2b6c" + integrity sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg== + dependencies: + define-data-property "^1.0.1" + has-property-descriptors "^1.0.0" + object-keys "^1.1.1" + del@^6.1.1: version "6.1.1" resolved "https://registry.yarnpkg.com/del/-/del-6.1.1.tgz#3b70314f1ec0aa325c6b14eb36b95786671edb7a" @@ -2520,6 +2625,11 @@ entities@~2.0: resolved "https://registry.yarnpkg.com/entities/-/entities-2.0.3.tgz#5c487e5742ab93c15abb5da22759b8590ec03b7f" integrity sha512-MyoZ0jgnLvB2X3Lg5HqpFmn1kybDiIfEQmKzTb5apr51Rb+T3KdmMiqa70T+bhGnyv7bQ6WMj2QMHpGMmlrUYQ== +entities@~3.0.1: + version "3.0.1" + resolved "https://registry.yarnpkg.com/entities/-/entities-3.0.1.tgz#2b887ca62585e96db3903482d336c1006c3001d4" + integrity sha512-WiyBqoomrwMdFG1e0kqvASYfnlb0lp8M5o5Fw2OFq1hNZxxcNk8Ik0Xm7LxzBhuidnZB/UtBqVCgUz3kBOP51Q== + env-paths@^2.2.0: version "2.2.1" resolved "https://registry.yarnpkg.com/env-paths/-/env-paths-2.2.1.tgz#420399d416ce1fbe9bc0a07c62fa68d67fd0f8f2" @@ -2537,6 +2647,58 @@ error-ex@^1.3.1: dependencies: is-arrayish "^0.2.1" +es-abstract@^1.17.5, es-abstract@^1.22.1, es-abstract@^1.22.3, es-abstract@^1.23.0: + version "1.23.3" + resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.23.3.tgz#8f0c5a35cd215312573c5a27c87dfd6c881a0aa0" + integrity sha512-e+HfNH61Bj1X9/jLc5v1owaLYuHdeHHSQlkhCBiTK8rBvKaULl/beGMxwrMXjpYrv4pz22BlY570vVePA2ho4A== + dependencies: + array-buffer-byte-length "^1.0.1" + arraybuffer.prototype.slice "^1.0.3" + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + data-view-buffer "^1.0.1" + data-view-byte-length "^1.0.1" + data-view-byte-offset "^1.0.0" + es-define-property "^1.0.0" + es-errors "^1.3.0" + es-object-atoms "^1.0.0" + es-set-tostringtag "^2.0.3" + es-to-primitive "^1.2.1" + function.prototype.name "^1.1.6" + get-intrinsic "^1.2.4" + get-symbol-description "^1.0.2" + globalthis "^1.0.3" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + has-proto "^1.0.3" + has-symbols "^1.0.3" + hasown "^2.0.2" + internal-slot "^1.0.7" + is-array-buffer "^3.0.4" + is-callable "^1.2.7" + is-data-view "^1.0.1" + is-negative-zero "^2.0.3" + is-regex "^1.1.4" + is-shared-array-buffer "^1.0.3" + is-string "^1.0.7" + is-typed-array "^1.1.13" + is-weakref "^1.0.2" + object-inspect "^1.13.1" + object-keys "^1.1.1" + object.assign "^4.1.5" + regexp.prototype.flags "^1.5.2" + safe-array-concat "^1.1.2" + safe-regex-test "^1.0.3" + string.prototype.trim "^1.2.9" + string.prototype.trimend "^1.0.8" + string.prototype.trimstart "^1.0.8" + typed-array-buffer "^1.0.2" + typed-array-byte-length "^1.0.1" + typed-array-byte-offset "^1.0.2" + typed-array-length "^1.0.6" + unbox-primitive "^1.0.2" + which-typed-array "^1.1.15" + es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: version "1.22.1" resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.22.1.tgz#8b4e5fc5cefd7f1660f0f8e1a52900dfbc9d9ccc" @@ -2582,6 +2744,25 @@ es-abstract@^1.19.0, es-abstract@^1.20.4, es-abstract@^1.21.2: unbox-primitive "^1.0.2" which-typed-array "^1.1.10" +es-define-property@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-define-property/-/es-define-property-1.0.0.tgz#c7faefbdff8b2696cf5f46921edfb77cc4ba3845" + integrity sha512-jxayLKShrEqqzJ0eumQbVhTYQM27CfT1T35+gCgDFoL82JLsXqTJ76zv6A0YLOgEnLUMvLzsDsGIrl8NFpT2gQ== + dependencies: + get-intrinsic "^1.2.4" + +es-errors@^1.2.1, es-errors@^1.3.0: + version "1.3.0" + resolved "https://registry.yarnpkg.com/es-errors/-/es-errors-1.3.0.tgz#05f75a25dab98e4fb1dcd5e1472c0546d5057c8f" + integrity sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw== + +es-object-atoms@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/es-object-atoms/-/es-object-atoms-1.0.0.tgz#ddb55cd47ac2e240701260bc2a8e31ecb643d941" + integrity sha512-MZ4iQ6JwHOBQjahnjwaC1ZtIBH+2ohjamzAO3oaHcXYup7qxjF2fixyH+Q71voWHeOkI2q/TnJao/KfXYIZWbw== + dependencies: + es-errors "^1.3.0" + es-set-tostringtag@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.1.tgz#338d502f6f674301d710b80c8592de8a15f09cd8" @@ -2591,6 +2772,15 @@ es-set-tostringtag@^2.0.1: has "^1.0.3" has-tostringtag "^1.0.0" +es-set-tostringtag@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/es-set-tostringtag/-/es-set-tostringtag-2.0.3.tgz#8bb60f0a440c2e4281962428438d58545af39777" + integrity sha512-3T8uNMC3OQTHkFUsFq8r/BwAXLHvU/9O9mE0fBc/MY5iq/8H7ncvO947LmYA6ldWw9Uh8Yhf25zu6n7nML5QWQ== + dependencies: + get-intrinsic "^1.2.4" + has-tostringtag "^1.0.2" + hasown "^2.0.1" + es-shim-unscopables@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/es-shim-unscopables/-/es-shim-unscopables-1.0.0.tgz#702e632193201e3edf8713635d083d378e510241" @@ -3059,6 +3249,11 @@ function-bind@^1.1.1: resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d" integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A== +function-bind@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.2.tgz#2c02d864d97f3ea6c8830c464cbd11ab6eab7a1c" + integrity sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA== + function.prototype.name@^1.1.5: version "1.1.5" resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.5.tgz#cce0505fe1ffb80503e6f9e46cc64e46a12a9621" @@ -3069,6 +3264,16 @@ function.prototype.name@^1.1.5: es-abstract "^1.19.0" functions-have-names "^1.2.2" +function.prototype.name@^1.1.6: + version "1.1.6" + resolved "https://registry.yarnpkg.com/function.prototype.name/-/function.prototype.name-1.1.6.tgz#cdf315b7d90ee77a4c6ee216c3c3362da07533fd" + integrity sha512-Z5kx79swU5P27WEayXM1tBi5Ze/lbIyiNgU3qyXUOf9b2rgXYyF9Dy9Cx+IQv/Lc8WCG6L82zwUPpSS9hGehIg== + dependencies: + call-bind "^1.0.2" + define-properties "^1.2.0" + es-abstract "^1.22.1" + functions-have-names "^1.2.3" + functions-have-names@^1.2.2, functions-have-names@^1.2.3: version "1.2.3" resolved "https://registry.yarnpkg.com/functions-have-names/-/functions-have-names-1.2.3.tgz#0404fe4ee2ba2f607f0e0ec3c80bae994133b834" @@ -3108,6 +3313,17 @@ get-intrinsic@^1.0.2, get-intrinsic@^1.1.1, get-intrinsic@^1.1.3, get-intrinsic@ has-proto "^1.0.1" has-symbols "^1.0.3" +get-intrinsic@^1.2.3, get-intrinsic@^1.2.4: + version "1.2.4" + resolved "https://registry.yarnpkg.com/get-intrinsic/-/get-intrinsic-1.2.4.tgz#e385f5a4b5227d449c3eabbad05494ef0abbeadd" + integrity sha512-5uYhsJH8VJBTv7oslg4BznJYhDoRI6waYCxMmCdnTrcCrHA/fCFKoTFz2JKKE0HdDFUF7/oQuhzumXJK7paBRQ== + dependencies: + es-errors "^1.3.0" + function-bind "^1.1.2" + has-proto "^1.0.1" + has-symbols "^1.0.3" + hasown "^2.0.0" + get-package-type@^0.1.0: version "0.1.0" resolved "https://registry.yarnpkg.com/get-package-type/-/get-package-type-0.1.0.tgz#8de2d803cff44df3bc6c456e6668b36c3926e11a" @@ -3141,6 +3357,15 @@ get-symbol-description@^1.0.0: call-bind "^1.0.2" get-intrinsic "^1.1.1" +get-symbol-description@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/get-symbol-description/-/get-symbol-description-1.0.2.tgz#533744d5aa20aca4e079c8e5daf7fd44202821f5" + integrity sha512-g0QYk1dZBxGwk+Ngc+ltRH2IBp2f7zBkBMBJZCDerh6EhlhSR6+9irMCuT/09zD6qkarHUSn529sK/yL4S27mg== + dependencies: + call-bind "^1.0.5" + es-errors "^1.3.0" + get-intrinsic "^1.2.4" + get-tsconfig@^4.5.0: version "4.6.2" resolved "https://registry.yarnpkg.com/get-tsconfig/-/get-tsconfig-4.6.2.tgz#831879a5e6c2aa24fe79b60340e2233a1e0f472e" @@ -3370,11 +3595,23 @@ has-property-descriptors@^1.0.0: dependencies: get-intrinsic "^1.1.1" +has-property-descriptors@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-property-descriptors/-/has-property-descriptors-1.0.2.tgz#963ed7d071dc7bf5f084c5bfbe0d1b6222586854" + integrity sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg== + dependencies: + es-define-property "^1.0.0" + has-proto@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.1.tgz#1885c1305538958aff469fef37937c22795408e0" integrity sha512-7qE+iP+O+bgF9clE5+UoBFzE65mlBiVj3tKCrlNQ0Ogwm0BjpT/gK4SlLYDMybDh5I3TCTKnPPa0oMG7JDYrhg== +has-proto@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/has-proto/-/has-proto-1.0.3.tgz#b31ddfe9b0e6e9914536a6ab286426d0214f77fd" + integrity sha512-SJ1amZAJUiZS+PhsVLf5tGydlaVB8EdFpaSO4gmiUKUOxk8qzn5AIy4ZeJUmh22znIdk/uMAUT2pl3FxzVUH+Q== + has-symbols@^1.0.2, has-symbols@^1.0.3: version "1.0.3" resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.3.tgz#bb7b2c4349251dce87b125f7bdf874aa7c8b39f8" @@ -3387,6 +3624,13 @@ has-tostringtag@^1.0.0: dependencies: has-symbols "^1.0.2" +has-tostringtag@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/has-tostringtag/-/has-tostringtag-1.0.2.tgz#2cdc42d40bef2e5b4eeab7c01a73c54ce7ab5abc" + integrity sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw== + dependencies: + has-symbols "^1.0.3" + has-unicode@^2.0.1: version "2.0.1" resolved "https://registry.yarnpkg.com/has-unicode/-/has-unicode-2.0.1.tgz#e0e6fe6a28cf51138855e086d1691e771de2a8b9" @@ -3404,6 +3648,13 @@ has@^1.0.3: dependencies: function-bind "^1.1.1" +hasown@^2.0.0, hasown@^2.0.1, hasown@^2.0.2: + version "2.0.2" + resolved "https://registry.yarnpkg.com/hasown/-/hasown-2.0.2.tgz#003eaf91be7adc372e84ec59dc37252cedb80003" + integrity sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ== + dependencies: + function-bind "^1.1.2" + hosted-git-info@^2.1.4: version "2.8.9" resolved "https://registry.yarnpkg.com/hosted-git-info/-/hosted-git-info-2.8.9.tgz#dffc0bf9a21c02209090f2aa69429e1414daf3f9" @@ -3597,6 +3848,15 @@ internal-slot@^1.0.5: has "^1.0.3" side-channel "^1.0.4" +internal-slot@^1.0.7: + version "1.0.7" + resolved "https://registry.yarnpkg.com/internal-slot/-/internal-slot-1.0.7.tgz#c06dcca3ed874249881007b0a5523b172a190802" + integrity sha512-NGnrKwXzSms2qUUih/ILZ5JBqNTSa1+ZmP6flaIp6KmSElgE9qdndzS3cqjrDovwFdmwsGsLdeFgB6suw+1e9g== + dependencies: + es-errors "^1.3.0" + hasown "^2.0.0" + side-channel "^1.0.4" + interpret@^1.0.0: version "1.4.0" resolved "https://registry.yarnpkg.com/interpret/-/interpret-1.4.0.tgz#665ab8bc4da27a774a40584e812e3e0fa45b1a1e" @@ -3616,6 +3876,14 @@ is-array-buffer@^3.0.1, is-array-buffer@^3.0.2: get-intrinsic "^1.2.0" is-typed-array "^1.1.10" +is-array-buffer@^3.0.4: + version "3.0.4" + resolved "https://registry.yarnpkg.com/is-array-buffer/-/is-array-buffer-3.0.4.tgz#7a1f92b3d61edd2bc65d24f130530ea93d7fae98" + integrity sha512-wcjaerHw0ydZwfhiKbXJWLDY8A7yV7KhjQOpb83hGgGfId/aQa4TOvwyzn2PuswW2gPCYEL/nEAiSVpdOj1lXw== + dependencies: + call-bind "^1.0.2" + get-intrinsic "^1.2.1" + is-arrayish@^0.2.1: version "0.2.1" resolved "https://registry.yarnpkg.com/is-arrayish/-/is-arrayish-0.2.1.tgz#77c99840527aa8ecb1a8ba697b80645a7a926a9d" @@ -3660,6 +3928,13 @@ is-core-module@^2.11.0, is-core-module@^2.12.1, is-core-module@^2.13.0, is-core- dependencies: has "^1.0.3" +is-data-view@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/is-data-view/-/is-data-view-1.0.1.tgz#4b4d3a511b70f3dc26d42c03ca9ca515d847759f" + integrity sha512-AHkaJrsUVW6wq6JS8y3JnM/GJF/9cf+k20+iDzlSaJrinEo5+7vRiteOSwBhHRiAyQATN1AmY4hwzxJKPmYf+w== + dependencies: + is-typed-array "^1.1.13" + is-date-object@^1.0.1: version "1.0.5" resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.5.tgz#0841d5536e724c25597bf6ea62e1bd38298df31f" @@ -3712,6 +3987,11 @@ is-negative-zero@^2.0.2: resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.2.tgz#7bf6f03a28003b8b3965de3ac26f664d765f3150" integrity sha512-dqJvarLawXsFbNDeJW7zAz8ItJ9cd28YufuuFzh0G8pNHjJMnY08Dv7sYX2uF5UpQOwieAeOExEYAWWfu7ZZUA== +is-negative-zero@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/is-negative-zero/-/is-negative-zero-2.0.3.tgz#ced903a027aca6381b777a5743069d7376a49747" + integrity sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw== + is-npm@^6.0.0: version "6.0.0" resolved "https://registry.yarnpkg.com/is-npm/-/is-npm-6.0.0.tgz#b59e75e8915543ca5d881ecff864077cba095261" @@ -3769,6 +4049,13 @@ is-shared-array-buffer@^1.0.2: dependencies: call-bind "^1.0.2" +is-shared-array-buffer@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/is-shared-array-buffer/-/is-shared-array-buffer-1.0.3.tgz#1237f1cba059cdb62431d378dcc37d9680181688" + integrity sha512-nA2hv5XIhLR3uVzDDfCIknerhx8XUKnstuOERPNNIinXG7v9u+ohXF67vxm4TPTEPU6lm61ZkwP3c9PCB97rhg== + dependencies: + call-bind "^1.0.7" + is-stream@^2.0.0: version "2.0.1" resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.1.tgz#fac1e3d53b97ad5a9d0ae9cef2389f5810a5c077" @@ -3802,6 +4089,13 @@ is-typed-array@^1.1.10, is-typed-array@^1.1.9: dependencies: which-typed-array "^1.1.11" +is-typed-array@^1.1.13: + version "1.1.13" + resolved "https://registry.yarnpkg.com/is-typed-array/-/is-typed-array-1.1.13.tgz#d6c5ca56df62334959322d7d7dd1cca50debe229" + integrity sha512-uZ25/bUAlUY5fR4OKT4rZQEBrzQWYV9ZJYGGsUmEJ6thodVJ1HX64ePQ6Z0qPWP+m+Uq6e9UugrE38jeYsDSMw== + dependencies: + which-typed-array "^1.1.14" + is-typedarray@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a" @@ -4363,22 +4657,22 @@ jsii-rosetta@^1.86.1: workerpool "^6.4.0" yargs "^16.2.0" -jsii-rosetta@~5.2.0: - version "5.2.11" - resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-5.2.11.tgz#7b8ccb229332674c3a4fc570850fb903c6fec62c" - integrity sha512-MtzCWfXX835kUc6sNnncXzpc5g88MdS+paWj3n97DnYnJQCl3OMxytkG1jjICJE0oxlrbvC44OiJb28+0jEmAg== +jsii-rosetta@~5.4.0: + version "5.4.21" + resolved "https://registry.yarnpkg.com/jsii-rosetta/-/jsii-rosetta-5.4.21.tgz#4073340067b13f3198d7bd08e49b2976626ebbb0" + integrity sha512-g7E935trSNQ8rbs+VYQiDBbeoaqS0sO7HuJ+bz27x7pvKBri5Uk9w+aLtmFI8UhHX7JgZ6mUxruY0zRW4AqCyQ== dependencies: - "@jsii/check-node" "1.93.0" - "@jsii/spec" "^1.93.0" + "@jsii/check-node" "1.98.0" + "@jsii/spec" "^1.98.0" "@xmldom/xmldom" "^0.8.10" chalk "^4" - commonmark "^0.30.0" + commonmark "^0.31.0" fast-glob "^3.3.2" - jsii "~5.2.5" - semver "^7.5.4" + jsii "~5.4.0" + semver "^7.6.2" semver-intersect "^1.5.0" stream-json "^1.8.0" - typescript "~5.2.2" + typescript "~5.4" workerpool "^6.5.1" yargs "^17.7.2" @@ -4401,23 +4695,23 @@ jsii@1.86.1: typescript "~3.9.10" yargs "^16.2.0" -jsii@~5.2.0, jsii@~5.2.5: - version "5.2.44" - resolved "https://registry.yarnpkg.com/jsii/-/jsii-5.2.44.tgz#7a768412f1a28f5f1ff3e92ab5f5b7e7430c3ae1" - integrity sha512-Z7sTqYzQ5yoJU/ie+svjqSzrOF5rl4pW/bojvCb/7MfJ+SaGqhMUQMxQGTfqmSvauME8JoVYqwMH89x6qreJ8A== +jsii@~5.4.0: + version "5.4.20" + resolved "https://registry.yarnpkg.com/jsii/-/jsii-5.4.20.tgz#d348ff3556d36a209a76eccc96e1ce2258192e47" + integrity sha512-kZoopDv3Gy6wKOt6aYzPhMvEErOAnQXoeUjd3sGAODey/mUVJRvXoScYVvmmtVgKqt+120OFPhVRWzgefwio+g== dependencies: - "@jsii/check-node" "1.93.0" - "@jsii/spec" "^1.93.0" + "@jsii/check-node" "1.98.0" + "@jsii/spec" "^1.98.0" case "^1.6.3" chalk "^4" downlevel-dts "^0.11.0" fast-deep-equal "^3.1.3" log4js "^6.9.1" - semver "^7.5.4" + semver "^7.6.2" semver-intersect "^1.5.0" sort-json "^2.0.1" - spdx-license-list "^6.8.0" - typescript "~5.2" + spdx-license-list "^6.9.0" + typescript "~5.4" yargs "^17.7.2" json-buffer@3.0.1: @@ -4836,7 +5130,7 @@ minimist-options@4.1.0: is-plain-obj "^1.1.0" kind-of "^6.0.3" -minimist@>=1.2.2, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6: +minimist@>=1.2.2, minimist@^1.2.0, minimist@^1.2.3, minimist@^1.2.5, minimist@^1.2.6, minimist@~1.2.5: version "1.2.8" resolved "https://registry.yarnpkg.com/minimist/-/minimist-1.2.8.tgz#c1a464e7693302e082a075cee0c057741ac4772c" integrity sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA== @@ -5156,6 +5450,11 @@ object-inspect@^1.12.3, object-inspect@^1.9.0: resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.12.3.tgz#ba62dffd67ee256c8c086dfae69e016cd1f198b9" integrity sha512-geUvdk7c+eizMNUDkRpW1wJwgfOiOeHbxBR/hLXK1aT6zmVSO0jsQcs7fj6MGw89jC/cjGfLcNOrtMYtGqm81g== +object-inspect@^1.13.1: + version "1.13.1" + resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.13.1.tgz#b96c6109324ccfef6b12216a956ca4dc2ff94bc2" + integrity sha512-5qoj1RUiKOMsCCNLV1CBiPYE10sziTsnmNxkAI/rZhiD63CF7IqdFGC/XzjWjpSgLf0LxXX3bDFIh0E18f6UhQ== + object-keys@^1.1.1: version "1.1.1" resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e" @@ -5171,6 +5470,16 @@ object.assign@^4.1.4: has-symbols "^1.0.3" object-keys "^1.1.1" +object.assign@^4.1.5: + version "4.1.5" + resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.5.tgz#3a833f9ab7fdb80fc9e8d2300c803d216d8fdbb0" + integrity sha512-byy+U7gp+FVwmyzKPYhW2h5l3crpmGsxl7X2s8y43IgxvG4g3QZ6CffDtsNQy1WsmZpQbO+ybo0AlW7TY6DcBQ== + dependencies: + call-bind "^1.0.5" + define-properties "^1.2.1" + has-symbols "^1.0.3" + object-keys "^1.1.1" + object.fromentries@^2.0.6: version "2.0.6" resolved "https://registry.yarnpkg.com/object.fromentries/-/object.fromentries-2.0.6.tgz#cdb04da08c539cffa912dcd368b886e0904bfa73" @@ -5469,6 +5778,11 @@ pkg-dir@^4.2.0: dependencies: find-up "^4.0.0" +possible-typed-array-names@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/possible-typed-array-names/-/possible-typed-array-names-1.0.0.tgz#89bb63c6fada2c3e90adc4a647beeeb39cc7bf8f" + integrity sha512-d7Uw+eZoloe0EHDIYoe+bQ5WXnGMOpmiZFTuMWCwpjzzkL2nTjcKiAk4hh8TjnGye2TwWOk3UXucZ+3rbmBa8Q== + prelude-ls@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/prelude-ls/-/prelude-ls-1.2.1.tgz#debc6489d7a6e6b0e7611888cec880337d316396" @@ -5713,6 +6027,16 @@ regexp.prototype.flags@^1.5.0: define-properties "^1.2.0" functions-have-names "^1.2.3" +regexp.prototype.flags@^1.5.2: + version "1.5.2" + resolved "https://registry.yarnpkg.com/regexp.prototype.flags/-/regexp.prototype.flags-1.5.2.tgz#138f644a3350f981a858c44f6bb1a61ff59be334" + integrity sha512-NcDiDkTLuPR+++OCKB0nWafEmhg/Da8aUPLPMQbK+bxKKCm1/S5he+AqYa4PlMCVBalb4/yxIRub6qkEx5yJbw== + dependencies: + call-bind "^1.0.6" + define-properties "^1.2.1" + es-errors "^1.3.0" + set-function-name "^2.0.1" + registry-auth-token@^5.0.1: version "5.0.2" resolved "https://registry.yarnpkg.com/registry-auth-token/-/registry-auth-token-5.0.2.tgz#8b026cc507c8552ebbe06724136267e63302f756" @@ -5861,6 +6185,16 @@ safe-array-concat@^1.0.0: has-symbols "^1.0.3" isarray "^2.0.5" +safe-array-concat@^1.1.2: + version "1.1.2" + resolved "https://registry.yarnpkg.com/safe-array-concat/-/safe-array-concat-1.1.2.tgz#81d77ee0c4e8b863635227c721278dd524c20edb" + integrity sha512-vj6RsCsWBCf19jIeHEfkRMw8DPiBb+DMXklQ/1SGDHOMlHdPUkZXFQ2YdplS23zESTijAcurb1aSgJA3AgMu1Q== + dependencies: + call-bind "^1.0.7" + get-intrinsic "^1.2.4" + has-symbols "^1.0.3" + isarray "^2.0.5" + safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" @@ -5885,6 +6219,15 @@ safe-regex-test@^1.0.0: get-intrinsic "^1.1.3" is-regex "^1.1.4" +safe-regex-test@^1.0.3: + version "1.0.3" + resolved "https://registry.yarnpkg.com/safe-regex-test/-/safe-regex-test-1.0.3.tgz#a5b4c0f06e0ab50ea2c395c14d8371232924c377" + integrity sha512-CdASjNJPvRa7roO6Ra/gLYBTzYzzPyyBXxIMdGW3USQLyjWEls2RgW5UBTXaQVp+OrpeCK3bLem8smtmheoRuw== + dependencies: + call-bind "^1.0.6" + es-errors "^1.3.0" + is-regex "^1.1.4" + safe-stable-stringify@^2.3.1: version "2.4.3" resolved "https://registry.yarnpkg.com/safe-stable-stringify/-/safe-stable-stringify-2.4.3.tgz#138c84b6f6edb3db5f8ef3ef7115b8f55ccbf886" @@ -5938,11 +6281,38 @@ semver@^7.0.0, semver@^7.1.1, semver@^7.3.2, semver@^7.3.4, semver@^7.3.5, semve dependencies: lru-cache "^6.0.0" +semver@^7.6.2: + version "7.6.2" + resolved "https://registry.yarnpkg.com/semver/-/semver-7.6.2.tgz#1e3b34759f896e8f14d6134732ce798aeb0c6e13" + integrity sha512-FNAIBWCx9qcRhoHcgcJ0gvU7SN1lYU2ZXuSfl04bSC5OpvDHFyJCjdNHomPXxjQlCBU67YW64PzY7/VIEH7F2w== + set-blocking@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/set-blocking/-/set-blocking-2.0.0.tgz#045f9782d011ae9a6803ddd382b24392b3d890f7" integrity sha512-KiKBS8AnWGEyLzofFfmvKwpdPzqiy16LvQfK3yv/fVH7Bj13/wl3JSR1J+rfgRE9q7xUJK4qvgS8raSOeLUehw== +set-function-length@^1.2.1: + version "1.2.2" + resolved "https://registry.yarnpkg.com/set-function-length/-/set-function-length-1.2.2.tgz#aac72314198eaed975cf77b2c3b6b880695e5449" + integrity sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + function-bind "^1.1.2" + get-intrinsic "^1.2.4" + gopd "^1.0.1" + has-property-descriptors "^1.0.2" + +set-function-name@^2.0.1: + version "2.0.2" + resolved "https://registry.yarnpkg.com/set-function-name/-/set-function-name-2.0.2.tgz#16a705c5a0dc2f5e638ca96d8a8cd4e1c2b90985" + integrity sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ== + dependencies: + define-data-property "^1.1.4" + es-errors "^1.3.0" + functions-have-names "^1.2.3" + has-property-descriptors "^1.0.2" + shebang-command@^2.0.0: version "2.0.0" resolved "https://registry.yarnpkg.com/shebang-command/-/shebang-command-2.0.0.tgz#ccd0af4f8835fbdc265b82461aaf0c36663f34ea" @@ -6108,7 +6478,7 @@ spdx-license-list@^6.6.0: resolved "https://registry.yarnpkg.com/spdx-license-list/-/spdx-license-list-6.6.0.tgz#403e1807fd87ef2b4781677bc91896d23eaed4ea" integrity sha512-vLwdf9AWgdJQmG8cai2HKfkInFsliKaCCOwXmdVonClIhdURTX61KdDOoXC1qcQ7gDaZj+CUTcrMJeAdnCtrKA== -spdx-license-list@^6.8.0: +spdx-license-list@^6.9.0: version "6.9.0" resolved "https://registry.yarnpkg.com/spdx-license-list/-/spdx-license-list-6.9.0.tgz#5543abb3a15f985a12808f642a622d2721c372ad" integrity sha512-L2jl5vc2j6jxWcNCvcVj/BW9A8yGIG02Dw+IUw0ZxDM70f7Ylf5Hq39appV1BI9yxyWQRpq2TQ1qaXvf+yjkqA== @@ -6200,7 +6570,16 @@ string-length@^4.0.1: char-regex "^1.0.2" strip-ansi "^6.0.0" -"string-width-cjs@npm:string-width@^4.2.0", "string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: +"string-width-cjs@npm:string-width@^4.2.0": + version "4.2.3" + resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" + integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== + dependencies: + emoji-regex "^8.0.0" + is-fullwidth-code-point "^3.0.0" + strip-ansi "^6.0.1" + +"string-width@^1.0.2 || 2 || 3 || 4", string-width@^4.1.0, string-width@^4.2.0, string-width@^4.2.3: version "4.2.3" resolved "https://registry.yarnpkg.com/string-width/-/string-width-4.2.3.tgz#269c7117d27b05ad2e536830a8ec895ef9c6d010" integrity sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g== @@ -6223,6 +6602,14 @@ string.prototype.repeat@^0.2.0: resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-0.2.0.tgz#aba36de08dcee6a5a337d49b2ea1da1b28fc0ecf" integrity sha512-1BH+X+1hSthZFW+X+JaUkjkkUPwIlLEMJBLANN3hOob3RhEk5snLWNECDnYbgn/m5c5JV7Ersu1Yubaf+05cIA== +string.prototype.repeat@^1.0.0: + version "1.0.0" + resolved "https://registry.yarnpkg.com/string.prototype.repeat/-/string.prototype.repeat-1.0.0.tgz#e90872ee0308b29435aa26275f6e1b762daee01a" + integrity sha512-0u/TldDbKD8bFCQ/4f5+mNRrXwZ8hg2w7ZR8wa16e8z9XpePWl3eGEcUD0OXpEH/VJH/2G3gjUtR3ZOiBe2S/w== + dependencies: + define-properties "^1.1.3" + es-abstract "^1.17.5" + string.prototype.trim@^1.2.7: version "1.2.7" resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.7.tgz#a68352740859f6893f14ce3ef1bb3037f7a90533" @@ -6232,6 +6619,16 @@ string.prototype.trim@^1.2.7: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trim@^1.2.9: + version "1.2.9" + resolved "https://registry.yarnpkg.com/string.prototype.trim/-/string.prototype.trim-1.2.9.tgz#b6fa326d72d2c78b6df02f7759c73f8f6274faa4" + integrity sha512-klHuCNxiMZ8MlsOihJhJEBJAiMVqU3Z2nEXWfWnIqjN0gEFS9J9+IxKozWWtQGcgoa1WUZzLjKPTr4ZHNFTFxw== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-abstract "^1.23.0" + es-object-atoms "^1.0.0" + string.prototype.trimend@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.6.tgz#c4a27fa026d979d79c04f17397f250a462944533" @@ -6241,6 +6638,15 @@ string.prototype.trimend@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimend@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.8.tgz#3651b8513719e8a9f48de7f2f77640b26652b229" + integrity sha512-p73uL5VCHCO2BZZ6krwwQE3kCzM7NKmis8S//xEC6fQonchbum4eP6kR4DLEjQFO3Wnj3Fuo8NM0kOSjVdHjZQ== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string.prototype.trimstart@^1.0.6: version "1.0.6" resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.6.tgz#e90ab66aa8e4007d92ef591bbf3cd422c56bdcf4" @@ -6250,6 +6656,15 @@ string.prototype.trimstart@^1.0.6: define-properties "^1.1.4" es-abstract "^1.20.4" +string.prototype.trimstart@^1.0.8: + version "1.0.8" + resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.8.tgz#7ee834dda8c7c17eff3118472bb35bfedaa34dde" + integrity sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg== + dependencies: + call-bind "^1.0.7" + define-properties "^1.2.1" + es-object-atoms "^1.0.0" + string_decoder@^1.1.1: version "1.3.0" resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e" @@ -6269,7 +6684,14 @@ stringify-package@^1.0.1: resolved "https://registry.yarnpkg.com/stringify-package/-/stringify-package-1.0.1.tgz#e5aa3643e7f74d0f28628b72f3dad5cecfc3ba85" integrity sha512-sa4DUQsYciMP1xhKWGuFM04fB0LG/9DlluZoSVywUMRNvzid6XucHK0/90xGxRoHrAaROrcHK1aPKaijCtSrhg== -"strip-ansi-cjs@npm:strip-ansi@^6.0.1", strip-ansi@^6.0.0, strip-ansi@^6.0.1: +"strip-ansi-cjs@npm:strip-ansi@^6.0.1": + version "6.0.1" + resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" + integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== + dependencies: + ansi-regex "^5.0.1" + +strip-ansi@^6.0.0, strip-ansi@^6.0.1: version "6.0.1" resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-6.0.1.tgz#9e26c63d30f53443e9489495b2105d37b67a85d9" integrity sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A== @@ -6587,6 +7009,15 @@ typed-array-buffer@^1.0.0: get-intrinsic "^1.2.1" is-typed-array "^1.1.10" +typed-array-buffer@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-buffer/-/typed-array-buffer-1.0.2.tgz#1867c5d83b20fcb5ccf32649e5e2fc7424474ff3" + integrity sha512-gEymJYKZtKXzzBzM4jqa9w6Q1Jjm7x2d+sh19AdsD4wqnMPDYyvwpsIc2Q/835kHuo3BEQ7CjelGhfTsoBb2MQ== + dependencies: + call-bind "^1.0.7" + es-errors "^1.3.0" + is-typed-array "^1.1.13" + typed-array-byte-length@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.0.tgz#d787a24a995711611fb2b87a4052799517b230d0" @@ -6597,6 +7028,17 @@ typed-array-byte-length@^1.0.0: has-proto "^1.0.1" is-typed-array "^1.1.10" +typed-array-byte-length@^1.0.1: + version "1.0.1" + resolved "https://registry.yarnpkg.com/typed-array-byte-length/-/typed-array-byte-length-1.0.1.tgz#d92972d3cff99a3fa2e765a28fcdc0f1d89dec67" + integrity sha512-3iMJ9q0ao7WE9tWcaYKIptkNBuOIcZCCT0d4MRvuuH88fEoEH62IuQe0OtraD3ebQEoTRk8XCBoknUNc1Y67pw== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + typed-array-byte-offset@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.0.tgz#cbbe89b51fdef9cd6aaf07ad4707340abbc4ea0b" @@ -6608,6 +7050,18 @@ typed-array-byte-offset@^1.0.0: has-proto "^1.0.1" is-typed-array "^1.1.10" +typed-array-byte-offset@^1.0.2: + version "1.0.2" + resolved "https://registry.yarnpkg.com/typed-array-byte-offset/-/typed-array-byte-offset-1.0.2.tgz#f9ec1acb9259f395093e4567eb3c28a580d02063" + integrity sha512-Ous0vodHa56FviZucS2E63zkgtgrACj7omjwd/8lTEMEPFFyjfixMZ1ZXenpgCFBBt4EC1J2XsyVS2gkG0eTFA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + typed-array-length@^1.0.4: version "1.0.4" resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.4.tgz#89d83785e5c4098bec72e08b319651f0eac9c1bb" @@ -6617,6 +7071,18 @@ typed-array-length@^1.0.4: for-each "^0.3.3" is-typed-array "^1.1.9" +typed-array-length@^1.0.6: + version "1.0.6" + resolved "https://registry.yarnpkg.com/typed-array-length/-/typed-array-length-1.0.6.tgz#57155207c76e64a3457482dfdc1c9d1d3c4c73a3" + integrity sha512-/OxDN6OtAk5KBpGb28T+HZc2M+ADtvRxXrKKbUwtsLgdoxgX13hyy7ek6bFRl5+aBs2yZzB0c4CnQfAtVypW/g== + dependencies: + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-proto "^1.0.3" + is-typed-array "^1.1.13" + possible-typed-array-names "^1.0.0" + typedarray-to-buffer@^3.1.5: version "3.1.5" resolved "https://registry.yarnpkg.com/typedarray-to-buffer/-/typedarray-to-buffer-3.1.5.tgz#a97ee7a9ff42691b9f783ff1bc5112fe3fca9080" @@ -6644,10 +7110,10 @@ typescript@~3.9.10: resolved "https://registry.yarnpkg.com/typescript/-/typescript-3.9.10.tgz#70f3910ac7a51ed6bef79da7800690b19bf778b8" integrity sha512-w6fIxVE/H1PkLKcCPsFqKE7Kv7QUwhU8qQY2MueZXWx5cPZdwFupLgKK3vntcK98BtNHZtAF4LA/yl2a7k8R6Q== -typescript@~5.2, typescript@~5.2.2: - version "5.2.2" - resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.2.2.tgz#5ebb5e5a5b75f085f22bc3f8460fba308310fa78" - integrity sha512-mI4WrpHsbCIcwT9cF4FZvr80QUeKvsUsUvKDoR+X/7XHQH98xYD8YHZg7ANtz2GtZt/CBq2QJ0thkGJMHfqc1w== +typescript@~5.4: + version "5.4.5" + resolved "https://registry.yarnpkg.com/typescript/-/typescript-5.4.5.tgz#42ccef2c571fdbd0f6718b1d1f5e6e5ef006f611" + integrity sha512-vcI4UpRgg81oIRUFwR0WSIHKt11nJ7SAVlYNIu+QpqeyXP+gpQJy/Z4+F0aGxSE4MqwjyXvW/TzgkLAx2AGHwQ== uglify-js@^3.1.4: version "3.17.4" @@ -6733,7 +7199,7 @@ update-notifier@^6.0.2: semver-diff "^4.0.0" xdg-basedir "^5.1.0" -uri-js@^4.2.2: +uri-js@^4.2.2, uri-js@^4.4.1: version "4.4.1" resolved "https://registry.yarnpkg.com/uri-js/-/uri-js-4.4.1.tgz#9b1a52595225859e55f669d928f88c6c57f2a77e" integrity sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg== @@ -6833,6 +7299,17 @@ which-typed-array@^1.1.10, which-typed-array@^1.1.11: gopd "^1.0.1" has-tostringtag "^1.0.0" +which-typed-array@^1.1.14, which-typed-array@^1.1.15: + version "1.1.15" + resolved "https://registry.yarnpkg.com/which-typed-array/-/which-typed-array-1.1.15.tgz#264859e9b11a649b388bfaaf4f767df1f779b38d" + integrity sha512-oV0jmFtUky6CXfkqehVvBP/LSWJ2sy4vWMioiENyJLePrBO/yKyV9OyJySfAKosh+RYkIl5zJCNZ8/4JncrpdA== + dependencies: + available-typed-arrays "^1.0.7" + call-bind "^1.0.7" + for-each "^0.3.3" + gopd "^1.0.1" + has-tostringtag "^1.0.2" + which@^2.0.1, which@^2.0.2: version "2.0.2" resolved "https://registry.yarnpkg.com/which/-/which-2.0.2.tgz#7c6a8dd0a636a0327e10b59c9286eee93f3f51b1" @@ -6902,7 +7379,7 @@ workerpool@^6.5.1: resolved "https://registry.yarnpkg.com/workerpool/-/workerpool-6.5.1.tgz#060f73b39d0caf97c6db64da004cd01b4c099544" integrity sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA== -"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0", wrap-ansi@^7.0.0: +"wrap-ansi-cjs@npm:wrap-ansi@^7.0.0": version "7.0.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== @@ -6920,6 +7397,15 @@ wrap-ansi@^6.0.1: string-width "^4.1.0" strip-ansi "^6.0.0" +wrap-ansi@^7.0.0: + version "7.0.0" + resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-7.0.0.tgz#67e145cff510a6a6984bdf1152911d69d2eb9e43" + integrity sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q== + dependencies: + ansi-styles "^4.0.0" + string-width "^4.1.0" + strip-ansi "^6.0.0" + wrap-ansi@^8.1.0: version "8.1.0" resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-8.1.0.tgz#56dc22368ee570face1b49819975d9b9a5ead214" From 767b4d35136817328cd029cf3547fa82dbcc988b Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 7 Jun 2024 12:42:18 -0400 Subject: [PATCH 09/30] Fix build errors and modularizing --- src/builders.ts | 49 ++++++++++++++++++++++++------------------------- src/common.ts | 11 +++++++++++ 2 files changed, 35 insertions(+), 25 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 612a449..eb5358c 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import { ApiObject, ApiObjectProps, Yaml } from 'cdk8s'; import { Construct } from 'constructs'; -import { usingBuildParameter, usingResultsPath } from './common'; +import { invertBuildParameter, usingBuildParameter, usingResultsPath } from './common'; import { Pipeline, PipelineParam, @@ -61,7 +61,7 @@ const DefaultClusterRoleBindingProps = createRoleBindingProps( /** * Resolves the value through different means. */ -interface ValueResolver { +export interface IValueResolver { /** * Gets the string value for a parameter * @returns string The value. @@ -69,41 +69,41 @@ interface ValueResolver { get value(): string; } -export class ConstantStringValueResolver implements ValueResolver { +export class ConstantStringValueResolver implements IValueResolver { private val: string; constructor(val: string) { this.val = val; } - + public get value(): string { return this.val; } } -export class PipelineParameterValueResolver implements ValueResolver { +export class PipelineParameterValueResolver implements IValueResolver { private val: ParameterBuilder; constructor(param: ParameterBuilder) { this.val = param; } - + get value(): string { // TODO: Fix this... needs to return a representation that would be a link // to the actual parameter //return `$(params.${this.val.logicalID})` - return usingBuildParameter(this.val.logicalID!) + return usingBuildParameter(this.val.logicalID!); } } -export function constant(val: string) : ValueResolver { +export function constant(val: string) : IValueResolver { return new ConstantStringValueResolver(val); } -export function fromPipelineParam(param: ParameterBuilder): ValueResolver { +export function fromPipelineParam(param: ParameterBuilder): IValueResolver { return new PipelineParameterValueResolver(param); } @@ -196,14 +196,14 @@ export class ParameterBuilder { private _name: string; private _description?: string; private _type?: string; - private _value?: ValueResolver; + private _value?: IValueResolver; private _defaultValue?: string; - private _requiresPipelineParam: boolean; + private _requiresPipelineParam: string | undefined; constructor(id: string) { this._logicalID = id; this._name = id; - this._requiresPipelineParam = false; + this._requiresPipelineParam = undefined; } /** @@ -267,16 +267,15 @@ export class ParameterBuilder { * Sets the value for the parameter * @param val */ - public withValue(val: string | ValueResolver): ParameterBuilder { + public withValue(val: string | IValueResolver): ParameterBuilder { // If you are giving it a value here, then you do not // need the Pipeline parameter for this parameter. - this._requiresPipelineParam = false; + this._requiresPipelineParam = undefined; if (typeof(val) === 'string') { this._value = constant(val); - } - else { + } else { if (val instanceof PipelineParameterValueResolver) { - this._requiresPipelineParam = true; + this._requiresPipelineParam = invertBuildParameter(val.value); } this._value = val; } @@ -304,9 +303,10 @@ export class ParameterBuilder { } /** - * Returns true if this parameter expects input at the pipeline level. + * Returns the name of the input if the parameter expects one at the + * pipeline level, undefined otherwise. */ - public get requiresPipelineParameter(): boolean { + public get requiresPipelineParameter(): string | undefined { return this._requiresPipelineParam; } } @@ -819,7 +819,7 @@ export class TaskBuilder { taskParams.push({ name: p.logicalID, description: p.description, - default: p.defaultValue, + default: p.defaultValue || '', }); }); @@ -937,17 +937,16 @@ export class PipelineBuilder { pipelineParams.set(par.logicalID!, { name: par.logicalID, type: par.type, - default: par.defaultValue + default: par.defaultValue || '', }); }); // Then, check if any Tasks require a missing pipeline-level parameter this._tasks?.forEach((t) => { t.parameters?.forEach(p => { - if(p.requiresPipelineParameter) { - // TODO: find better way to retrieve ID of pipeline-level param referenced by task param? - const requiredPipelineParam = p.value!.substring(9, p.value!.length - 1); + if (p.requiresPipelineParameter) { + const requiredPipelineParam = p.requiresPipelineParameter; const pp = pipelineParams.has(requiredPipelineParam); - if(!pp) { + if (!pp) { throw new Error(`Parameter '${p.logicalID}' in Task '${t.name}' expects Pipeline value from parameter '${requiredPipelineParam}', which is not defined.`); } } diff --git a/src/common.ts b/src/common.ts index dd6d621..ef57701 100644 --- a/src/common.ts +++ b/src/common.ts @@ -47,6 +47,17 @@ export function usingBuildParameter(name: string): string { return `$(params.${name})`; } +/** + * Retrieves the parameter referenced by 'name' from the string outputted by + * usingBuildParameter(name). + * + * For example, if the input string is $(params.foo)`, the result will be `foo`. + * @param buildParam The reference string for the parameter. + */ +export function invertBuildParameter(buildParam: string): string { + return buildParam.substring(9, buildParam.length - 1); +} + /** * Builds the correct string for building a reference to the file in which the * result can be written during the execution of the Task. For example, if the From 1100e373bab5126c9cc7076df308c96ca365ce11 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 7 Jun 2024 12:43:34 -0400 Subject: [PATCH 10/30] Update builder tests and snapshots --- .../pipelinebuilder.test.ts.snap | 76 +++++++++++++++++++ test/pipelinebuilder.test.ts | 53 +++++++------ test/taskbuilder.test.ts | 18 +++-- 3 files changed, 117 insertions(+), 30 deletions(-) diff --git a/test/__snapshots__/pipelinebuilder.test.ts.snap b/test/__snapshots__/pipelinebuilder.test.ts.snap index 0b05c4e..92bfede 100644 --- a/test/__snapshots__/pipelinebuilder.test.ts.snap +++ b/test/__snapshots__/pipelinebuilder.test.ts.snap @@ -12,18 +12,22 @@ exports[`PipelineBuilderTest PipelineBuilderWithComplexTasks 1`] = ` "description": "This pipeline closes a repository, builds a Docker image, etc.", "params": [ { + "default": "", "name": "repo-url", "type": "string", }, { + "default": "", "name": "your-name", "type": "string", }, { + "default": "", "name": "your-color", "type": "string", }, { + "default": "", "name": "your-quest", "type": "string", }, @@ -126,6 +130,7 @@ exports[`PipelineBuilderTest PipelineBuilderWithDuplicateTasks 1`] = ` "description": "This pipeline closes a repository, builds a Docker image, etc.", "params": [ { + "default": "", "name": "repo-url", "type": "string", }, @@ -194,6 +199,7 @@ exports[`PipelineBuilderTest PipelineBuilderWithDuplicates 1`] = ` "description": "This pipeline closes a repository, builds a Docker image, etc.", "params": [ { + "default": "", "name": "repo-url", "type": "string", }, @@ -250,6 +256,75 @@ exports[`PipelineBuilderTest PipelineBuilderWithDuplicates 1`] = ` ] `; +exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "Task", + "metadata": { + "annotations": undefined, + "labels": undefined, + "name": "build-push", + }, + "spec": { + "description": undefined, + "params": [ + { + "default": "", + "description": "", + "name": "pathToDockerFile", + }, + { + "default": "", + "description": "", + "name": "pathToContext", + }, + ], + "results": [], + "steps": [], + "workspaces": [], + }, + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "Pipeline", + "metadata": { + "name": "pipeline-with-parameters", + }, + "spec": { + "description": undefined, + "params": [ + { + "default": "/some/where/or/other", + "name": "context", + "type": "string", + }, + ], + "tasks": [ + { + "name": "build-skaffold-web", + "params": [ + { + "name": "pathToDockerFile", + "value": "Dockerfile", + }, + { + "name": "pathToContext", + "value": "$(params.context)", + }, + ], + "taskRef": { + "name": "build-push", + }, + "workspaces": [], + }, + ], + "workspaces": [], + }, + }, +] +`; + exports[`PipelineBuilderTest PipelineBuilderWithStaticOverride 1`] = ` [ { @@ -321,6 +396,7 @@ exports[`PipelineBuilderTest PipelineRunBuilder 1`] = ` "description": "This pipeline closes a repository, builds a Docker image, etc.", "params": [ { + "default": "", "name": "repo-url", "type": "string", }, diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index c4827f7..ececfc0 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -9,7 +9,7 @@ import { TaskBuilder, WorkspaceBuilder, fromPipelineParam, - constant + constant, } from '../src'; class PipelineRunTest extends Chart { @@ -17,7 +17,7 @@ class PipelineRunTest extends Chart { super(scope, id, props); const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') @@ -45,7 +45,7 @@ class PipelineRunTestWithUndefinedWorkspaceError extends Chart { super(scope, id, props); const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') @@ -72,7 +72,7 @@ class PipelineRunTestWithUndefinedParamError extends Chart { super(scope, id, props); const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') @@ -100,7 +100,7 @@ class PipelineRunTestWithError extends Chart { super(scope, id, props); const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const myTask = new TaskBuilder(this, 'fetch-source') .withName('git-clone') @@ -128,13 +128,13 @@ class MySecondTestChart extends Chart { super(scope, id, props); const repoParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const nameParam = new ParameterBuilder('your-name') - .withDefaultValue('') + .withDefaultValue(''); const colorParam = new ParameterBuilder('your-color') - .withDefaultValue('') + .withDefaultValue(''); const questParam = new ParameterBuilder('your-quest') - .withDefaultValue('') + .withDefaultValue(''); const myTask = new TaskBuilder(this, 'git-clone') .withName('fetch-source') @@ -171,9 +171,9 @@ class MyTestChartWithDuplicateParams extends Chart { const myWorkspace = new WorkspaceBuilder('output') .withDescription('The files cloned by the task') .withName('shared-data'); - + const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const urlParam = new ParameterBuilder('url') .withValue(fromPipelineParam(pipelineParam)); @@ -206,10 +206,10 @@ class MyTestChartWithStaticOverride extends Chart { super(scope, id, props); const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') - + .withDefaultValue(''); + const urlParam = new ParameterBuilder('url') - .withValue(fromPipelineParam(pipelineParam)); + .withValue(fromPipelineParam(pipelineParam)); const myTask2 = new TaskBuilder(this, 'cat-readme') .withName('print-readme') @@ -237,7 +237,7 @@ class MyTestChartWithDuplicateTasks extends Chart { .withName('shared-data'); const pipelineParam = new ParameterBuilder('repo-url') - .withDefaultValue('') + .withDefaultValue(''); const urlParam = new ParameterBuilder('url') .withValue(fromPipelineParam(pipelineParam)); @@ -263,19 +263,19 @@ class PipelineLevelParamTest extends Chart { super(scope, id, props); const pipelineParam = new ParameterBuilder('context') - .withDefaultValue('/some/where/or/other') - + .withDefaultValue('/some/where/or/other'); + const taskParam = new ParameterBuilder('pathToDockerFile') - .withValue(constant('Dockerfile')) - + .withValue(constant('Dockerfile')); + const taskParam2 = new ParameterBuilder('pathToContext') - .withValue(fromPipelineParam(pipelineParam)) - + .withValue(fromPipelineParam(pipelineParam)); + const myTask = new TaskBuilder(this, 'build-skaffold-web') .withName('build-push') .withStringParam(taskParam) - .withStringParam(taskParam2) - + .withStringParam(taskParam2); + new PipelineBuilder(this, 'pipeline-with-parameters') .withStringParam(pipelineParam) .withTask(myTask) @@ -342,4 +342,11 @@ describe('PipelineBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + + test('PipelineBuilderWithParameters', () => { + const app = Testing.app(); + const chart = new PipelineLevelParamTest(app, 'test-chart'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); }); diff --git a/test/taskbuilder.test.ts b/test/taskbuilder.test.ts index 3ba2da9..b4eec97 100644 --- a/test/taskbuilder.test.ts +++ b/test/taskbuilder.test.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import { Chart, Testing } from 'cdk8s'; import { ChartProps } from 'cdk8s/lib/chart'; import { Construct } from 'constructs'; -import { usingBuildParameter, usingWorkspacePath, secretKeyRef, TaskBuilder, TaskStepBuilder, valueFrom, WorkspaceBuilder, ParameterBuilder } from '../src'; +import { usingBuildParameter, usingWorkspacePath, secretKeyRef, TaskBuilder, TaskStepBuilder, valueFrom, WorkspaceBuilder, ParameterBuilder, fromPipelineParam } from '../src'; /** * Using "ansible-runner" as the reference task that I want this test builder to @@ -17,9 +17,11 @@ class TestBasicTaskBuild extends Chart { const runnerDir = new WorkspaceBuilder('runner-dir') .withDescription('The Ansibler runner directory'); + const pipelineParam = new ParameterBuilder('project-dir'); + const projectDirName = new ParameterBuilder('project-dir') .ofType('string') - .withPiplineParameter('project-dir'); + .withValue(fromPipelineParam(pipelineParam)); new TaskBuilder(this, 'my-task') .withName('ansible-runner') @@ -79,9 +81,11 @@ class TestBasicTaskBuildFromObject extends Chart { const runnerDir = new WorkspaceBuilder('runner-dir') .withDescription('The Ansibler runner directory'); + const pipelineParam = new ParameterBuilder('project-dir'); + const projectDirName = new ParameterBuilder('project-dir') .ofType('string') - .withPiplineParameter('project-dir'); + .withValue(fromPipelineParam(pipelineParam)); new TaskBuilder(this, 'my-task') .withName('ansible-runner') @@ -159,16 +163,16 @@ class TestPullRequestTaskBuild extends Chart { .withName('pull-request') .withDescription('This Task allows a user to interact with an SCM (source control management)\nsystem through an abstracted interface\n\nThis Task works with both public SCM instances and self-hosted/enterprise GitHub/GitLab\ninstances. In download mode, this Task will look at the state of an existing pull\nrequest and populate the pr workspace with the state of the pull request, including the\n.MANIFEST file. In upload mode, this Task will look at the contents of the pr workspace\n and compare it to the .MANIFEST file (if it exists).') .withStringParam(new ParameterBuilder('mode') - .withPiplineParameter('mode') + .withValue(fromPipelineParam(new ParameterBuilder('mode'))) .withDescription('If "download", the state of the pull request at `url` will be fetched. If "upload" then the pull request at `url` will be updated.')) .withStringParam(new ParameterBuilder('url') - .withPiplineParameter('repo-url') + .withValue(fromPipelineParam(new ParameterBuilder('repo-url'))) .withDescription('The URL of the Pull Reuqest, e.g. `https://github.com/bobcatfish/catservice/pull/16`')) .withStringParam(new ParameterBuilder('provider') - .withPiplineParameter('provider') + .withValue(fromPipelineParam(new ParameterBuilder('provider'))) .withDescription('The type of SCM system, currently `github` or `gitlab`')) .withStringParam(new ParameterBuilder('secret-key-ref') - .withPiplineParameter('secret-key-ref') + .withValue(fromPipelineParam(new ParameterBuilder('secret-key-ref'))) .withDescription('The name of an opaque secret containing a key called "token" with a base64 encoded SCM token')) .withStringParam(new ParameterBuilder('insecure-skip-tls-verify') .withDefaultValue('false') From 59f5cc4b296fdd86371cd99df06861c60a9bfa1d Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 7 Jun 2024 14:09:58 -0400 Subject: [PATCH 11/30] API markdown updates --- API.md | 194 +++++++++++++++++++++++++++++++++++++++++++++------------ 1 file changed, 154 insertions(+), 40 deletions(-) diff --git a/API.md b/API.md index 6765f99..dbe45ef 100644 --- a/API.md +++ b/API.md @@ -2652,6 +2652,53 @@ public readonly logicalID: string; ## Classes +### ConstantStringValueResolver + +- *Implements:* IValueResolver + +#### Initializers + +```typescript +import { ConstantStringValueResolver } from 'cdk8s-pipelines' + +new ConstantStringValueResolver(val: string) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| val | string | *No description.* | + +--- + +##### `val`Required + +- *Type:* string + +--- + + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| value | string | Gets the string value for a parameter. | + +--- + +##### `value`Required + +```typescript +public readonly value: string; +``` + +- *Type:* string + +Gets the string value for a parameter. + +--- + + ### ParameterBuilder Builds the parameters for use by Tasks and Pipelines. @@ -2685,8 +2732,7 @@ new ParameterBuilder(id: string) | ofType | Sets the type of the parameter. | | withDefaultValue | Sets the default value for the parameter. | | withDescription | Sets the description of the parameter. | -| withName | Sets the name of the parameter. | -| withPiplineParameter | Sets the default value for the parameter. | +| withName | *No description.* | | withValue | Sets the value for the parameter. | --- @@ -2733,51 +2779,29 @@ Sets the description of the parameter. --- -##### `withName` +##### ~~`withName`~~ ```typescript public withName(name: string): ParameterBuilder ``` -Sets the name of the parameter. - ###### `name`Required - *Type:* string --- -##### `withPiplineParameter` - -```typescript -public withPiplineParameter(pipelineParamName: string, defaultValue?: string): ParameterBuilder -``` - -Sets the default value for the parameter. - -###### `pipelineParamName`Required - -- *Type:* string - ---- - -###### `defaultValue`Optional - -- *Type:* string - ---- - ##### `withValue` ```typescript -public withValue(val: string): ParameterBuilder +public withValue(val: string | IValueResolver): ParameterBuilder ``` Sets the value for the parameter. ###### `val`Required -- *Type:* string +- *Type:* string | IValueResolver --- @@ -2787,10 +2811,10 @@ Sets the value for the parameter. | **Name** | **Type** | **Description** | | --- | --- | --- | | description | string | Gets the description of the parameter. | -| requiresPipelineParameter | boolean | Returns true if this parameter expects input at the pipeline level. | | defaultValue | string | *No description.* | | logicalID | string | Gets the logicalID for the `ParameterBuilder`, which is used by the underlying construct. | | name | string | Gets the name of the parameter. | +| requiresPipelineParameter | string | Returns the name of the input if the parameter expects one at the pipeline level, undefined otherwise. | | type | string | Gets the type of the parameter. | | value | string | Gets the value of the parameter. | @@ -2808,18 +2832,6 @@ Gets the description of the parameter. --- -##### `requiresPipelineParameter`Required - -```typescript -public readonly requiresPipelineParameter: boolean; -``` - -- *Type:* boolean - -Returns true if this parameter expects input at the pipeline level. - ---- - ##### `defaultValue`Optional ```typescript @@ -2854,6 +2866,18 @@ Gets the name of the parameter. --- +##### `requiresPipelineParameter`Optional + +```typescript +public readonly requiresPipelineParameter: string; +``` + +- *Type:* string + +Returns the name of the input if the parameter expects one at the pipeline level, undefined otherwise. + +--- + ##### `type`Optional ```typescript @@ -2915,6 +2939,7 @@ new PipelineBuilder(scope: Construct, id: string) | buildPipeline | Builds the actual [Pipeline](https://tekton.dev/docs/getting-started/pipelines/) from the settings configured using the fluid syntax. | | withDescription | Provides the name for the pipeline task and will be rendered as the `name` property. | | withName | Provides the name for the pipeline task and will be rendered as the `name` property. | +| withStringParam | Add parameter of type string to the Pipeline. | | withTask | *No description.* | --- @@ -2961,6 +2986,20 @@ Provides the name for the pipeline task and will be rendered as the `name` prope --- +##### `withStringParam` + +```typescript +public withStringParam(param: ParameterBuilder): PipelineBuilder +``` + +Add parameter of type string to the Pipeline. + +###### `param`Required + +- *Type:* ParameterBuilder + +--- + ##### `withTask` ```typescript @@ -3031,6 +3070,53 @@ a local variable before the loop and reference that instead. --- +### PipelineParameterValueResolver + +- *Implements:* IValueResolver + +#### Initializers + +```typescript +import { PipelineParameterValueResolver } from 'cdk8s-pipelines' + +new PipelineParameterValueResolver(param: ParameterBuilder) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| param | ParameterBuilder | *No description.* | + +--- + +##### `param`Required + +- *Type:* ParameterBuilder + +--- + + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| value | string | Gets the string value for a parameter. | + +--- + +##### `value`Required + +```typescript +public readonly value: string; +``` + +- *Type:* string + +Gets the string value for a parameter. + +--- + + ### PipelineRunBuilder Builds a `PipelineRun` using the supplied configuration. @@ -3946,4 +4032,32 @@ Gets the name of the workspace. --- +## Protocols + +### IValueResolver + +- *Implemented By:* ConstantStringValueResolver, PipelineParameterValueResolver, IValueResolver + +Resolves the value through different means. + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| value | string | Gets the string value for a parameter. | + +--- + +##### `value`Required + +```typescript +public readonly value: string; +``` + +- *Type:* string + +Gets the string value for a parameter. + +--- From 7af13a28d53f5f5254bcd6dfd7e1773d18bdbdcb Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 12 Jun 2024 09:56:53 -0400 Subject: [PATCH 12/30] Clarify id and name properties of builders Signed-off-by: snehajais22 --- src/builders.ts | 67 ++++++++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 23 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 15be3d1..6892ac9 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -84,7 +84,7 @@ export const DefaultBuilderOptions: BuilderOptions = { */ export class WorkspaceBuilder { private readonly _logicalID: string; - private _name?: string; + private _binding?: string; private _description?: string; /** @@ -107,7 +107,7 @@ export class WorkspaceBuilder { * Gets the name of the workspace. */ public get name(): string | undefined { - return this._name; + return this._logicalID; } /** @@ -118,11 +118,18 @@ export class WorkspaceBuilder { } /** - * Sets the name of the workspace. - * @param name + * Gets the binding of task workspace to a pipeline workspace. */ - public withName(name: string): WorkspaceBuilder { - this._name = name; + public get binding(): string | undefined { + return this._binding; + } + + /** + * Sets the binding of a task workspace to a pipeline workspace. + * @param workspace + */ + public withBinding(workspace: string): WorkspaceBuilder { + this._binding = workspace; return this; } @@ -663,14 +670,14 @@ export class TaskBuilder { } /** - * Gets the name of the `Task` built by the `TaskBuilder`. + * Gets the name of the `Task` within a pipeline. */ public get name(): string | undefined { return this._name; } /** - * Sets the name of the `Task` being built. + * Sets the `Task`'s name within a pipeline. * @param name */ public withName(name: string): TaskBuilder { @@ -787,7 +794,7 @@ export class TaskBuilder { const props: TaskProps = { metadata: { - name: this.name, + name: this.logicalID, labels: this._labels, annotations: this._annotations, }, @@ -811,16 +818,26 @@ export class TaskBuilder { export class PipelineBuilder { private readonly _scope: Construct; private readonly _id: string; + /** + * @deprecated pipeline name is given by `id` + */ private _name?: string; private _description?: string; private _tasks?: TaskBuilder[]; + /** + * Creates a new instance of the `PipelineBuilder` using the given `scope` and + * `id`. + * @param scope + * @param id + */ public constructor(scope: Construct, id: string) { this._scope = scope; this._id = id; } /** + * @deprecated pipeline name is set by `id` * Provides the name for the pipeline task and will be * rendered as the `name` property. * @param name @@ -834,7 +851,7 @@ export class PipelineBuilder { * Gets the name of the pipeline */ public get name(): string { - return this._name || this._id; + return this._id; } /** @@ -905,14 +922,18 @@ export class PipelineBuilder { const pipelineWorkspaces = new Map(); this._tasks?.forEach((t) => { t.workspaces?.forEach((w) => { + if (w.binding) { // Only add the workspace on the pipeline level if it is not already // there... - const ws = pipelineWorkspaces.get(w.name!); - if (!ws) { - pipelineWorkspaces.set(w.name!, { - name: w.name, - description: w.description, - }); + const ws = pipelineWorkspaces.get(w.binding); + if (!ws) { + pipelineWorkspaces.set(w.binding, { + name: w.binding, + description: w.description, + }); + } + } else { + throw new Error(`Workspace ${w.logicalID} in Task ${t.name} has no binding to a pipeline workspace.`); } }); }); @@ -947,7 +968,7 @@ export class PipelineBuilder { t.workspaces?.forEach((w) => { taskWorkspaces.push({ name: w.logicalID, - workspace: w.name, + workspace: w.binding, }); }); @@ -960,11 +981,11 @@ export class PipelineBuilder { // built along with the pipeline, but only if we haven't already // built the task yet. if (!taskList.find(it => { - return it == t.name; + return it == t.logicalID; })) { t.buildTask(); } - taskList.push(t.name!); + taskList.push(t.logicalID); } }); @@ -986,9 +1007,9 @@ export class PipelineBuilder { function createOrderedPipelineTask(t: TaskBuilder, after: string, params: TaskParam[], ws: TaskWorkspace[]): PipelineTask { if (after) { return { - name: t.logicalID, + name: t.name || t.logicalID, taskRef: { - name: t.name, + name: t.logicalID, }, runAfter: [after], params: params, @@ -996,9 +1017,9 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string, params: TaskPa }; } return { - name: t.logicalID, + name: t.name || t.logicalID, taskRef: { - name: t.name, + name: t.logicalID, }, params: params, workspaces: ws, From e215411fe5683ac8574933406763cc223d39d0fe Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 12 Jun 2024 14:15:26 -0400 Subject: [PATCH 13/30] Update builder unit tests Signed-off-by: snehajais22 --- API.md | 49 ++++++++++++--------- src/builders.ts | 21 ++++----- test/pipelinebuilder.test.ts | 82 ++++++++++++++++-------------------- test/taskbuilder.test.ts | 11 ++--- 4 files changed, 79 insertions(+), 84 deletions(-) diff --git a/API.md b/API.md index dbe45ef..b72452d 100644 --- a/API.md +++ b/API.md @@ -2937,8 +2937,8 @@ new PipelineBuilder(scope: Construct, id: string) | **Name** | **Description** | | --- | --- | | buildPipeline | Builds the actual [Pipeline](https://tekton.dev/docs/getting-started/pipelines/) from the settings configured using the fluid syntax. | -| withDescription | Provides the name for the pipeline task and will be rendered as the `name` property. | -| withName | Provides the name for the pipeline task and will be rendered as the `name` property. | +| withDescription | Provides the description for the pipeline. | +| withName | *No description.* | | withStringParam | Add parameter of type string to the Pipeline. | | withTask | *No description.* | @@ -2964,7 +2964,7 @@ Builds the actual [Pipeline](https://tekton.dev/docs/getting-started/pipelines/) public withDescription(description: string): PipelineBuilder ``` -Provides the name for the pipeline task and will be rendered as the `name` property. +Provides the description for the pipeline. ###### `description`Required @@ -2972,14 +2972,12 @@ Provides the name for the pipeline task and will be rendered as the `name` prope --- -##### `withName` +##### ~~`withName`~~ ```typescript public withName(name: string): PipelineBuilder ``` -Provides the name for the pipeline task and will be rendered as the `name` property. - ###### `name`Required - *Type:* string @@ -3326,7 +3324,7 @@ new TaskBuilder(scope: Construct, id: string) | withAnnotation | Adds an annotation to the `Task` `metadata` with the provided key and value. | | withDescription | Sets the `description` of the `Task` being built. | | withLabel | Adds a label to the `Task` with the provided label key and value. | -| withName | Sets the name of the `Task` being built. | +| withName | Sets the name of the `Task` to be used within a pipeline. | | withResult | Allows you to add a result to the Task. | | withStep | Adds the given `step` (`TaskStepBuilder`) to the `Task`. | | withStringParam | Adds a parameter of type string to the `Task`. | @@ -3410,7 +3408,7 @@ Adds a label to the `Task` with the provided label key and value. public withName(name: string): TaskBuilder ``` -Sets the name of the `Task` being built. +Sets the name of the `Task` to be used within a pipeline. ###### `name`Required @@ -3493,7 +3491,7 @@ Adds the specified workspace to the `Task`. | --- | --- | --- | | logicalID | string | *No description.* | | description | string | Gets the `description` of the `Task`. | -| name | string | Gets the name of the `Task` built by the `TaskBuilder`. | +| name | string | Gets the name of the `Task` in the context of a pipeline. | | parameters | ParameterBuilder[] | *No description.* | | workspaces | WorkspaceBuilder[] | Gets the workspaces for the `Task`. | @@ -3529,7 +3527,7 @@ public readonly name: string; - *Type:* string -Gets the name of the `Task` built by the `TaskBuilder`. +Gets the name of the `Task` in the context of a pipeline. --- @@ -3951,34 +3949,34 @@ new WorkspaceBuilder(id: string) | **Name** | **Description** | | --- | --- | +| withBinding | Sets the binding of a task workspace to a pipeline workspace. | | withDescription | Sets the description of the workspace. | -| withName | Sets the name of the workspace. | --- -##### `withDescription` +##### `withBinding` ```typescript -public withDescription(desc: string): WorkspaceBuilder +public withBinding(workspace: string): WorkspaceBuilder ``` -Sets the description of the workspace. +Sets the binding of a task workspace to a pipeline workspace. -###### `desc`Required +###### `workspace`Required - *Type:* string --- -##### `withName` +##### `withDescription` ```typescript -public withName(name: string): WorkspaceBuilder +public withDescription(desc: string): WorkspaceBuilder ``` -Sets the name of the workspace. +Sets the description of the workspace. -###### `name`Required +###### `desc`Required - *Type:* string @@ -3990,6 +3988,7 @@ Sets the name of the workspace. | **Name** | **Type** | **Description** | | --- | --- | --- | | description | string | Gets the description of the workspace. | +| binding | string | Gets the binding of task workspace to a pipeline workspace. | | logicalID | string | Gets the logical ID of the `Workspace`. | | name | string | Gets the name of the workspace. | @@ -4007,6 +4006,18 @@ Gets the description of the workspace. --- +##### `binding`Optional + +```typescript +public readonly binding: string; +``` + +- *Type:* string + +Gets the binding of task workspace to a pipeline workspace. + +--- + ##### `logicalID`Optional ```typescript diff --git a/src/builders.ts b/src/builders.ts index 5400c5c..e57f663 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -716,14 +716,14 @@ export class TaskBuilder { } /** - * Gets the name of the `Task` within a pipeline. + * Gets the name of the `Task` in the context of a pipeline. */ public get name(): string | undefined { return this._name; } /** - * Sets the `Task`'s name within a pipeline. + * Sets the name of the `Task` to be used within a pipeline. * @param name */ public withName(name: string): TaskBuilder { @@ -864,23 +864,21 @@ export class TaskBuilder { export class PipelineBuilder { private readonly _scope: Construct; private readonly _id: string; - /** - * @deprecated pipeline name is given by `id` - */ - private _name?: string; + private _name: string; private _description?: string; private _tasks?: TaskBuilder[]; private _params?: Map; /** * Creates a new instance of the `PipelineBuilder` using the given `scope` and - * `id`. + * `id`. `id` also sets the pipeline's name. * @param scope * @param id */ public constructor(scope: Construct, id: string) { this._scope = scope; this._id = id; + this._name = id; } /** @@ -898,12 +896,11 @@ export class PipelineBuilder { * Gets the name of the pipeline */ public get name(): string { - return this._id; + return this._name; } /** - * Provides the name for the pipeline task and will be - * rendered as the `name` property. + * Provides the description for the pipeline. * @param description */ public withDescription(description: string): PipelineBuilder { @@ -998,7 +995,7 @@ export class PipelineBuilder { }); } } else { - throw new Error(`Workspace ${w.logicalID} in Task ${t.name} has no binding to a pipeline workspace.`); + throw new Error(`Workspace '${w.logicalID}' in Task '${t.name}' has no binding to a workspace in Pipeline '${this.name}'.`); } }); }); @@ -1037,7 +1034,7 @@ export class PipelineBuilder { }); }); - const pt = createOrderedPipelineTask(t, ((i > 0) ? this._tasks![i - 1].logicalID : ''), taskParams, taskWorkspaces); + const pt = createOrderedPipelineTask(t, ((i > 0) ? (this._tasks![i - 1].name || this._tasks![i - 1].logicalID) : ''), taskParams, taskWorkspaces); pipelineTasks.push(pt); diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index ececfc0..65efef5 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -19,15 +19,14 @@ class PipelineRunTest extends Chart { const pipelineParam = new ParameterBuilder('repo-url') .withDefaultValue(''); - const myTask = new TaskBuilder(this, 'fetch-source') - .withName('git-clone') + const myTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output') - .withName('shared-data') + .withBinding('shared-data') .withDescription('The files cloned by the task')) .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); - const pipeline = new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + const pipeline = new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withStringParam(pipelineParam); @@ -47,15 +46,14 @@ class PipelineRunTestWithUndefinedWorkspaceError extends Chart { const pipelineParam = new ParameterBuilder('repo-url') .withDefaultValue(''); - const myTask = new TaskBuilder(this, 'fetch-source') - .withName('git-clone') + const myTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output') - .withName('shared-data') + .withBinding('shared-data') .withDescription('The files cloned by the task')) .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); - const pipeline = new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + const pipeline = new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withStringParam(pipelineParam); @@ -74,15 +72,14 @@ class PipelineRunTestWithUndefinedParamError extends Chart { const pipelineParam = new ParameterBuilder('repo-url') .withDefaultValue(''); - const myTask = new TaskBuilder(this, 'fetch-source') - .withName('git-clone') + const myTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output') - .withName('shared-data') + .withBinding('shared-data') .withDescription('The files cloned by the task')) .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); - const pipeline = new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + const pipeline = new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withStringParam(pipelineParam); @@ -102,15 +99,14 @@ class PipelineRunTestWithError extends Chart { const pipelineParam = new ParameterBuilder('repo-url') .withDefaultValue(''); - const myTask = new TaskBuilder(this, 'fetch-source') - .withName('git-clone') + const myTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output') - .withName('shared-data') + .withBinding('shared-data') .withDescription('The files cloned by the task')) .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); - const pipeline = new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + const pipeline = new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withStringParam(pipelineParam); @@ -136,24 +132,23 @@ class MySecondTestChart extends Chart { const questParam = new ParameterBuilder('your-quest') .withDefaultValue(''); - const myTask = new TaskBuilder(this, 'git-clone') - .withName('fetch-source') + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') .withWorkspace(new WorkspaceBuilder('output') - .withName('shared-data') + .withBinding('shared-data') .withDescription('The files cloned by the task')) .withWorkspace(new WorkspaceBuilder('ssh-credentials') - .withName('ssh-creds') + .withBinding('ssh-creds') .withDescription('The SSH files for credentials')) .withWorkspace(new WorkspaceBuilder('config') - .withName('config-data') + .withBinding('config-data') .withDescription('The files for configuration for stuff')) .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(repoParam))) .withStringParam(new ParameterBuilder('name').withValue(fromPipelineParam(nameParam))) .withStringParam(new ParameterBuilder('color').withValue(fromPipelineParam(colorParam))) .withStringParam(new ParameterBuilder('quest').withValue(fromPipelineParam(questParam))); - new PipelineBuilder(this, 'my-test-pipeline-2') - .withName('clone-build-push') + new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withStringParam(repoParam) @@ -170,7 +165,7 @@ class MyTestChartWithDuplicateParams extends Chart { const myWorkspace = new WorkspaceBuilder('output') .withDescription('The files cloned by the task') - .withName('shared-data'); + .withBinding('shared-data'); const pipelineParam = new ParameterBuilder('repo-url') .withDefaultValue(''); @@ -178,20 +173,19 @@ class MyTestChartWithDuplicateParams extends Chart { const urlParam = new ParameterBuilder('url') .withValue(fromPipelineParam(pipelineParam)); - const myTask = new TaskBuilder(this, 'git-clone') - .withName('fetch-source') + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') .withWorkspace(myWorkspace) .withStringParam(urlParam) ; - const myTask2 = new TaskBuilder(this, 'cat-readme') - .withName('print-readme') + const myTask2 = new TaskBuilder(this, 'print-readme') + .withName('cat-readme') .withWorkspace(myWorkspace) .withStringParam(urlParam) ; - new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withTask(myTask2) @@ -211,16 +205,15 @@ class MyTestChartWithStaticOverride extends Chart { const urlParam = new ParameterBuilder('url') .withValue(fromPipelineParam(pipelineParam)); - const myTask2 = new TaskBuilder(this, 'cat-readme') - .withName('print-readme') + const myTask2 = new TaskBuilder(this, 'print-readme') + .withName('cat-readme') .withStringParam(urlParam) ; // This should override the parameter with a static value. myTask2.withStringParam(new ParameterBuilder('url').withValue('https://api.example.io')); - new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask2) // pipelineParam not added because myTask2 doesn't reference it anymore @@ -234,7 +227,7 @@ class MyTestChartWithDuplicateTasks extends Chart { const myWorkspace = new WorkspaceBuilder('output') .withDescription('The files cloned by the task') - .withName('shared-data'); + .withBinding('shared-data'); const pipelineParam = new ParameterBuilder('repo-url') .withDefaultValue(''); @@ -242,14 +235,13 @@ class MyTestChartWithDuplicateTasks extends Chart { const urlParam = new ParameterBuilder('url') .withValue(fromPipelineParam(pipelineParam)); - const myTask = new TaskBuilder(this, 'git-clone') - .withName('fetch-source') + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') .withWorkspace(myWorkspace) .withStringParam(urlParam) ; - new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') .withTask(myTask) .withTask(myTask) @@ -271,8 +263,8 @@ class PipelineLevelParamTest extends Chart { const taskParam2 = new ParameterBuilder('pathToContext') .withValue(fromPipelineParam(pipelineParam)); - const myTask = new TaskBuilder(this, 'build-skaffold-web') - .withName('build-push') + const myTask = new TaskBuilder(this, 'build-push') + .withName('build-skaffold-web') .withStringParam(taskParam) .withStringParam(taskParam2); diff --git a/test/taskbuilder.test.ts b/test/taskbuilder.test.ts index b4eec97..92fd580 100644 --- a/test/taskbuilder.test.ts +++ b/test/taskbuilder.test.ts @@ -23,8 +23,7 @@ class TestBasicTaskBuild extends Chart { .ofType('string') .withValue(fromPipelineParam(pipelineParam)); - new TaskBuilder(this, 'my-task') - .withName('ansible-runner') + new TaskBuilder(this, 'ansible-runner') .withDescription('Task to run Ansible playbooks using Ansible Runner') .withWorkspace(runnerDir) .withStringParam(projectDirName) @@ -87,8 +86,7 @@ class TestBasicTaskBuildFromObject extends Chart { .ofType('string') .withValue(fromPipelineParam(pipelineParam)); - new TaskBuilder(this, 'my-task') - .withName('ansible-runner') + new TaskBuilder(this, 'ansible-runner') .withDescription('Task to run Ansible playbooks using Ansible Runner') .withWorkspace(runnerDir) .withStringParam(projectDirName) @@ -109,8 +107,7 @@ class TestBasicTaskBuildFromScriptData extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); - new TaskBuilder(this, 'my-task') - .withName('ansible-runner') + new TaskBuilder(this, 'ansible-runner') .withDescription('Task to run Ansible playbooks using Ansible Runner') .withStep(new TaskStepBuilder() .withName('requirements') @@ -129,7 +126,6 @@ class TestIBMCloudSecretsManagerGet extends Chart { super(scope, id, props); new TaskBuilder(this, 'ibmcloud-secrets-manager-get') - .withName('ibmcloud-secrets-manager-get') .withLabel('app.kubernetes.io/version', '0.1') .withAnnotation('tekton.dev/categories', 'IBM Cloud') .withAnnotation('tekton.dev/pipelines.minVersion', '0.17.0') @@ -160,7 +156,6 @@ class TestPullRequestTaskBuild extends Chart { // be nice to compare the snapshots with each other just to make sure that the // builder does build the exact same object as the longer, non-builder method. new TaskBuilder(this, 'pull-request') - .withName('pull-request') .withDescription('This Task allows a user to interact with an SCM (source control management)\nsystem through an abstracted interface\n\nThis Task works with both public SCM instances and self-hosted/enterprise GitHub/GitLab\ninstances. In download mode, this Task will look at the state of an existing pull\nrequest and populate the pr workspace with the state of the pull request, including the\n.MANIFEST file. In upload mode, this Task will look at the contents of the pr workspace\n and compare it to the .MANIFEST file (if it exists).') .withStringParam(new ParameterBuilder('mode') .withValue(fromPipelineParam(new ParameterBuilder('mode'))) From e1e133d7c8c68d11337a4f199a3c0a142aba63f2 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 14 Jun 2024 10:12:13 -0400 Subject: [PATCH 14/30] Check task names are unique in pipeline, with updated tests Signed-off-by: snehajais22 --- src/builders.ts | 10 + .../pipelinebuilder.test.ts.snap | 182 +++++++++--------- test/pipelinebuilder.test.ts | 52 ++++- 3 files changed, 148 insertions(+), 96 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index e57f663..b8b9121 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -1014,9 +1014,19 @@ export class PipelineBuilder { // the build. Not that it really hurts anything, but it makes the multidoc // YAML file bigger and more complex than it needs to be. const taskList: string[] = new Array(); + // To ensure that all tasks in the pipeline have unique names. + const taskNames: string[] = new Array(); this._tasks?.forEach((t, i) => { + const taskName = t.name || t.logicalID; + if (taskNames.find(it => { + return it == taskName; + })) { + throw new Error(`Multiple tasks found with name '${taskName}' in Pipeline '${this.name}', but task names must be unique.`); + } + taskNames.push(taskName); + const taskParams: TaskParam[] = new Array(); const taskWorkspaces: PipelineTaskWorkspace[] = new Array(); diff --git a/test/__snapshots__/pipelinebuilder.test.ts.snap b/test/__snapshots__/pipelinebuilder.test.ts.snap index 92bfede..8e60bbe 100644 --- a/test/__snapshots__/pipelinebuilder.test.ts.snap +++ b/test/__snapshots__/pipelinebuilder.test.ts.snap @@ -91,35 +91,8 @@ exports[`PipelineBuilderTest PipelineBuilderWithComplexTasks 1`] = ` ] `; -exports[`PipelineBuilderTest PipelineBuilderWithDuplicateTasks 1`] = ` +exports[`PipelineBuilderTest PipelineBuilderWithDuplicates 1`] = ` [ - { - "apiVersion": "tekton.dev/v1", - "kind": "Task", - "metadata": { - "annotations": undefined, - "labels": undefined, - "name": "fetch-source", - }, - "spec": { - "description": undefined, - "params": [ - { - "default": "", - "description": "", - "name": "url", - }, - ], - "results": [], - "steps": [], - "workspaces": [ - { - "description": "The files cloned by the task", - "name": "output", - }, - ], - }, - }, { "apiVersion": "tekton.dev/v1", "kind": "Pipeline", @@ -155,7 +128,7 @@ exports[`PipelineBuilderTest PipelineBuilderWithDuplicateTasks 1`] = ` ], }, { - "name": "git-clone", + "name": "cat-readme", "params": [ { "name": "url", @@ -166,7 +139,7 @@ exports[`PipelineBuilderTest PipelineBuilderWithDuplicateTasks 1`] = ` "git-clone", ], "taskRef": { - "name": "fetch-source", + "name": "print-readme", }, "workspaces": [ { @@ -187,76 +160,76 @@ exports[`PipelineBuilderTest PipelineBuilderWithDuplicateTasks 1`] = ` ] `; -exports[`PipelineBuilderTest PipelineBuilderWithDuplicates 1`] = ` +exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` [ { "apiVersion": "tekton.dev/v1", - "kind": "Pipeline", + "kind": "Task", "metadata": { - "name": "clone-build-push", + "annotations": undefined, + "labels": undefined, + "name": "build-push", }, "spec": { - "description": "This pipeline closes a repository, builds a Docker image, etc.", + "description": undefined, "params": [ { "default": "", - "name": "repo-url", + "description": "", + "name": "pathToDockerFile", + }, + { + "default": "", + "description": "", + "name": "pathToContext", + }, + ], + "results": [], + "steps": [], + "workspaces": [], + }, + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "Pipeline", + "metadata": { + "name": "pipeline-with-parameters", + }, + "spec": { + "description": undefined, + "params": [ + { + "default": "/some/where/or/other", + "name": "context", "type": "string", }, ], "tasks": [ { - "name": "git-clone", + "name": "build-skaffold-web", "params": [ { - "name": "url", - "value": "$(params.repo-url)", - }, - ], - "taskRef": { - "name": "fetch-source", - }, - "workspaces": [ - { - "name": "output", - "workspace": "shared-data", + "name": "pathToDockerFile", + "value": "Dockerfile", }, - ], - }, - { - "name": "cat-readme", - "params": [ { - "name": "url", - "value": "$(params.repo-url)", + "name": "pathToContext", + "value": "$(params.context)", }, ], - "runAfter": [ - "git-clone", - ], "taskRef": { - "name": "print-readme", + "name": "build-push", }, - "workspaces": [ - { - "name": "output", - "workspace": "shared-data", - }, - ], - }, - ], - "workspaces": [ - { - "description": "The files cloned by the task", - "name": "shared-data", + "workspaces": [], }, ], + "workspaces": [], }, }, ] `; -exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` +exports[`PipelineBuilderTest PipelineBuilderWithSimilarTasks 1`] = ` [ { "apiVersion": "tekton.dev/v1", @@ -264,7 +237,7 @@ exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` "metadata": { "annotations": undefined, "labels": undefined, - "name": "build-push", + "name": "fetch-source", }, "spec": { "description": undefined, @@ -272,54 +245,81 @@ exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` { "default": "", "description": "", - "name": "pathToDockerFile", - }, - { - "default": "", - "description": "", - "name": "pathToContext", + "name": "url", }, ], "results": [], "steps": [], - "workspaces": [], + "workspaces": [ + { + "description": "The files cloned by the task", + "name": "output", + }, + ], }, }, { "apiVersion": "tekton.dev/v1", "kind": "Pipeline", "metadata": { - "name": "pipeline-with-parameters", + "name": "clone-build-push", }, "spec": { - "description": undefined, + "description": "This pipeline closes a repository, builds a Docker image, etc.", "params": [ { - "default": "/some/where/or/other", - "name": "context", + "default": "", + "name": "repo-url", "type": "string", }, ], "tasks": [ { - "name": "build-skaffold-web", + "name": "git-clone", "params": [ { - "name": "pathToDockerFile", - "value": "Dockerfile", + "name": "url", + "value": "$(params.repo-url)", }, + ], + "taskRef": { + "name": "fetch-source", + }, + "workspaces": [ { - "name": "pathToContext", - "value": "$(params.context)", + "name": "output", + "workspace": "shared-data", + }, + ], + }, + { + "name": "git-clone-2", + "params": [ + { + "name": "url", + "value": "$(params.repo-url)", }, ], + "runAfter": [ + "git-clone", + ], "taskRef": { - "name": "build-push", + "name": "fetch-source", }, - "workspaces": [], + "workspaces": [ + { + "name": "output", + "workspace": "shared-data", + }, + ], + }, + ], + "workspaces": [ + { + "description": "The files cloned by the task", + "name": "shared-data", }, ], - "workspaces": [], }, }, ] diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index 65efef5..55618e3 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -221,7 +221,7 @@ class MyTestChartWithStaticOverride extends Chart { } } -class MyTestChartWithDuplicateTasks extends Chart { +class MyTestChartWithDuplicateTasksError extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); @@ -275,6 +275,40 @@ class PipelineLevelParamTest extends Chart { } } +class MyTestChartWithSimilarTasks extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myWorkspace = new WorkspaceBuilder('output') + .withDescription('The files cloned by the task') + .withBinding('shared-data'); + + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue(''); + + const urlParam = new ParameterBuilder('url') + .withValue(fromPipelineParam(pipelineParam)); + + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') + .withWorkspace(myWorkspace) + .withStringParam(urlParam) + ; + + const myTask2 = new TaskBuilder(this, 'fetch-source') + .withName('git-clone-2') + .withWorkspace(myWorkspace) + .withStringParam(urlParam); + + new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .withTask(myTask2) + .withStringParam(pipelineParam) + .buildPipeline({ includeDependencies: true }); + } +} + describe('PipelineBuilderTest', () => { test('PipelineRunBuilder', () => { const app = Testing.app(); @@ -328,11 +362,12 @@ describe('PipelineBuilderTest', () => { expect(results).toMatchSnapshot(); }); - test('PipelineBuilderWithDuplicateTasks', () => { + test('PipelineBuilderWithDuplicateTasksError', () => { const app = Testing.app(); - const chart = new MyTestChartWithDuplicateTasks(app, 'test-chart'); - const results = Testing.synth(chart); - expect(results).toMatchSnapshot(); + const f = () => { + new MyTestChartWithDuplicateTasksError(app, 'test-chart'); + }; + expect(f).toThrowError('Multiple tasks found with name \'git-clone\' in Pipeline \'clone-build-push\', but task names must be unique.'); }); test('PipelineBuilderWithParameters', () => { @@ -341,4 +376,11 @@ describe('PipelineBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + + test('PipelineBuilderWithSimilarTasks', () => { + const app = Testing.app(); + const chart = new MyTestChartWithSimilarTasks(app, 'test-chart'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); }); From 362ff21015409db7d73788d91238030cbb1a0656 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 19 Jun 2024 10:47:40 -0400 Subject: [PATCH 15/30] Include specifyRunAfter functionality for tasks in pipeline Signed-off-by: snehajais22 --- API.md | 32 ++++++++++++++++++++++++++++++++ src/builders.ts | 40 ++++++++++++++++++++++++++++++++++++---- 2 files changed, 68 insertions(+), 4 deletions(-) diff --git a/API.md b/API.md index b72452d..65a649c 100644 --- a/API.md +++ b/API.md @@ -3321,6 +3321,7 @@ new TaskBuilder(scope: Construct, id: string) | **Name** | **Description** | | --- | --- | | buildTask | Builds the `Task`. | +| specifyRunAfter | Allows you to specify which task(s), if any, the 'Task' should run after in a pipeline. | | withAnnotation | Adds an annotation to the `Task` `metadata` with the provided key and value. | | withDescription | Sets the `description` of the `Task` being built. | | withLabel | Adds a label to the `Task` with the provided label key and value. | @@ -3340,6 +3341,24 @@ public buildTask(): void Builds the `Task`. +##### `specifyRunAfter` + +```typescript +public specifyRunAfter(taskArray: string[]): TaskBuilder +``` + +Allows you to specify which task(s), if any, the 'Task' should run after in a pipeline. + +An empty array as input indicates the 'Task' yaml should have no +runAfter field. +By default, the value of runAfter is set to the preceeding 'Task' in the pipeline. + +###### `taskArray`Required + +- *Type:* string[] + +--- + ##### `withAnnotation` ```typescript @@ -3493,6 +3512,7 @@ Adds the specified workspace to the `Task`. | description | string | Gets the `description` of the `Task`. | | name | string | Gets the name of the `Task` in the context of a pipeline. | | parameters | ParameterBuilder[] | *No description.* | +| runAfter | string[] | Gets the list of task names for the runAfter value of the `Task`. | | workspaces | WorkspaceBuilder[] | Gets the workspaces for the `Task`. | --- @@ -3541,6 +3561,18 @@ public readonly parameters: ParameterBuilder[]; --- +##### `runAfter`Optional + +```typescript +public readonly runAfter: string[]; +``` + +- *Type:* string[] + +Gets the list of task names for the runAfter value of the `Task`. + +--- + ##### `workspaces`Optional ```typescript diff --git a/src/builders.ts b/src/builders.ts index b8b9121..0619837 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -664,6 +664,7 @@ export class TaskBuilder { private _labels?: { [key: string]: string; }; + private _runafter?: string[]; /** * Creates a new instance of the `TaskBuilder` using the given `scope` and @@ -807,6 +808,25 @@ export class TaskBuilder { return this; } + /** + * Allows you to specify the names of which task(s), if any, the 'Task' should + * run after in a pipeline. An empty array as input indicates the 'Task' yaml + * should have no runAfter field. + * By default, the value of runAfter is set to the preceeding 'Task' in the pipeline. + * @param taskArray + */ + public specifyRunAfter(taskArray: string[]): TaskBuilder { + this._runafter = taskArray; + return this; + } + + /** + * Gets the list of task names for the runAfter value of the `Task`. + */ + public get runAfter(): string[] | undefined { + return this._runafter; + } + /** * Builds the `Task`. */ @@ -1044,7 +1064,19 @@ export class PipelineBuilder { }); }); - const pt = createOrderedPipelineTask(t, ((i > 0) ? (this._tasks![i - 1].name || this._tasks![i - 1].logicalID) : ''), taskParams, taskWorkspaces); + const after = []; + if (t.runAfter != undefined) { + t.runAfter.forEach(name => { + if (!this._tasks?.find(it => {return (it.name || it.logicalID) == name;})) { + throw new Error(`${name} supplied as value for runAfter but no such task found in pipeline.`); + } + after.push(name); + }); + } else if (i > 0) { + after.push(this._tasks![i - 1].name || this._tasks![i - 1].logicalID); + } + + const pt = createOrderedPipelineTask(t, after, taskParams, taskWorkspaces); pipelineTasks.push(pt); @@ -1076,14 +1108,14 @@ export class PipelineBuilder { } } -function createOrderedPipelineTask(t: TaskBuilder, after: string, params: TaskParam[], ws: TaskWorkspace[]): PipelineTask { - if (after) { +function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: TaskParam[], ws: TaskWorkspace[]): PipelineTask { + if (after.length) { return { name: t.name || t.logicalID, taskRef: { name: t.logicalID, }, - runAfter: [after], + runAfter: after, params: params, workspaces: ws, }; From aa780479020c914962382fd1b0253d3e751da904 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 19 Jun 2024 10:54:31 -0400 Subject: [PATCH 16/30] Simplify implementation of Task name default behavior Signed-off-by: snehajais22 --- API.md | 24 +++++++++++++----------- src/builders.ts | 15 ++++++++------- 2 files changed, 21 insertions(+), 18 deletions(-) diff --git a/API.md b/API.md index 65a649c..97c1333 100644 --- a/API.md +++ b/API.md @@ -3321,7 +3321,7 @@ new TaskBuilder(scope: Construct, id: string) | **Name** | **Description** | | --- | --- | | buildTask | Builds the `Task`. | -| specifyRunAfter | Allows you to specify which task(s), if any, the 'Task' should run after in a pipeline. | +| specifyRunAfter | Allows you to specify the names of which task(s), if any, the 'Task' should run after in a pipeline. | | withAnnotation | Adds an annotation to the `Task` `metadata` with the provided key and value. | | withDescription | Sets the `description` of the `Task` being built. | | withLabel | Adds a label to the `Task` with the provided label key and value. | @@ -3347,10 +3347,10 @@ Builds the `Task`. public specifyRunAfter(taskArray: string[]): TaskBuilder ``` -Allows you to specify which task(s), if any, the 'Task' should run after in a pipeline. +Allows you to specify the names of which task(s), if any, the 'Task' should run after in a pipeline. -An empty array as input indicates the 'Task' yaml should have no -runAfter field. +An empty array as input indicates the 'Task' yaml +should have no runAfter field. By default, the value of runAfter is set to the preceeding 'Task' in the pipeline. ###### `taskArray`Required @@ -3509,8 +3509,8 @@ Adds the specified workspace to the `Task`. | **Name** | **Type** | **Description** | | --- | --- | --- | | logicalID | string | *No description.* | -| description | string | Gets the `description` of the `Task`. | | name | string | Gets the name of the `Task` in the context of a pipeline. | +| description | string | Gets the `description` of the `Task`. | | parameters | ParameterBuilder[] | *No description.* | | runAfter | string[] | Gets the list of task names for the runAfter value of the `Task`. | | workspaces | WorkspaceBuilder[] | Gets the workspaces for the `Task`. | @@ -3527,27 +3527,29 @@ public readonly logicalID: string; --- -##### `description`Optional +##### `name`Required ```typescript -public readonly description: string; +public readonly name: string; ``` - *Type:* string -Gets the `description` of the `Task`. +Gets the name of the `Task` in the context of a pipeline. + +If not set, the 'Task' id is used. --- -##### `name`Optional +##### `description`Optional ```typescript -public readonly name: string; +public readonly description: string; ``` - *Type:* string -Gets the name of the `Task` in the context of a pipeline. +Gets the `description` of the `Task`. --- diff --git a/src/builders.ts b/src/builders.ts index 0619837..54ddfce 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -718,9 +718,10 @@ export class TaskBuilder { /** * Gets the name of the `Task` in the context of a pipeline. + * If not set, the 'Task' id is used. */ - public get name(): string | undefined { - return this._name; + public get name(): string { + return this._name || this._id; } /** @@ -1039,7 +1040,7 @@ export class PipelineBuilder { this._tasks?.forEach((t, i) => { - const taskName = t.name || t.logicalID; + const taskName = t.name; if (taskNames.find(it => { return it == taskName; })) { @@ -1067,13 +1068,13 @@ export class PipelineBuilder { const after = []; if (t.runAfter != undefined) { t.runAfter.forEach(name => { - if (!this._tasks?.find(it => {return (it.name || it.logicalID) == name;})) { + if (!this._tasks?.find(it => {return (it.name) == name;})) { throw new Error(`${name} supplied as value for runAfter but no such task found in pipeline.`); } after.push(name); }); } else if (i > 0) { - after.push(this._tasks![i - 1].name || this._tasks![i - 1].logicalID); + after.push(this._tasks![i - 1].name); } const pt = createOrderedPipelineTask(t, after, taskParams, taskWorkspaces); @@ -1111,7 +1112,7 @@ export class PipelineBuilder { function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: TaskParam[], ws: TaskWorkspace[]): PipelineTask { if (after.length) { return { - name: t.name || t.logicalID, + name: t.name, taskRef: { name: t.logicalID, }, @@ -1121,7 +1122,7 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: Task }; } return { - name: t.name || t.logicalID, + name: t.name, taskRef: { name: t.logicalID, }, From 96f71fc3d7f4a7cc6155ff708e42efc9fcf38264 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 19 Jun 2024 11:13:16 -0400 Subject: [PATCH 17/30] Unit tests for specifyRunAfter Signed-off-by: snehajais22 --- src/builders.ts | 2 +- .../pipelinebuilder.test.ts.snap | 81 +++++++++++++++++++ test/pipelinebuilder.test.ts | 52 ++++++++++++ 3 files changed, 134 insertions(+), 1 deletion(-) diff --git a/src/builders.ts b/src/builders.ts index 54ddfce..166e188 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -1069,7 +1069,7 @@ export class PipelineBuilder { if (t.runAfter != undefined) { t.runAfter.forEach(name => { if (!this._tasks?.find(it => {return (it.name) == name;})) { - throw new Error(`${name} supplied as value for runAfter but no such task found in pipeline.`); + throw new Error(`'${name}' supplied as value for runAfter but no such task found in pipeline.`); } after.push(name); }); diff --git a/test/__snapshots__/pipelinebuilder.test.ts.snap b/test/__snapshots__/pipelinebuilder.test.ts.snap index 8e60bbe..fa3c47d 100644 --- a/test/__snapshots__/pipelinebuilder.test.ts.snap +++ b/test/__snapshots__/pipelinebuilder.test.ts.snap @@ -229,6 +229,87 @@ exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` ] `; +exports[`PipelineBuilderTest PipelineBuilderWithRunAfter 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "Task", + "metadata": { + "annotations": undefined, + "labels": undefined, + "name": "print-readme", + }, + "spec": { + "description": undefined, + "params": [], + "results": [], + "steps": [], + "workspaces": [], + }, + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "Task", + "metadata": { + "annotations": undefined, + "labels": undefined, + "name": "git-clone", + }, + "spec": { + "description": undefined, + "params": [], + "results": [], + "steps": [], + "workspaces": [], + }, + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "Pipeline", + "metadata": { + "name": "clone-read", + }, + "spec": { + "description": undefined, + "params": [], + "tasks": [ + { + "name": "cat-readme", + "params": [], + "runAfter": [ + "fetch-again", + ], + "taskRef": { + "name": "print-readme", + }, + "workspaces": [], + }, + { + "name": "fetch-source", + "params": [], + "taskRef": { + "name": "git-clone", + }, + "workspaces": [], + }, + { + "name": "fetch-again", + "params": [], + "runAfter": [ + "fetch-source", + ], + "taskRef": { + "name": "git-clone", + }, + "workspaces": [], + }, + ], + "workspaces": [], + }, + }, +] +`; + exports[`PipelineBuilderTest PipelineBuilderWithSimilarTasks 1`] = ` [ { diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index 55618e3..c969499 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -309,6 +309,43 @@ class MyTestChartWithSimilarTasks extends Chart { } } +class MyTestChartWithRunAfter extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const firstTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-source') + .specifyRunAfter([]); + + const secondTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-again'); + + const thirdTask = new TaskBuilder(this, 'print-readme') + .withName('cat-readme') + .specifyRunAfter(['fetch-again']); + + new PipelineBuilder(this, 'clone-read') + .withTask(thirdTask) + .withTask(firstTask) + .withTask(secondTask) + .buildPipeline({ includeDependencies: true }); + } +} + +class MyTestChartWithRunAfterError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'print-readme') + .withName('cat-readme') + .specifyRunAfter(['fetch-source']); + + new PipelineBuilder(this, 'clone-read') + .withTask(myTask) + .buildPipeline({ includeDependencies: true }); + } +} + describe('PipelineBuilderTest', () => { test('PipelineRunBuilder', () => { const app = Testing.app(); @@ -383,4 +420,19 @@ describe('PipelineBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + + test('PipelineBuilderWithRunAfter', () => { + const app = Testing.app(); + const chart = new MyTestChartWithRunAfter(app, 'test-chart'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); + + test('PipelineBuilderWithRunAfterError', () => { + const app = Testing.app(); + const f = () => { + new MyTestChartWithRunAfterError(app, 'test-chart'); + }; + expect(f).toThrowError('\'fetch-source\' supplied as value for runAfter but no such task found in pipeline.'); + }); }); From ed3c7b53359f79776a81d85e74b0ca085e3a1b81 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 10 Jul 2024 14:04:53 -0400 Subject: [PATCH 18/30] Add referencingTask method to TaskBuilder Signed-off-by: snehajais22 --- API.md | 33 +++++++++++++++++++++++++++++++++ src/builders.ts | 31 +++++++++++++++++++++++++------ test/pipelinebuilder.test.ts | 7 +++++-- 3 files changed, 63 insertions(+), 8 deletions(-) diff --git a/API.md b/API.md index 97c1333..6be3a65 100644 --- a/API.md +++ b/API.md @@ -3321,6 +3321,7 @@ new TaskBuilder(scope: Construct, id: string) | **Name** | **Description** | | --- | --- | | buildTask | Builds the `Task`. | +| referencingTask | Sets the taskRef field of the 'Task'. | | specifyRunAfter | Allows you to specify the names of which task(s), if any, the 'Task' should run after in a pipeline. | | withAnnotation | Adds an annotation to the `Task` `metadata` with the provided key and value. | | withDescription | Sets the `description` of the `Task` being built. | @@ -3341,6 +3342,23 @@ public buildTask(): void Builds the `Task`. +##### `referencingTask` + +```typescript +public referencingTask(taskRef: string): TaskBuilder +``` + +Sets the taskRef field of the 'Task'. + +Use only for tasks within pipelines: +overrides logicalID as the name of the 'Task' in its individual yaml. + +###### `taskRef`Required + +- *Type:* string + +--- + ##### `specifyRunAfter` ```typescript @@ -3510,6 +3528,7 @@ Adds the specified workspace to the `Task`. | --- | --- | --- | | logicalID | string | *No description.* | | name | string | Gets the name of the `Task` in the context of a pipeline. | +| taskRef | string | Gets the taskRef field of the `Task` for use within a pipeline. | | description | string | Gets the `description` of the `Task`. | | parameters | ParameterBuilder[] | *No description.* | | runAfter | string[] | Gets the list of task names for the runAfter value of the `Task`. | @@ -3541,6 +3560,20 @@ If not set, the 'Task' id is used. --- +##### `taskRef`Required + +```typescript +public readonly taskRef: string; +``` + +- *Type:* string + +Gets the taskRef field of the `Task` for use within a pipeline. + +If not set, the 'Task' id is used. + +--- + ##### `description`Optional ```typescript diff --git a/src/builders.ts b/src/builders.ts index 166e188..72abcf8 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -653,6 +653,7 @@ export class TaskBuilder { private _steps?: TaskStepBuilder[]; private _name?: string; private _description?: string; + private _taskref?: string; // These were initially arrays, but converted them to maps so that if // multiple values are added that the last one will win. private _workspaces = new Map; @@ -828,6 +829,24 @@ export class TaskBuilder { return this._runafter; } + /** + * Sets the taskRef field of the 'Task'. Use only for tasks within pipelines: + * overrides logicalID as the name of the 'Task' in its individual yaml. + * @param taskRef + */ + public referencingTask(taskRef: string): TaskBuilder { + this._taskref = taskRef; + return this; + } + + /** + * Gets the taskRef field of the `Task` for use within a pipeline. + * If not set, the 'Task' id is used. + */ + public get taskRef(): string { + return this._taskref || this._id; + } + /** * Builds the `Task`. */ @@ -861,7 +880,7 @@ export class TaskBuilder { const props: TaskProps = { metadata: { - name: this.logicalID, + name: this.taskRef, labels: this._labels, annotations: this._annotations, }, @@ -1084,13 +1103,13 @@ export class PipelineBuilder { if (opts.includeDependencies) { // Build the task if the user has asked for the dependencies to be // built along with the pipeline, but only if we haven't already - // built the task yet. + // built the taskRef yet. if (!taskList.find(it => { - return it == t.logicalID; + return it == t.taskRef; })) { t.buildTask(); } - taskList.push(t.logicalID); + taskList.push(t.taskRef); } }); @@ -1114,7 +1133,7 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: Task return { name: t.name, taskRef: { - name: t.logicalID, + name: t.taskRef, }, runAfter: after, params: params, @@ -1124,7 +1143,7 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: Task return { name: t.name, taskRef: { - name: t.logicalID, + name: t.taskRef, }, params: params, workspaces: ws, diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index c969499..e64761b 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -290,12 +290,14 @@ class MyTestChartWithSimilarTasks extends Chart { .withValue(fromPipelineParam(pipelineParam)); const myTask = new TaskBuilder(this, 'fetch-source') + .referencingTask('fetch-source') .withName('git-clone') .withWorkspace(myWorkspace) .withStringParam(urlParam) ; - const myTask2 = new TaskBuilder(this, 'fetch-source') + const myTask2 = new TaskBuilder(this, 'fetch-source-2') + .referencingTask('fetch-source') .withName('git-clone-2') .withWorkspace(myWorkspace) .withStringParam(urlParam); @@ -317,7 +319,8 @@ class MyTestChartWithRunAfter extends Chart { .withName('fetch-source') .specifyRunAfter([]); - const secondTask = new TaskBuilder(this, 'git-clone') + const secondTask = new TaskBuilder(this, 'git-clone-2') + .referencingTask('git-clone') .withName('fetch-again'); const thirdTask = new TaskBuilder(this, 'print-readme') From dbdb089d5e235f8927ae4b609b847c78ab0ce3e5 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Thu, 18 Jul 2024 15:24:57 -0400 Subject: [PATCH 19/30] feature: using resolvers to specify remote tasks in taskRef field Signed-off-by: snehajais22 --- API.md | 272 ++++++++++++++++-- src/builders.ts | 111 +++++-- src/pipelines.ts | 4 +- src/tasks.ts | 23 ++ .../pipelinebuilder.test.ts.snap | 62 ++++ test/pipelinebuilder.test.ts | 38 +++ 6 files changed, 466 insertions(+), 44 deletions(-) diff --git a/API.md b/API.md index 6be3a65..ea13cd9 100644 --- a/API.md +++ b/API.md @@ -1806,7 +1806,7 @@ const pipelineTask: PipelineTask = { ... } | name | string | *No description.* | | params | TaskParam[] | *No description.* | | runAfter | string[] | *No description.* | -| taskRef | TaskRef | *No description.* | +| taskRef | TaskRef \| RemoteTaskRef | *No description.* | | workspaces | PipelineTaskWorkspace[] | *No description.* | --- @@ -1844,10 +1844,10 @@ public readonly runAfter: string[]; ##### `taskRef`Optional ```typescript -public readonly taskRef: TaskRef; +public readonly taskRef: TaskRef | RemoteTaskRef; ``` -- *Type:* TaskRef +- *Type:* TaskRef | RemoteTaskRef --- @@ -1880,7 +1880,7 @@ const pipelineTaskDef: PipelineTaskDef = { ... } | name | string | *No description.* | | params | TaskParam[] | *No description.* | | runAfter | string[] | *No description.* | -| taskRef | TaskRef | *No description.* | +| taskRef | TaskRef \| RemoteTaskRef | *No description.* | | workspaces | PipelineTaskWorkspace[] | *No description.* | | refParams | PipelineParam[] | *No description.* | | refWorkspaces | PipelineTaskWorkspace[] | *No description.* | @@ -1920,10 +1920,10 @@ public readonly runAfter: string[]; ##### `taskRef`Optional ```typescript -public readonly taskRef: TaskRef; +public readonly taskRef: TaskRef | RemoteTaskRef; ``` -- *Type:* TaskRef +- *Type:* TaskRef | RemoteTaskRef --- @@ -2042,6 +2042,49 @@ The description of the workspace. --- +### ResolverParam + +A Resolver parameter value. + +#### Initializer + +```typescript +import { ResolverParam } from 'cdk8s-pipelines' + +const resolverParam: ResolverParam = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| name | string | *No description.* | +| value | string | The value of the resolver parameter. | + +--- + +##### `name`Optional + +```typescript +public readonly name: string; +``` + +- *Type:* string + +--- + +##### `value`Optional + +```typescript +public readonly value: string; +``` + +- *Type:* string + +The value of the resolver parameter. + +--- + ### TaskEnvValueSource The source for a `env` `valueFrom`. @@ -2652,6 +2695,88 @@ public readonly logicalID: string; ## Classes +### ClusterTaskResolver + +- *Implements:* IRemoteTaskResolver + +Resolves the provided cluster-scoped task into yaml for the taskRef field. + +#### Initializers + +```typescript +import { ClusterTaskResolver } from 'cdk8s-pipelines' + +new ClusterTaskResolver(name: string, namespace: string) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| name | string | The name of the cluster-scoped task. | +| namespace | string | The namespace of the cluster-scoped task. | + +--- + +##### `name`Required + +- *Type:* string + +The name of the cluster-scoped task. + +--- + +##### `namespace`Required + +- *Type:* string + +The namespace of the cluster-scoped task. + +--- + + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| taskRef | RemoteTaskRef | Gets the YAML representation of cluster-scoped task. | +| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | + +--- + +##### `taskRef`Required + +```typescript +public readonly taskRef: RemoteTaskRef; +``` + +- *Type:* RemoteTaskRef + +Gets the YAML representation of cluster-scoped task. + +--- + +##### `params`Optional + +```typescript +public readonly params: ResolverParam[]; +``` + +- *Type:* ResolverParam[] + +--- + +##### `resolver`Optional + +```typescript +public readonly resolver: string; +``` + +- *Type:* string + +--- + + ### ConstantStringValueResolver - *Implements:* IValueResolver @@ -3282,6 +3407,71 @@ The sub path on the `persistentVolumeClaim` to use for the `workspace`. +### RemoteTaskRef + +A remote `Task` reference. + +Will be generated as a `taskRef`. + +#### Initializers + +```typescript +import { RemoteTaskRef } from 'cdk8s-pipelines' + +new RemoteTaskRef(resolver: string, params: ResolverParam[]) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| resolver | string | *No description.* | +| params | ResolverParam[] | *No description.* | + +--- + +##### `resolver`Required + +- *Type:* string + +--- + +##### `params`Required + +- *Type:* ResolverParam[] + +--- + + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | + +--- + +##### `params`Optional + +```typescript +public readonly params: ResolverParam[]; +``` + +- *Type:* ResolverParam[] + +--- + +##### `resolver`Optional + +```typescript +public readonly resolver: string; +``` + +- *Type:* string + +--- + + ### TaskBuilder Builds Tekton `Task` objects that are independent of a `Pipeline`. @@ -3321,7 +3511,7 @@ new TaskBuilder(scope: Construct, id: string) | **Name** | **Description** | | --- | --- | | buildTask | Builds the `Task`. | -| referencingTask | Sets the taskRef field of the 'Task'. | +| referencingTask | TODO. | | specifyRunAfter | Allows you to specify the names of which task(s), if any, the 'Task' should run after in a pipeline. | | withAnnotation | Adds an annotation to the `Task` `metadata` with the provided key and value. | | withDescription | Sets the `description` of the `Task` being built. | @@ -3345,17 +3535,16 @@ Builds the `Task`. ##### `referencingTask` ```typescript -public referencingTask(taskRef: string): TaskBuilder +public referencingTask(task: string | IRemoteTaskResolver): TaskBuilder ``` -Sets the taskRef field of the 'Task'. +TODO. -Use only for tasks within pipelines: -overrides logicalID as the name of the 'Task' in its individual yaml. +###### `task`Required -###### `taskRef`Required +- *Type:* string | IRemoteTaskResolver -- *Type:* string +TODO. --- @@ -3528,7 +3717,7 @@ Adds the specified workspace to the `Task`. | --- | --- | --- | | logicalID | string | *No description.* | | name | string | Gets the name of the `Task` in the context of a pipeline. | -| taskRef | string | Gets the taskRef field of the `Task` for use within a pipeline. | +| taskRef | TaskRef \| RemoteTaskRef | TODO. | | description | string | Gets the `description` of the `Task`. | | parameters | ParameterBuilder[] | *No description.* | | runAfter | string[] | Gets the list of task names for the runAfter value of the `Task`. | @@ -3563,14 +3752,12 @@ If not set, the 'Task' id is used. ##### `taskRef`Required ```typescript -public readonly taskRef: string; +public readonly taskRef: TaskRef | RemoteTaskRef; ``` -- *Type:* string +- *Type:* TaskRef | RemoteTaskRef -Gets the taskRef field of the `Task` for use within a pipeline. - -If not set, the 'Task' id is used. +TODO. --- @@ -4112,6 +4299,53 @@ Gets the name of the workspace. ## Protocols +### IRemoteTaskResolver + +- *Implemented By:* ClusterTaskResolver, IRemoteTaskResolver + + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| taskRef | RemoteTaskRef | Gets the taskRef yaml for a remote Task. | +| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | + +--- + +##### `taskRef`Required + +```typescript +public readonly taskRef: RemoteTaskRef; +``` + +- *Type:* RemoteTaskRef + +Gets the taskRef yaml for a remote Task. + +--- + +##### `params`Optional + +```typescript +public readonly params: ResolverParam[]; +``` + +- *Type:* ResolverParam[] + +--- + +##### `resolver`Optional + +```typescript +public readonly resolver: string; +``` + +- *Type:* string + +--- + ### IValueResolver - *Implemented By:* ConstantStringValueResolver, PipelineParameterValueResolver, IValueResolver diff --git a/src/builders.ts b/src/builders.ts index 72abcf8..0d5f7db 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -17,7 +17,20 @@ import { PipelineTaskWorkspace, PipelineWorkspace, } from './pipelines'; -import { Task, TaskEnvValueSource, TaskParam, TaskProps, TaskSpecParam, TaskSpecResult, TaskStep, TaskStepEnv, TaskWorkspace } from './tasks'; +import { + Task, + TaskEnvValueSource, + TaskParam, + TaskProps, + TaskSpecParam, + TaskSpecResult, + TaskStep, + TaskStepEnv, + TaskWorkspace, + ResolverParam, + RemoteTaskRef, + TaskRef, +} from './tasks'; const DefaultPipelineServiceAccountName = 'default:pipeline'; @@ -640,6 +653,56 @@ export class TaskStepBuilder { } } +export interface IRemoteTaskResolver { + resolver?: string; + params?: ResolverParam[]; + /** + * Gets the taskRef yaml for a remote Task + * @returns RemoteTaskRef The yaml as an API Object + */ + get taskRef(): RemoteTaskRef; +} + +/** + * Resolves the provided cluster-scoped task into yaml for the taskRef field. + */ +export class ClusterTaskResolver implements IRemoteTaskResolver { + resolver?: string; + params?: ResolverParam[]; + + /** + * Creates an instance of the `ClusterTaskResolver`. + * @param name The name of the cluster-scoped task. + * @param namespace The namespace of the cluster-scoped task. + */ + constructor(name: string, namespace: string) { + this.resolver = 'cluster'; + this.params = new Array(); + this.params.push({ + name: 'name', + value: name, + }); + this.params.push({ + name: 'namespace', + value: namespace, + }); + this.params.push({ + name: 'kind', + value: 'task', + }); + } + + /** + * Gets the YAML representation of cluster-scoped task. + */ + get taskRef(): RemoteTaskRef { + return { + resolver: this.resolver, + params: this.params, + }; + } +} + /** * Builds Tekton `Task` objects that are independent of a `Pipeline`. * @@ -653,7 +716,7 @@ export class TaskBuilder { private _steps?: TaskStepBuilder[]; private _name?: string; private _description?: string; - private _taskref?: string; + private _taskref?: TaskRef | RemoteTaskRef; // These were initially arrays, but converted them to maps so that if // multiple values are added that the last one will win. private _workspaces = new Map; @@ -830,21 +893,23 @@ export class TaskBuilder { } /** - * Sets the taskRef field of the 'Task'. Use only for tasks within pipelines: - * overrides logicalID as the name of the 'Task' in its individual yaml. - * @param taskRef + * TODO + * @param task TODO */ - public referencingTask(taskRef: string): TaskBuilder { - this._taskref = taskRef; + public referencingTask(task: string | IRemoteTaskResolver): TaskBuilder { + if (typeof(task) == 'string') { + this._taskref = { name: task }; + } else { + this._taskref = task.taskRef; + } return this; } /** - * Gets the taskRef field of the `Task` for use within a pipeline. - * If not set, the 'Task' id is used. + * TODO */ - public get taskRef(): string { - return this._taskref || this._id; + public get taskRef(): TaskRef | RemoteTaskRef { + return this._taskref || { name: this._id }; } /** @@ -878,9 +943,12 @@ export class TaskBuilder { }); }); + // Note: buildTask called for this TaskBuilder object only if this.taskRef is a TaskRef + const taskName = ('name' in this.taskRef) ? this.taskRef.name : this.logicalID; + const props: TaskProps = { metadata: { - name: this.taskRef, + name: taskName, labels: this._labels, annotations: this._annotations, }, @@ -1100,16 +1168,17 @@ export class PipelineBuilder { pipelineTasks.push(pt); - if (opts.includeDependencies) { + if (opts.includeDependencies && 'name' in t.taskRef) { // Build the task if the user has asked for the dependencies to be - // built along with the pipeline, but only if we haven't already - // built the taskRef yet. + // built along with the pipeline, but only if the task does not + // reference a remote location and we haven't already built the taskRef. + const buildName = t.taskRef.name!; if (!taskList.find(it => { - return it == t.taskRef; + return it == buildName; })) { t.buildTask(); } - taskList.push(t.taskRef); + taskList.push(buildName); } }); @@ -1132,9 +1201,7 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: Task if (after.length) { return { name: t.name, - taskRef: { - name: t.taskRef, - }, + taskRef: t.taskRef, runAfter: after, params: params, workspaces: ws, @@ -1142,9 +1209,7 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: Task } return { name: t.name, - taskRef: { - name: t.taskRef, - }, + taskRef: t.taskRef, params: params, workspaces: ws, }; diff --git a/src/pipelines.ts b/src/pipelines.ts index 473cfc6..5023153 100644 --- a/src/pipelines.ts +++ b/src/pipelines.ts @@ -1,7 +1,7 @@ import { ApiObject, ApiObjectMetadata, GroupVersionKind } from 'cdk8s'; import { Construct } from 'constructs'; import { NamedResource, TektonV1ApiVersion } from './common'; -import { TaskParam, TaskRef } from './tasks'; +import { RemoteTaskRef, TaskParam, TaskRef } from './tasks'; // The following interfaces and classes are strictly for generating the YAML or // JSON in the proper format for the Tekton pipelines. See the builders to use @@ -23,7 +23,7 @@ export interface PipelineTaskWorkspace extends NamedResource { * A task in a pipeline. See https://tekton.dev/docs/pipelines/pipelines/#adding-tasks-to-the-pipeline */ export interface PipelineTask extends NamedResource { - readonly taskRef?: TaskRef; + readonly taskRef?: TaskRef | RemoteTaskRef; readonly params?: TaskParam[]; readonly runAfter?: string[]; readonly workspaces?: PipelineTaskWorkspace[]; diff --git a/src/tasks.ts b/src/tasks.ts index a7c7e43..f19d209 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -68,6 +68,29 @@ export class TaskRef { } } +/** + * A Resolver parameter value. + */ +export interface ResolverParam extends NamedResource { + /** + * The value of the resolver parameter. + */ + readonly value?: string; +} + +/** + * A remote `Task` reference. Will be generated as a `taskRef`. + */ +export class RemoteTaskRef { + resolver?: string; + params?: ResolverParam[]; + + constructor(resolver: string, params: ResolverParam[]) { + this.resolver = resolver; + this.params = params; + } +} + /** * A Task parameter value. */ diff --git a/test/__snapshots__/pipelinebuilder.test.ts.snap b/test/__snapshots__/pipelinebuilder.test.ts.snap index fa3c47d..72a7643 100644 --- a/test/__snapshots__/pipelinebuilder.test.ts.snap +++ b/test/__snapshots__/pipelinebuilder.test.ts.snap @@ -229,6 +229,68 @@ exports[`PipelineBuilderTest PipelineBuilderWithParameters 1`] = ` ] `; +exports[`PipelineBuilderTest PipelineBuilderWithResolver 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "Pipeline", + "metadata": { + "name": "clone-build-push", + }, + "spec": { + "description": "This pipeline closes a repository, builds a Docker image, etc.", + "params": [ + { + "default": "", + "name": "repo-url", + "type": "string", + }, + ], + "tasks": [ + { + "name": "fetch-source", + "params": [ + { + "name": "url", + "value": "$(params.repo-url)", + }, + ], + "taskRef": { + "params": [ + { + "name": "name", + "value": "git-clone", + }, + { + "name": "namespace", + "value": "default", + }, + { + "name": "kind", + "value": "task", + }, + ], + "resolver": "cluster", + }, + "workspaces": [ + { + "name": "output", + "workspace": "shared-data", + }, + ], + }, + ], + "workspaces": [ + { + "description": "The files cloned by the task", + "name": "shared-data", + }, + ], + }, + }, +] +`; + exports[`PipelineBuilderTest PipelineBuilderWithRunAfter 1`] = ` [ { diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index e64761b..6b76706 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -10,6 +10,7 @@ import { WorkspaceBuilder, fromPipelineParam, constant, + ClusterTaskResolver, } from '../src'; class PipelineRunTest extends Chart { @@ -349,6 +350,36 @@ class MyTestChartWithRunAfterError extends Chart { } } +class PipelineTestWithResolver extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myWorkspace = new WorkspaceBuilder('output') + .withDescription('The files cloned by the task') + .withBinding('shared-data'); + + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue(''); + + const urlParam = new ParameterBuilder('url') + .withValue(fromPipelineParam(pipelineParam)); + + const resolver = new ClusterTaskResolver('git-clone', 'default'); + + const myTask = new TaskBuilder(this, 'fetch-source') + .referencingTask(resolver) + .withWorkspace(myWorkspace) + .withStringParam(urlParam) + ; + + new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .withStringParam(pipelineParam) + .buildPipeline({ includeDependencies: true }); + } +} + describe('PipelineBuilderTest', () => { test('PipelineRunBuilder', () => { const app = Testing.app(); @@ -438,4 +469,11 @@ describe('PipelineBuilderTest', () => { }; expect(f).toThrowError('\'fetch-source\' supplied as value for runAfter but no such task found in pipeline.'); }); + + test('PipelineBuilderWithResolver', () => { + const app = Testing.app(); + const chart = new PipelineTestWithResolver(app, 'test-chart'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); }); From 0f960e0752e075a3c5ed88614da2b7751397bdb3 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 19 Jul 2024 10:58:31 -0400 Subject: [PATCH 20/30] Update documentation for new feature Signed-off-by: snehajais22 --- API.md | 15 ++++++++++----- src/builders.ts | 12 ++++++++---- 2 files changed, 18 insertions(+), 9 deletions(-) diff --git a/API.md b/API.md index ea13cd9..da0cffb 100644 --- a/API.md +++ b/API.md @@ -3511,7 +3511,7 @@ new TaskBuilder(scope: Construct, id: string) | **Name** | **Description** | | --- | --- | | buildTask | Builds the `Task`. | -| referencingTask | TODO. | +| referencingTask | Sets the taskRef field of the `Task`. | | specifyRunAfter | Allows you to specify the names of which task(s), if any, the 'Task' should run after in a pipeline. | | withAnnotation | Adds an annotation to the `Task` `metadata` with the provided key and value. | | withDescription | Sets the `description` of the `Task` being built. | @@ -3538,13 +3538,16 @@ Builds the `Task`. public referencingTask(task: string | IRemoteTaskResolver): TaskBuilder ``` -TODO. +Sets the taskRef field of the `Task`. + +Use only for tasks within pipelines: +overrides `logicalID as the name of the `Task` in its individual yaml. ###### `task`Required - *Type:* string | IRemoteTaskResolver -TODO. +as string: name of the local task being referenced as IRemoteTaskResolver: resolver for a task in remote location. --- @@ -3717,7 +3720,7 @@ Adds the specified workspace to the `Task`. | --- | --- | --- | | logicalID | string | *No description.* | | name | string | Gets the name of the `Task` in the context of a pipeline. | -| taskRef | TaskRef \| RemoteTaskRef | TODO. | +| taskRef | TaskRef \| RemoteTaskRef | Gets the taskRef field of the `Task` for use within a pipeline. | | description | string | Gets the `description` of the `Task`. | | parameters | ParameterBuilder[] | *No description.* | | runAfter | string[] | Gets the list of task names for the runAfter value of the `Task`. | @@ -3757,7 +3760,9 @@ public readonly taskRef: TaskRef | RemoteTaskRef; - *Type:* TaskRef | RemoteTaskRef -TODO. +Gets the taskRef field of the `Task` for use within a pipeline. + +If not set, a locally-scoped task named with the `logicalID` is used. --- diff --git a/src/builders.ts b/src/builders.ts index 0d5f7db..c3f26a6 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -893,8 +893,10 @@ export class TaskBuilder { } /** - * TODO - * @param task TODO + * Sets the taskRef field of the `Task`. Use only for tasks within pipelines: + * overrides `logicalID as the name of the `Task` in its individual yaml. + * @param task as string: name of the local task being referenced + * as IRemoteTaskResolver: resolver for a task in remote location */ public referencingTask(task: string | IRemoteTaskResolver): TaskBuilder { if (typeof(task) == 'string') { @@ -906,7 +908,8 @@ export class TaskBuilder { } /** - * TODO + * Gets the taskRef field of the `Task` for use within a pipeline. + * If not set, a locally-scoped task named with the `logicalID` is used. */ public get taskRef(): TaskRef | RemoteTaskRef { return this._taskref || { name: this._id }; @@ -943,7 +946,8 @@ export class TaskBuilder { }); }); - // Note: buildTask called for this TaskBuilder object only if this.taskRef is a TaskRef + // Note: buildTask called for this TaskBuilder object only if this.taskRef is a TaskRef, + // not if it is a RemoteTaskRef const taskName = ('name' in this.taskRef) ? this.taskRef.name : this.logicalID; const props: TaskProps = { From a4646b35cc66662ce3d0ea86cd62970a820385c1 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Mon, 22 Jul 2024 13:41:00 -0400 Subject: [PATCH 21/30] Added unit tests for src code coverage Signed-off-by: snehajais22 --- src/builders.ts | 4 +- .../pipelinebuilder.test.ts.snap | 98 ++++++++++++++ test/pipelinebuilder.test.ts | 121 ++++++++++++++++++ 3 files changed, 221 insertions(+), 2 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 72abcf8..a99d96d 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -29,7 +29,7 @@ const DefaultPipelineServiceAccountName = 'default:pipeline'; * @param sa * @param saNamespace */ -function createRoleBindingProps(bindingName: string, bindingNs: string, rolename: string, sa: string, saNamespace: string): ApiObjectProps { +export function createRoleBindingProps(bindingName: string, bindingNs: string, rolename: string, sa: string, saNamespace: string): ApiObjectProps { return { apiVersion: 'rbac.authorization.k8s.io/v1', kind: 'ClusterRoleBinding', @@ -792,7 +792,7 @@ export class TaskBuilder { // First, check to see if there is already a result with this name const existing = this._results.find((obj) => obj.name === name); if (existing) { - throw new Error(`Cannot add result ${name}, as it already exists.`); + throw new Error(`Cannot add result '${name}', as it already exists.`); } this._results.push({ name: name, diff --git a/test/__snapshots__/pipelinebuilder.test.ts.snap b/test/__snapshots__/pipelinebuilder.test.ts.snap index fa3c47d..75d3a5c 100644 --- a/test/__snapshots__/pipelinebuilder.test.ts.snap +++ b/test/__snapshots__/pipelinebuilder.test.ts.snap @@ -558,3 +558,101 @@ exports[`PipelineBuilderTest PipelineRunBuilder 1`] = ` }, ] `; + +exports[`PipelineBuilderTest PipelineRunBuilderCustom 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "Task", + "metadata": { + "annotations": undefined, + "labels": undefined, + "name": "git-clone", + }, + "spec": { + "description": undefined, + "params": [ + { + "default": "", + "description": "", + "name": "url", + }, + ], + "results": [], + "steps": [], + "workspaces": [], + }, + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "Pipeline", + "metadata": { + "name": "clone-build-push", + }, + "spec": { + "description": "This pipeline closes a repository, builds a Docker image, etc.", + "params": [ + { + "default": "", + "name": "repo-url", + "type": "string", + }, + ], + "tasks": [ + { + "name": "fetch-source", + "params": [ + { + "name": "url", + "value": "$(params.repo-url)", + }, + ], + "taskRef": { + "name": "git-clone", + }, + "workspaces": [], + }, + ], + "workspaces": [], + }, + }, + { + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": { + "name": "pipeline-admin-default-crb", + "namespace": "default", + }, + "roleRef": { + "kind": "ClusterRole", + "name": "cluster-admin", + }, + "subjects": [ + { + "kind": "ServiceAccount", + "name": "pipeline", + "namespace": "default", + }, + ], + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "PipelineRun", + "metadata": { + "name": "my-pipeline-run", + }, + "spec": { + "params": [ + { + "name": "repo-url", + "value": "https://github.com/exmaple/my-repo", + }, + ], + "pipelineRef": { + "name": "clone-build-push", + }, + "workspaces": [], + }, + }, +] +`; diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index e64761b..84a811a 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -10,6 +10,7 @@ import { WorkspaceBuilder, fromPipelineParam, constant, + createRoleBindingProps, } from '../src'; class PipelineRunTest extends Chart { @@ -39,6 +40,40 @@ class PipelineRunTest extends Chart { } } +class PipelineRunTestCustom extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue(''); + + const myTask = new TaskBuilder(this, 'git-clone') + .withName('fetch-source') + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(pipelineParam))); + + const pipeline = new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .withStringParam(pipelineParam); + pipeline.buildPipeline({ includeDependencies: true }); + + const CRB = createRoleBindingProps( + 'pipeline-admin-default-crb', + 'default', + 'cluster-admin', + 'pipeline', + 'default'); + + const serviceAccount = 'default:pipeline'; + + new PipelineRunBuilder(this, 'my-pipeline-run', pipeline) + .withRunParam('repo-url', 'https://github.com/exmaple/my-repo') + .withClusterRoleBindingProps(CRB) + .withServiceAccount(serviceAccount) + .buildPipelineRun({ includeDependencies: true }); + } +} + class PipelineRunTestWithUndefinedWorkspaceError extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { super(scope, id, props); @@ -194,6 +229,61 @@ class MyTestChartWithDuplicateParams extends Chart { } } +class MyTestChartWithPipelineParamError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const pipelineParam = new ParameterBuilder('repo-url') + .withDefaultValue(''); + + const urlParam = new ParameterBuilder('url') + .withValue(fromPipelineParam(pipelineParam)); + + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') + .withStringParam(urlParam); + + new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .buildPipeline(); + } +} + +class MyTestChartWithDuplicateResultError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') + .withResult('result', 'The original result') + .withResult('result', 'The duplicate result'); + + new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .buildPipeline(); + } +} + +class MyTestChartWithWorkspaceError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myWorkspace = new WorkspaceBuilder('output') + .withDescription('The files cloned by the task'); + + const myTask = new TaskBuilder(this, 'fetch-source') + .withName('git-clone') + .withWorkspace(myWorkspace); + + new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .buildPipeline(); + } +} + class MyTestChartWithStaticOverride extends Chart { constructor(scope: Construct, id: string, props?: ChartProps) { @@ -357,6 +447,13 @@ describe('PipelineBuilderTest', () => { expect(results).toMatchSnapshot(); }); + test('PipelineRunBuilderCustom', () => { + const app = Testing.app(); + const chart = new PipelineRunTestCustom(app, 'test-chart'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); + test('PipelineRunBuilderWithError', () => { const app = Testing.app(); const f = () => { @@ -395,6 +492,30 @@ describe('PipelineBuilderTest', () => { expect(results).toMatchSnapshot(); }); + test('PipelineBuilderWithPipelineParamError', () => { + const app = Testing.app(); + const f = () => { + new MyTestChartWithPipelineParamError(app, 'test-chart'); + }; + expect(f).toThrowError('Parameter \'url\' in Task \'git-clone\' expects Pipeline value from parameter \'repo-url\', which is not defined.'); + }); + + test('PipelineBuilderWithDuplicateResults', () => { + const app = Testing.app(); + const f = () => { + new MyTestChartWithDuplicateResultError(app, 'test-chart'); + }; + expect(f).toThrowError('Cannot add result \'result\', as it already exists.'); + }); + + test('PipelineBuilderWithWorkspaceError', () => { + const app = Testing.app(); + const f = () => { + new MyTestChartWithWorkspaceError(app, 'test-chart'); + }; + expect(f).toThrowError('Workspace \'output\' in Task \'git-clone\' has no binding to a workspace in Pipeline \'clone-build-push\'.'); + }); + test('PipelineBuilderWithStaticOverride', () => { const app = Testing.app(); const chart = new MyTestChartWithStaticOverride(app, 'test-chart'); From 93c40f36d346c8e9a575ade7dccf545f010fcaa9 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 31 Jul 2024 14:50:20 -0400 Subject: [PATCH 22/30] Update Remote Resolver structure and add to PipelineRunBuilder Signed-off-by: snehajais22 --- src/builders.ts | 141 +++++++++++++++++++++++++++++------------------ src/common.ts | 23 ++++++++ src/pipelines.ts | 8 +-- src/tasks.ts | 23 -------- 4 files changed, 113 insertions(+), 82 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 770f81a..756cb8c 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -6,7 +6,7 @@ import * as fs from 'fs'; import { ApiObject, ApiObjectProps, Yaml } from 'cdk8s'; import { Construct } from 'constructs'; -import { invertBuildParameter, usingBuildParameter, usingResultsPath } from './common'; +import { invertBuildParameter, usingBuildParameter, usingResultsPath, ResolverParam, RemoteRef } from './common'; import { Pipeline, PipelineParam, @@ -27,8 +27,6 @@ import { TaskStep, TaskStepEnv, TaskWorkspace, - ResolverParam, - RemoteTaskRef, TaskRef, } from './tasks'; @@ -653,30 +651,34 @@ export class TaskStepBuilder { } } -export interface IRemoteTaskResolver { +export interface IRemoteResolver { resolver?: string; params?: ResolverParam[]; + kind?: string; /** * Gets the taskRef yaml for a remote Task * @returns RemoteTaskRef The yaml as an API Object */ - get taskRef(): RemoteTaskRef; + get remoteRef(): RemoteRef; } /** * Resolves the provided cluster-scoped task into yaml for the taskRef field. */ -export class ClusterTaskResolver implements IRemoteTaskResolver { +export class ClusterRemoteResolver implements IRemoteResolver { resolver?: string; params?: ResolverParam[]; + kind?: string; /** - * Creates an instance of the `ClusterTaskResolver`. - * @param name The name of the cluster-scoped task. - * @param namespace The namespace of the cluster-scoped task. + * Creates an instance of the `ClusterRemoteResolver`. + * @param kind task | pipeline + * @param name The name of the cluster-scoped object. + * @param namespace The namespace of the cluster-scoped object. */ - constructor(name: string, namespace: string) { + constructor(kind: string, name: string, namespace: string) { this.resolver = 'cluster'; + this.kind = kind; this.params = new Array(); this.params.push({ name: 'name', @@ -688,14 +690,14 @@ export class ClusterTaskResolver implements IRemoteTaskResolver { }); this.params.push({ name: 'kind', - value: 'task', + value: kind, }); } /** - * Gets the YAML representation of cluster-scoped task. + * Gets the YAML reference to the cluster-scoped object. */ - get taskRef(): RemoteTaskRef { + get remoteRef(): RemoteRef { return { resolver: this.resolver, params: this.params, @@ -716,7 +718,7 @@ export class TaskBuilder { private _steps?: TaskStepBuilder[]; private _name?: string; private _description?: string; - private _taskref?: TaskRef | RemoteTaskRef; + private _taskref?: TaskRef | RemoteRef; // These were initially arrays, but converted them to maps so that if // multiple values are added that the last one will win. private _workspaces = new Map; @@ -898,11 +900,14 @@ export class TaskBuilder { * @param task as string: name of the local task being referenced * as IRemoteTaskResolver: resolver for a task in remote location */ - public referencingTask(task: string | IRemoteTaskResolver): TaskBuilder { + public referencingTask(task: string | IRemoteResolver): TaskBuilder { if (typeof(task) == 'string') { this._taskref = { name: task }; } else { - this._taskref = task.taskRef; + if (task.kind != 'task') { + throw new Error(`Remote resource be of kind 'task' in taskRef of Task ${this.name}.`); + } + this._taskref = task.remoteRef; } return this; } @@ -911,7 +916,7 @@ export class TaskBuilder { * Gets the taskRef field of the `Task` for use within a pipeline. * If not set, a locally-scoped task named with the `logicalID` is used. */ - public get taskRef(): TaskRef | RemoteTaskRef { + public get taskRef(): TaskRef | RemoteRef { return this._taskref || { name: this._id }; } @@ -1227,7 +1232,7 @@ function createOrderedPipelineTask(t: TaskBuilder, after: string[], params: Task export class PipelineRunBuilder { private readonly _scope: Construct; private readonly _id: string; - private readonly _pipeline: PipelineBuilder; + private readonly _pipeline: PipelineBuilder | IRemoteResolver; private readonly _runParams: PipelineRunParam[]; private readonly _runWorkspaces: PipelineRunWorkspace[]; private _sa: string; @@ -1243,9 +1248,10 @@ export class PipelineRunBuilder { * * @param scope The `Construct` in which to create the `PipelineRun`. * @param id The logical ID of the `PipelineRun` construct. - * @param pipeline The `Pipeline` for which to create this run, using the `PipelineBuilder`. + * @param pipeline The `Pipeline` for which to create this run, using the `PipelineBuilder` or + * `IRemoteResolver` for a remote `Pipeline`. */ - public constructor(scope: Construct, id: string, pipeline: PipelineBuilder) { + public constructor(scope: Construct, id: string, pipeline: PipelineBuilder | IRemoteResolver) { this._scope = scope; this._id = id; this._pipeline = pipeline; @@ -1258,20 +1264,28 @@ export class PipelineRunBuilder { /** * Adds a run parameter to the `PipelineRun`. It will throw an error if you try * to add a parameter that does not exist on the pipeline. + * If the `PipelineRun` references a remote pipeline, consistency checks are omitted. * * @param name The name of the parameter added to the pipeline run. * @param value The value of the parameter added to the pipeline run. */ public withRunParam(name: string, value: string): PipelineRunBuilder { - const params = this._pipeline.params; - const p = params.find((obj) => obj.name === name); - if (p) { + if (this._pipeline instanceof PipelineBuilder) { + const params = this._pipeline.params; + const p = params.find((obj) => obj.name === name); + if (p) { + this._runParams.push({ + name: name, + value: value, + }); + } else { + throw new Error(`PipelineRun parameter '${name}' does not exist in pipeline '${this._pipeline.name}'`); + } + } else { this._runParams.push({ name: name, value: value, }); - } else { - throw new Error(`PipelineRun parameter '${name}' does not exist in pipeline '${this._pipeline.name}'`); } return this; } @@ -1316,6 +1330,8 @@ export class PipelineRunBuilder { /** * Builds the `PipelineRun` for the configured `Pipeline` used in the constructor. + * If the `PipelineRun` references a remote pipeline, consistency checks for parameters + * and workspaces expected by the pipeline are omitted. * @param opts */ public buildPipelineRun(opts: BuilderOptions = DefaultBuilderOptions): void { @@ -1324,38 +1340,53 @@ export class PipelineRunBuilder { new ApiObject(this._scope, this._crbProps.metadata?.name!, this._crbProps); } - // Throw an error here if the parameters are not defined that are required - // by the Pipeline, because there is really no point in going any further. - const params = this._pipeline.params; - params.forEach((p) => { - const prp = this._runParams.find((obj) => obj.name == p.name); - if (!prp) { - throw new Error(`Pipeline parameter '${p.name}' is not defined in PipelineRun '${this._id}'`); - } - }); + if (this._pipeline instanceof PipelineBuilder) { + // Throw an error here if the parameters are not defined that are required + // by the Pipeline, because there is really no point in going any further. + const params = this._pipeline.params; + params.forEach((p) => { + const prp = this._runParams.find((obj) => obj.name == p.name); + if (!prp) { + throw new Error(`Pipeline parameter '${p.name}' is not defined in PipelineRun '${this._id}'`); + } + }); - // Do the same thing for workspaces. Check to make sure that the workspaces - // expected by the Pipeline are defined in the PipelineRun. - const workspaces: PipelineWorkspace[] = this._pipeline.workspaces; - workspaces.forEach((ws) => { - const pws = this._runWorkspaces.find((obj) => obj.name == ws.name); - if (!pws) { - throw new Error(`Pipeline workspace '${ws.name}' is not defined in PipelineRun '${this._id}'`); - } - }); + // Do the same thing for workspaces. Check to make sure that the workspaces + // expected by the Pipeline are defined in the PipelineRun. + const workspaces: PipelineWorkspace[] = this._pipeline.workspaces; + workspaces.forEach((ws) => { + const pws = this._runWorkspaces.find((obj) => obj.name == ws.name); + if (!pws) { + throw new Error(`Pipeline workspace '${ws.name}' is not defined in PipelineRun '${this._id}'`); + } + }); - new PipelineRun(this._scope, this._id, { - metadata: { - name: this._id, - }, - serviceAccountName: this._sa, - spec: { - pipelineRef: { - name: this._pipeline.name, + new PipelineRun(this._scope, this._id, { + metadata: { + name: this._id, }, - params: this._runParams, - workspaces: this._runWorkspaces, - }, - }); + serviceAccountName: this._sa, + spec: { + pipelineRef: { + name: this._pipeline.name, + }, + params: this._runParams, + workspaces: this._runWorkspaces, + }, + }); + + } else { + new PipelineRun(this._scope, this._id, { + metadata: { + name: this._id, + }, + serviceAccountName: this._sa, + spec: { + pipelineRef: this._pipeline.remoteRef, + params: this._runParams, + workspaces: this._runWorkspaces, + }, + }); + } } } diff --git a/src/common.ts b/src/common.ts index ef57701..1fa1040 100644 --- a/src/common.ts +++ b/src/common.ts @@ -21,6 +21,29 @@ export interface NameKeyPair extends NamedResource { readonly key?: string; } +/** + * A Resolver parameter value. + */ +export interface ResolverParam extends NamedResource { + /** + * The value of the resolver parameter. + */ + readonly value?: string; +} + +/** + * A remote `Task` or `Pipeline` reference. Generated as `taskRef` or `pipelineRef`, respectively. + */ +export class RemoteRef { + resolver?: string; + params?: ResolverParam[]; + + constructor(resolver: string, params: ResolverParam[]) { + this.resolver = resolver; + this.params = params; + } +} + export function secretKeyRef(name: string, key: string): NameKeyPair { return { name: name, diff --git a/src/pipelines.ts b/src/pipelines.ts index 5023153..d4a4e7b 100644 --- a/src/pipelines.ts +++ b/src/pipelines.ts @@ -1,7 +1,7 @@ import { ApiObject, ApiObjectMetadata, GroupVersionKind } from 'cdk8s'; import { Construct } from 'constructs'; -import { NamedResource, TektonV1ApiVersion } from './common'; -import { RemoteTaskRef, TaskParam, TaskRef } from './tasks'; +import { NamedResource, TektonV1ApiVersion, RemoteRef } from './common'; +import { TaskParam, TaskRef } from './tasks'; // The following interfaces and classes are strictly for generating the YAML or // JSON in the proper format for the Tekton pipelines. See the builders to use @@ -23,7 +23,7 @@ export interface PipelineTaskWorkspace extends NamedResource { * A task in a pipeline. See https://tekton.dev/docs/pipelines/pipelines/#adding-tasks-to-the-pipeline */ export interface PipelineTask extends NamedResource { - readonly taskRef?: TaskRef | RemoteTaskRef; + readonly taskRef?: TaskRef | RemoteRef; readonly params?: TaskParam[]; readonly runAfter?: string[]; readonly workspaces?: PipelineTaskWorkspace[]; @@ -197,7 +197,7 @@ export interface PipelineRunSpec { /** * Required `Pipeline` reference. */ - readonly pipelineRef: PipelineRef; + readonly pipelineRef: PipelineRef | RemoteRef; readonly params?: PipelineRunParam[]; readonly workspaces?: PipelineRunWorkspace[]; } diff --git a/src/tasks.ts b/src/tasks.ts index f19d209..a7c7e43 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -68,29 +68,6 @@ export class TaskRef { } } -/** - * A Resolver parameter value. - */ -export interface ResolverParam extends NamedResource { - /** - * The value of the resolver parameter. - */ - readonly value?: string; -} - -/** - * A remote `Task` reference. Will be generated as a `taskRef`. - */ -export class RemoteTaskRef { - resolver?: string; - params?: ResolverParam[]; - - constructor(resolver: string, params: ResolverParam[]) { - this.resolver = resolver; - this.params = params; - } -} - /** * A Task parameter value. */ From 3f7079dbd6ad5322e7ec9c8b56ba8f82d1a13dba Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 31 Jul 2024 15:11:52 -0400 Subject: [PATCH 23/30] Unit tests and updated docs Signed-off-by: snehajais22 --- API.md | 159 +++++++++++------- src/builders.ts | 6 +- .../pipelinebuilder.test.ts.snap | 65 +++++++ test/pipelinebuilder.test.ts | 70 +++++++- 4 files changed, 235 insertions(+), 65 deletions(-) diff --git a/API.md b/API.md index da0cffb..4f6b515 100644 --- a/API.md +++ b/API.md @@ -1616,7 +1616,7 @@ const pipelineRunSpec: PipelineRunSpec = { ... } | **Name** | **Type** | **Description** | | --- | --- | --- | -| pipelineRef | PipelineRef | Required `Pipeline` reference. | +| pipelineRef | RemoteRef \| PipelineRef | Required `Pipeline` reference. | | params | PipelineRunParam[] | *No description.* | | workspaces | PipelineRunWorkspace[] | *No description.* | @@ -1625,10 +1625,10 @@ const pipelineRunSpec: PipelineRunSpec = { ... } ##### `pipelineRef`Required ```typescript -public readonly pipelineRef: PipelineRef; +public readonly pipelineRef: RemoteRef | PipelineRef; ``` -- *Type:* PipelineRef +- *Type:* RemoteRef | PipelineRef Required `Pipeline` reference. @@ -1806,7 +1806,7 @@ const pipelineTask: PipelineTask = { ... } | name | string | *No description.* | | params | TaskParam[] | *No description.* | | runAfter | string[] | *No description.* | -| taskRef | TaskRef \| RemoteTaskRef | *No description.* | +| taskRef | RemoteRef \| TaskRef | *No description.* | | workspaces | PipelineTaskWorkspace[] | *No description.* | --- @@ -1844,10 +1844,10 @@ public readonly runAfter: string[]; ##### `taskRef`Optional ```typescript -public readonly taskRef: TaskRef | RemoteTaskRef; +public readonly taskRef: RemoteRef | TaskRef; ``` -- *Type:* TaskRef | RemoteTaskRef +- *Type:* RemoteRef | TaskRef --- @@ -1880,7 +1880,7 @@ const pipelineTaskDef: PipelineTaskDef = { ... } | name | string | *No description.* | | params | TaskParam[] | *No description.* | | runAfter | string[] | *No description.* | -| taskRef | TaskRef \| RemoteTaskRef | *No description.* | +| taskRef | RemoteRef \| TaskRef | *No description.* | | workspaces | PipelineTaskWorkspace[] | *No description.* | | refParams | PipelineParam[] | *No description.* | | refWorkspaces | PipelineTaskWorkspace[] | *No description.* | @@ -1920,10 +1920,10 @@ public readonly runAfter: string[]; ##### `taskRef`Optional ```typescript -public readonly taskRef: TaskRef | RemoteTaskRef; +public readonly taskRef: RemoteRef | TaskRef; ``` -- *Type:* TaskRef | RemoteTaskRef +- *Type:* RemoteRef | TaskRef --- @@ -2695,40 +2695,49 @@ public readonly logicalID: string; ## Classes -### ClusterTaskResolver +### ClusterRemoteResolver -- *Implements:* IRemoteTaskResolver +- *Implements:* IRemoteResolver Resolves the provided cluster-scoped task into yaml for the taskRef field. -#### Initializers +#### Initializers ```typescript -import { ClusterTaskResolver } from 'cdk8s-pipelines' +import { ClusterRemoteResolver } from 'cdk8s-pipelines' -new ClusterTaskResolver(name: string, namespace: string) +new ClusterRemoteResolver(kind: string, name: string, namespace: string) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | -| name | string | The name of the cluster-scoped task. | -| namespace | string | The namespace of the cluster-scoped task. | +| kind | string | task \| pipeline. | +| name | string | The name of the cluster-scoped object. | +| namespace | string | The namespace of the cluster-scoped object. | --- -##### `name`Required +##### `kind`Required - *Type:* string -The name of the cluster-scoped task. +task | pipeline. --- -##### `namespace`Required +##### `name`Required - *Type:* string -The namespace of the cluster-scoped task. +The name of the cluster-scoped object. + +--- + +##### `namespace`Required + +- *Type:* string + +The namespace of the cluster-scoped object. --- @@ -2738,25 +2747,36 @@ The namespace of the cluster-scoped task. | **Name** | **Type** | **Description** | | --- | --- | --- | -| taskRef | RemoteTaskRef | Gets the YAML representation of cluster-scoped task. | -| params | ResolverParam[] | *No description.* | -| resolver | string | *No description.* | +| remoteRef | RemoteRef | Gets the YAML reference to the cluster-scoped object. | +| kind | string | *No description.* | +| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | --- -##### `taskRef`Required +##### `remoteRef`Required ```typescript -public readonly taskRef: RemoteTaskRef; +public readonly remoteRef: RemoteRef; ``` -- *Type:* RemoteTaskRef +- *Type:* RemoteRef + +Gets the YAML reference to the cluster-scoped object. + +--- + +##### `kind`Optional + +```typescript +public readonly kind: string; +``` -Gets the YAML representation of cluster-scoped task. +- *Type:* string --- -##### `params`Optional +##### `params`Optional ```typescript public readonly params: ResolverParam[]; @@ -2766,7 +2786,7 @@ public readonly params: ResolverParam[]; --- -##### `resolver`Optional +##### `resolver`Optional ```typescript public readonly resolver: string; @@ -3251,14 +3271,14 @@ Builds a `PipelineRun` using the supplied configuration. ```typescript import { PipelineRunBuilder } from 'cdk8s-pipelines' -new PipelineRunBuilder(scope: Construct, id: string, pipeline: PipelineBuilder) +new PipelineRunBuilder(scope: Construct, id: string, pipeline: IRemoteResolver | PipelineBuilder) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | | scope | constructs.Construct | The `Construct` in which to create the `PipelineRun`. | | id | string | The logical ID of the `PipelineRun` construct. | -| pipeline | PipelineBuilder | The `Pipeline` for which to create this run, using the `PipelineBuilder`. | +| pipeline | IRemoteResolver \| PipelineBuilder | The `Pipeline` for which to create this run, using the `PipelineBuilder` or `IRemoteResolver` for a remote `Pipeline`. | --- @@ -3280,9 +3300,9 @@ The logical ID of the `PipelineRun` construct. ##### `pipeline`Required -- *Type:* PipelineBuilder +- *Type:* IRemoteResolver | PipelineBuilder -The `Pipeline` for which to create this run, using the `PipelineBuilder`. +The `Pipeline` for which to create this run, using the `PipelineBuilder` or `IRemoteResolver` for a remote `Pipeline`. --- @@ -3306,6 +3326,9 @@ public buildPipelineRun(opts?: BuilderOptions): void Builds the `PipelineRun` for the configured `Pipeline` used in the constructor. +If the `PipelineRun` references a remote pipeline, consistency checks for parameters +and workspaces expected by the pipeline are omitted. + ###### `opts`Optional - *Type:* BuilderOptions @@ -3334,6 +3357,7 @@ Adds a run parameter to the `PipelineRun`. It will throw an error if you try to add a parameter that does not exist on the pipeline. +If the `PipelineRun` references a remote pipeline, consistency checks are omitted. ###### `name`Required @@ -3407,34 +3431,34 @@ The sub path on the `persistentVolumeClaim` to use for the `workspace`. -### RemoteTaskRef +### RemoteRef -A remote `Task` reference. +A remote `Task` or `Pipeline` reference. -Will be generated as a `taskRef`. +Generated as `taskRef` or `pipelineRef`, respectively. -#### Initializers +#### Initializers ```typescript -import { RemoteTaskRef } from 'cdk8s-pipelines' +import { RemoteRef } from 'cdk8s-pipelines' -new RemoteTaskRef(resolver: string, params: ResolverParam[]) +new RemoteRef(resolver: string, params: ResolverParam[]) ``` | **Name** | **Type** | **Description** | | --- | --- | --- | -| resolver | string | *No description.* | -| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | +| params | ResolverParam[] | *No description.* | --- -##### `resolver`Required +##### `resolver`Required - *Type:* string --- -##### `params`Required +##### `params`Required - *Type:* ResolverParam[] @@ -3446,12 +3470,12 @@ new RemoteTaskRef(resolver: string, params: ResolverParam[]) | **Name** | **Type** | **Description** | | --- | --- | --- | -| params | ResolverParam[] | *No description.* | -| resolver | string | *No description.* | +| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | --- -##### `params`Optional +##### `params`Optional ```typescript public readonly params: ResolverParam[]; @@ -3461,7 +3485,7 @@ public readonly params: ResolverParam[]; --- -##### `resolver`Optional +##### `resolver`Optional ```typescript public readonly resolver: string; @@ -3535,7 +3559,7 @@ Builds the `Task`. ##### `referencingTask` ```typescript -public referencingTask(task: string | IRemoteTaskResolver): TaskBuilder +public referencingTask(task: string | IRemoteResolver): TaskBuilder ``` Sets the taskRef field of the `Task`. @@ -3545,7 +3569,7 @@ overrides `logicalID as the name of the `Task` in its individual yaml. ###### `task`Required -- *Type:* string | IRemoteTaskResolver +- *Type:* string | IRemoteResolver as string: name of the local task being referenced as IRemoteTaskResolver: resolver for a task in remote location. @@ -3720,7 +3744,7 @@ Adds the specified workspace to the `Task`. | --- | --- | --- | | logicalID | string | *No description.* | | name | string | Gets the name of the `Task` in the context of a pipeline. | -| taskRef | TaskRef \| RemoteTaskRef | Gets the taskRef field of the `Task` for use within a pipeline. | +| taskRef | RemoteRef \| TaskRef | Gets the taskRef field of the `Task` for use within a pipeline. | | description | string | Gets the `description` of the `Task`. | | parameters | ParameterBuilder[] | *No description.* | | runAfter | string[] | Gets the list of task names for the runAfter value of the `Task`. | @@ -3755,10 +3779,10 @@ If not set, the 'Task' id is used. ##### `taskRef`Required ```typescript -public readonly taskRef: TaskRef | RemoteTaskRef; +public readonly taskRef: RemoteRef | TaskRef; ``` -- *Type:* TaskRef | RemoteTaskRef +- *Type:* RemoteRef | TaskRef Gets the taskRef field of the `Task` for use within a pipeline. @@ -4304,34 +4328,45 @@ Gets the name of the workspace. ## Protocols -### IRemoteTaskResolver +### IRemoteResolver -- *Implemented By:* ClusterTaskResolver, IRemoteTaskResolver +- *Implemented By:* ClusterRemoteResolver, IRemoteResolver #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| taskRef | RemoteTaskRef | Gets the taskRef yaml for a remote Task. | -| params | ResolverParam[] | *No description.* | -| resolver | string | *No description.* | +| remoteRef | RemoteRef | Gets the taskRef yaml for a remote Task. | +| kind | string | *No description.* | +| params | ResolverParam[] | *No description.* | +| resolver | string | *No description.* | --- -##### `taskRef`Required +##### `remoteRef`Required ```typescript -public readonly taskRef: RemoteTaskRef; +public readonly remoteRef: RemoteRef; ``` -- *Type:* RemoteTaskRef +- *Type:* RemoteRef Gets the taskRef yaml for a remote Task. --- -##### `params`Optional +##### `kind`Optional + +```typescript +public readonly kind: string; +``` + +- *Type:* string + +--- + +##### `params`Optional ```typescript public readonly params: ResolverParam[]; @@ -4341,7 +4376,7 @@ public readonly params: ResolverParam[]; --- -##### `resolver`Optional +##### `resolver`Optional ```typescript public readonly resolver: string; diff --git a/src/builders.ts b/src/builders.ts index 756cb8c..c1af9ea 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -905,7 +905,7 @@ export class TaskBuilder { this._taskref = { name: task }; } else { if (task.kind != 'task') { - throw new Error(`Remote resource be of kind 'task' in taskRef of Task ${this.name}.`); + throw new Error(`Remote resource must be of kind 'task' in taskRef of Task '${this.name}'.`); } this._taskref = task.remoteRef; } @@ -1376,6 +1376,10 @@ export class PipelineRunBuilder { }); } else { + if (this._pipeline.kind != 'pipeline') { + throw new Error(`Remote resource must be of kind 'pipeline' in pipelineRef of PipelineRun '${this._id}'.`); + } + new PipelineRun(this._scope, this._id, { metadata: { name: this._id, diff --git a/test/__snapshots__/pipelinebuilder.test.ts.snap b/test/__snapshots__/pipelinebuilder.test.ts.snap index 138c8b6..87dd5f0 100644 --- a/test/__snapshots__/pipelinebuilder.test.ts.snap +++ b/test/__snapshots__/pipelinebuilder.test.ts.snap @@ -718,3 +718,68 @@ exports[`PipelineBuilderTest PipelineRunBuilderCustom 1`] = ` }, ] `; + +exports[`PipelineBuilderTest PipelineRunBuilderWithResolver 1`] = ` +[ + { + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": { + "name": "pipeline-admin-default-crb", + "namespace": "default", + }, + "roleRef": { + "kind": "ClusterRole", + "name": "cluster-admin", + }, + "subjects": [ + { + "kind": "ServiceAccount", + "name": "pipeline", + "namespace": "default", + }, + ], + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "PipelineRun", + "metadata": { + "name": "my-pipeline-run", + }, + "spec": { + "params": [ + { + "name": "repo-url", + "value": "https://github.com/exmaple/my-repo", + }, + ], + "pipelineRef": { + "params": [ + { + "name": "name", + "value": "clone-build-push", + }, + { + "name": "namespace", + "value": "default", + }, + { + "name": "kind", + "value": "pipeline", + }, + ], + "resolver": "cluster", + }, + "workspaces": [ + { + "name": "shared-data", + "persistentVolumeClaim": { + "claimName": "dataPVC", + }, + "subPath": "my-shared-data", + }, + ], + }, + }, +] +`; diff --git a/test/pipelinebuilder.test.ts b/test/pipelinebuilder.test.ts index fed1955..10f1d38 100644 --- a/test/pipelinebuilder.test.ts +++ b/test/pipelinebuilder.test.ts @@ -11,7 +11,7 @@ import { fromPipelineParam, constant, createRoleBindingProps, - ClusterTaskResolver, + ClusterRemoteResolver, } from '../src'; class PipelineRunTest extends Chart { @@ -454,7 +454,7 @@ class PipelineTestWithResolver extends Chart { const urlParam = new ParameterBuilder('url') .withValue(fromPipelineParam(pipelineParam)); - const resolver = new ClusterTaskResolver('git-clone', 'default'); + const resolver = new ClusterRemoteResolver('task', 'git-clone', 'default'); const myTask = new TaskBuilder(this, 'fetch-source') .referencingTask(resolver) @@ -470,6 +470,49 @@ class PipelineTestWithResolver extends Chart { } } +class PipelineTestWithResolverError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const resolver = new ClusterRemoteResolver('pipeline', 'git-clone', 'default'); + + const myTask = new TaskBuilder(this, 'fetch-source') + .referencingTask(resolver); + + new PipelineBuilder(this, 'clone-build-push') + .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withTask(myTask) + .buildPipeline({ includeDependencies: true }); + } +} + + +class PipelineRunTestWithResolver extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const resolver = new ClusterRemoteResolver('pipeline', 'clone-build-push', 'default'); + + new PipelineRunBuilder(this, 'my-pipeline-run', resolver) + .withRunParam('repo-url', 'https://github.com/exmaple/my-repo') + .withWorkspace('shared-data', 'dataPVC', 'my-shared-data') + .buildPipelineRun({ includeDependencies: true }); + } +} + +class PipelineRunTestWithResolverError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const resolver = new ClusterRemoteResolver('task', 'clone-build-push', 'default'); + + new PipelineRunBuilder(this, 'my-pipeline-run', resolver) + .withRunParam('repo-url', 'https://github.com/exmaple/my-repo') + .withWorkspace('shared-data', 'dataPVC', 'my-shared-data') + .buildPipelineRun({ includeDependencies: true }); + } +} + describe('PipelineBuilderTest', () => { test('PipelineRunBuilder', () => { const app = Testing.app(); @@ -597,4 +640,27 @@ describe('PipelineBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + + test('PipelineBuilderWithResolverError', () => { + const app = Testing.app(); + const f = () => { + new PipelineTestWithResolverError(app, 'test-chart'); + }; + expect(f).toThrowError('Remote resource must be of kind \'task\' in taskRef of Task \'fetch-source\'.'); + }); + + test('PipelineRunBuilderWithResolver', () => { + const app = Testing.app(); + const chart = new PipelineRunTestWithResolver(app, 'test-chart'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); + + test('PipelineRunBuilderWithResolverError', () => { + const app = Testing.app(); + const f = () => { + new PipelineRunTestWithResolverError(app, 'test-chart'); + }; + expect(f).toThrowError('Remote resource must be of kind \'pipeline\' in pipelineRef of PipelineRun \'my-pipeline-run\'.'); + }); }); From dfe3f2d514ca1ab1edecf50d9546c676c4a678b9 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Tue, 6 Aug 2024 17:30:04 -0400 Subject: [PATCH 24/30] Basic TaskRunBuilder class Signed-off-by: snehajais22 --- src/builders.ts | 117 ++++++++++++++++++++++++++++++++++++++++++++++++ src/tasks.ts | 112 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 229 insertions(+) diff --git a/src/builders.ts b/src/builders.ts index 770f81a..0374c19 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -30,6 +30,9 @@ import { ResolverParam, RemoteTaskRef, TaskRef, + TaskRunWorkspace, + TaskRun, + TaskRunParam, } from './tasks'; const DefaultPipelineServiceAccountName = 'default:pipeline'; @@ -970,6 +973,120 @@ export class TaskBuilder { } } +/** + * Builds a `TaskRun` using the supplied configuration. + * + * @see https://tekton.dev/docs/pipelines/taskruns/ + */ +export class TaskRunBuilder { + private readonly _scope: Construct; + private readonly _id: string; + private readonly _task: TaskBuilder; + private readonly _runParams: TaskRunParam[]; + private readonly _runWorkspaces: TaskRunWorkspace[]; + // private _sa: string; + // private _crbProps: ApiObjectProps; + + /** + * Creates a new instance of the `TaskRunBuilder` for the specified + * `Task` that is built by the `TaskBuilder` supplied here. + * + * A pipeline run is configured only for a specific task, so it did not + * make any sense here to allow the run to be created without the task + * specified. + * + * @param scope The `Construct` in which to create the `TaskRun`. + * @param id The logical ID of the `TaskRun` construct. + * @param pipeline The `Task` for which to create this run, using the `TaskBuilder`. + */ + public constructor(scope: Construct, id: string, task: TaskBuilder) { + this._scope = scope; + this._id = id; + this._task = task; + // this._sa = DefaultPipelineServiceAccountName; + // this._crbProps = DefaultClusterRoleBindingProps; + this._runParams = new Array(); + this._runWorkspaces = new Array(); + } + + /** + * Adds a run parameter to the `TaskRun`. It will throw an error if you try + * to add a parameter that does not exist on the task. + * + * @param name The name of the parameter added to the task run. + * @param value The value of the parameter added to the task run. + */ + public withRunParam(name: string, value: string): TaskRunBuilder { + const params = this._task.parameters!; + const p = params.find((obj) => obj.name === name); + if (p) { + this._runParams.push({ + name: name, + value: value, + }); + } else { + throw new Error(`TaskRun parameter '${name}' does not exist in task '${this._task.logicalID}'`); + } + return this; + } + + /** + * Allows you to specify the name of a `PersistentVolumeClaim` but does not + * do any compile-time validation on the volume claim's name or existence. + * + * @see https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim + * + * @param name The name of the workspace in the `TaskRun` that will be used by the `Task`. + * @param claimName The name of the `PersistentVolumeClaim` to use for the `workspace`. + * @param subPath The sub path on the `persistentVolumeClaim` to use for the `workspace`. + */ + public withWorkspace(name: string, claimName: string, subPath: string): TaskRunBuilder { + this._runWorkspaces.push({ + name: name, + persistentVolumeClaim: { + claimName: claimName, + }, + subPath: subPath, + }); + return this; + } + + /** + * Builds the `TaskRun` for the configured `Task` used in the constructor. + * @param opts + */ + public buildTaskRun(opts: BuilderOptions = DefaultBuilderOptions): void { + const params = this._task.parameters!; + params.forEach((p) => { + const prp = this._runParams.find((obj) => obj.name == p.name); + if (!prp) { + throw new Error(`Task parameter '${p.name}' is not defined in TaskRun '${this._id}'`); + } + }); + + const workspaces: TaskWorkspace[] = this._task.workspaces!; + workspaces.forEach((ws) => { + const pws = this._runWorkspaces.find((obj) => obj.name == ws.name); + if (!pws) { + throw new Error(`Task workspace '${ws.name}' is not defined in TaskRun '${this._id}'`); + } + }); + + new TaskRun(this._scope, this._id, { + metadata: { + name: this._id, + }, + spec: { + taskRef: { + name: this._task.logicalID, + }, + params: this._runParams, + workspaces: this._runWorkspaces, + }, + }); + } +} + /** * */ diff --git a/src/tasks.ts b/src/tasks.ts index f19d209..7e279dc 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -20,6 +20,7 @@ import { ApiObject, ApiObjectMetadata, GroupVersionKind } from 'cdk8s'; import { Construct } from 'constructs'; import { NamedResource, NameKeyPair, TektonV1ApiVersion } from './common'; +import { PersistentVolumeClaimRef } from './pipelines'; /** @@ -302,3 +303,114 @@ export class Task extends ApiObject { }), {}); } } + +/** + * The parameters for a particular `TaskRun`. + */ +export interface TaskRunParam extends NamedResource { + /** + * The value of the parameter in this `TaskRun`. + */ + readonly value: string; +} + +/** + * The `Workspace` configuration for a `TaskRun`. + * + * @see https://tekton.dev/docs/pipelines/taskruns/#specifying-workspaces + */ +export interface TaskRunWorkspace extends NamedResource { + readonly persistentVolumeClaim: PersistentVolumeClaimRef; + readonly subPath: string; +} + +/** + * The details for the `TaskRun`. + * @see https://tekton.dev/docs/pipelines/taskruns/#configuring-a-taskrun + */ +export interface TaskRunSpec { + /** + * Required `Task` reference. + */ + readonly taskRef: TaskRef; + readonly params?: TaskRunParam[]; + readonly workspaces?: TaskRunWorkspace[]; +} + +export interface TaskRunProps { + readonly metadata?: ApiObjectMetadata; + /** + * Specifies the configuration information for this `TaskRun` object. + */ + readonly spec?: TaskRunSpec; + /** + * Specifies a `ServiceAccount` object that supplies specific execution + * credentials for the `Task`. + */ + readonly serviceAccountName?: string; //NOTE should be 'default' if unspecified +} + +/** + * The TaskRun allows you to specify how you want to execute a `Task`. + * + * @see https://tekton.dev/docs/pipelines/taskruns/ + * @schema TaskRun + */ +export class TaskRun extends ApiObject { + + /** + * Returns the apiVersion and kind for "TaskRun" + */ + public static readonly GVK: GroupVersionKind = { + apiVersion: TektonV1ApiVersion, + kind: 'TaskRun', + }; + + /** + * Renders a Kubernetes manifest for `TaskRun`. + * + * This can be used to inline resource manifests inside other objects (e.g. as templates). + * + * @param props initialization props + */ + public static manifest(props: TaskProps = {}): any { + return { + ...TaskRun.GVK, + ...props, + }; + } + + private readonly _metadata?: ApiObjectMetadata; + private readonly _spec?: TaskRunSpec; + + /** + * Defines a `TaskRun` API object + * @param scope the scope in which to define this object + * @param id a scope-local name for the object + * @param props initialization props + */ + public constructor(scope: Construct, id: string, props: TaskRunProps = {}) { + super(scope, id, { + ...TaskRun.GVK, + }); + this._metadata = props.metadata; + this._spec = props.spec; + } + + /** + * Renders the object to Kubernetes JSON. + */ + public toJson(): any { + const result = { + ...TaskRun.GVK, + ...{ + metadata: this._metadata, + spec: this._spec, + }, + }; + return Object.entries(result).reduce((r, i) => (i[1] === undefined) ? r : ({ + ...r, + [i[0]]: i[1], + }), {}); + } +} From 3bdd36620a1c1d02cf37a7f822fd41de2bc9407c Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 7 Aug 2024 10:56:40 -0400 Subject: [PATCH 25/30] Remote Resolvers in TaskRuns Signed-off-by: snehajais22 --- src/builders.ts | 124 ++++++++++++++++++++++++++++++++++-------------- src/tasks.ts | 14 +++--- 2 files changed, 95 insertions(+), 43 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index bed1929..7508c75 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -986,11 +986,11 @@ export class TaskBuilder { export class TaskRunBuilder { private readonly _scope: Construct; private readonly _id: string; - private readonly _task: TaskBuilder; + private readonly _task: TaskBuilder | IRemoteResolver; private readonly _runParams: TaskRunParam[]; private readonly _runWorkspaces: TaskRunWorkspace[]; - // private _sa: string; - // private _crbProps: ApiObjectProps; + private _sa: string; + private _crbProps: ApiObjectProps; /** * Creates a new instance of the `TaskRunBuilder` for the specified @@ -1002,14 +1002,15 @@ export class TaskRunBuilder { * * @param scope The `Construct` in which to create the `TaskRun`. * @param id The logical ID of the `TaskRun` construct. - * @param pipeline The `Task` for which to create this run, using the `TaskBuilder`. + * @param pipeline The `Task` for which to create this run, using the `TaskBuilder` or + * `IRemoteResolver` for a remote `Task`. */ - public constructor(scope: Construct, id: string, task: TaskBuilder) { + public constructor(scope: Construct, id: string, task: TaskBuilder | IRemoteResolver) { this._scope = scope; this._id = id; this._task = task; - // this._sa = DefaultPipelineServiceAccountName; - // this._crbProps = DefaultClusterRoleBindingProps; + this._sa = DefaultPipelineServiceAccountName; + this._crbProps = DefaultClusterRoleBindingProps; this._runParams = new Array(); this._runWorkspaces = new Array(); } @@ -1017,20 +1018,28 @@ export class TaskRunBuilder { /** * Adds a run parameter to the `TaskRun`. It will throw an error if you try * to add a parameter that does not exist on the task. + * If the `TaskRun` references a remote task, consistency checks are omitted. * * @param name The name of the parameter added to the task run. * @param value The value of the parameter added to the task run. */ public withRunParam(name: string, value: string): TaskRunBuilder { - const params = this._task.parameters!; - const p = params.find((obj) => obj.name === name); - if (p) { + if (this._task instanceof TaskBuilder) { + const params = this._task.parameters!; + const p = params.find((obj) => obj.name === name); + if (p) { + this._runParams.push({ + name: name, + value: value, + }); + } else { + throw new Error(`TaskRun parameter '${name}' does not exist in task '${this._task.logicalID}'`); + } + } else { this._runParams.push({ name: name, value: value, }); - } else { - throw new Error(`TaskRun parameter '${name}' does not exist in task '${this._task.logicalID}'`); } return this; } @@ -1056,39 +1065,82 @@ export class TaskRunBuilder { return this; } + public withClusterRoleBindingProps(props: ApiObjectProps): TaskRunBuilder { + this._crbProps = props; + return this; + } + + /** + * Uses the provided role name for the `serviceAccountName` on the + * `TaskRun`. If this method is not called prior to `buildTaskRun()`, + * then the default service account will be used, which is _default:pipeline_. + * + * @param sa The name of the service account (`serviceAccountName`) to use. + */ + public withServiceAccount(sa: string): TaskRunBuilder { + this._sa = sa; + return this; + } + /** * Builds the `TaskRun` for the configured `Task` used in the constructor. + * If the `TaskRun` references a remote task, consistency checks for parameters + * and workspaces expected by the pipeline are omitted. * @param opts */ public buildTaskRun(opts: BuilderOptions = DefaultBuilderOptions): void { - const params = this._task.parameters!; - params.forEach((p) => { - const prp = this._runParams.find((obj) => obj.name == p.name); - if (!prp) { - throw new Error(`Task parameter '${p.name}' is not defined in TaskRun '${this._id}'`); - } - }); + if (opts && opts.includeDependencies) { + // Generate the ClusterRoleBinding document, if configured to do so. + new ApiObject(this._scope, this._crbProps.metadata?.name!, this._crbProps); + } - const workspaces: TaskWorkspace[] = this._task.workspaces!; - workspaces.forEach((ws) => { - const pws = this._runWorkspaces.find((obj) => obj.name == ws.name); - if (!pws) { - throw new Error(`Task workspace '${ws.name}' is not defined in TaskRun '${this._id}'`); + if (this._task instanceof TaskBuilder) { + const params = this._task.parameters!; + params.forEach((p) => { + const prp = this._runParams.find((obj) => obj.name == p.name); + if (!prp) { + throw new Error(`Task parameter '${p.name}' is not defined in TaskRun '${this._id}'`); + } + }); + + const workspaces: TaskWorkspace[] = this._task.workspaces!; + workspaces.forEach((ws) => { + const pws = this._runWorkspaces.find((obj) => obj.name == ws.name); + if (!pws) { + throw new Error(`Task workspace '${ws.name}' is not defined in TaskRun '${this._id}'`); + } + }); + + new TaskRun(this._scope, this._id, { + metadata: { + name: this._id, + }, + spec: { + serviceAccountName: this._sa, + taskRef: { + name: this._task.logicalID, + }, + params: this._runParams, + workspaces: this._runWorkspaces, + }, + }); + } else { + if (this._task.kind != 'task') { + throw new Error(`Remote resource must be of kind 'task' in taskRef of TaskRun '${this._id}'.`); } - }); - new TaskRun(this._scope, this._id, { - metadata: { - name: this._id, - }, - spec: { - taskRef: { - name: this._task.logicalID, + new TaskRun(this._scope, this._id, { + metadata: { + name: this._id, }, - params: this._runParams, - workspaces: this._runWorkspaces, - }, - }); + spec: { + serviceAccountName: this._sa, + taskRef: this._task.remoteRef, + params: this._runParams, + workspaces: this._runWorkspaces, + }, + }); + } } } diff --git a/src/tasks.ts b/src/tasks.ts index 7d7298e..6cbb220 100644 --- a/src/tasks.ts +++ b/src/tasks.ts @@ -19,7 +19,7 @@ */ import { ApiObject, ApiObjectMetadata, GroupVersionKind } from 'cdk8s'; import { Construct } from 'constructs'; -import { NamedResource, NameKeyPair, TektonV1ApiVersion } from './common'; +import { NamedResource, NameKeyPair, TektonV1ApiVersion, RemoteRef } from './common'; import { PersistentVolumeClaimRef } from './pipelines'; @@ -309,9 +309,14 @@ export interface TaskRunSpec { /** * Required `Task` reference. */ - readonly taskRef: TaskRef; + readonly taskRef: TaskRef | RemoteRef; readonly params?: TaskRunParam[]; readonly workspaces?: TaskRunWorkspace[]; + /** + * Specifies a `ServiceAccount` object that supplies specific execution + * credentials for the `Task`. + */ + readonly serviceAccountName?: string; } export interface TaskRunProps { @@ -320,11 +325,6 @@ export interface TaskRunProps { * Specifies the configuration information for this `TaskRun` object. */ readonly spec?: TaskRunSpec; - /** - * Specifies a `ServiceAccount` object that supplies specific execution - * credentials for the `Task`. - */ - readonly serviceAccountName?: string; //NOTE should be 'default' if unspecified } /** From 6c59e6321cf33b66aa294509326bc823d86aea3a Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 7 Aug 2024 10:57:35 -0400 Subject: [PATCH 26/30] TaskRun unit tests & documentation Signed-off-by: snehajais22 --- API.md | 884 ++++++++++++++++++-- test/__snapshots__/taskbuilder.test.ts.snap | 199 +++++ test/taskbuilder.test.ts | 177 +++- 3 files changed, 1169 insertions(+), 91 deletions(-) diff --git a/API.md b/API.md index 4f6b515..c9a8a91 100644 --- a/API.md +++ b/API.md @@ -1237,6 +1237,334 @@ Returns the apiVersion and kind for "Task". --- +### TaskRun + +The TaskRun allows you to specify how you want to execute a `Task`. + +> [https://tekton.dev/docs/pipelines/taskruns/](https://tekton.dev/docs/pipelines/taskruns/) + +#### Initializers + +```typescript +import { TaskRun } from 'cdk8s-pipelines' + +new TaskRun(scope: Construct, id: string, props?: TaskRunProps) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | the scope in which to define this object. | +| id | string | a scope-local name for the object. | +| props | TaskRunProps | initialization props. | + +--- + +##### `scope`Required + +- *Type:* constructs.Construct + +the scope in which to define this object. + +--- + +##### `id`Required + +- *Type:* string + +a scope-local name for the object. + +--- + +##### `props`Optional + +- *Type:* TaskRunProps + +initialization props. + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| toString | Returns a string representation of this construct. | +| addDependency | Create a dependency between this ApiObject and other constructs. | +| addJsonPatch | Applies a set of RFC-6902 JSON-Patch operations to the manifest synthesized for this API object. | +| toJson | Renders the object to Kubernetes JSON. | + +--- + +##### `toString` + +```typescript +public toString(): string +``` + +Returns a string representation of this construct. + +##### `addDependency` + +```typescript +public addDependency(dependencies: IConstruct): void +``` + +Create a dependency between this ApiObject and other constructs. + +These can be other ApiObjects, Charts, or custom. + +###### `dependencies`Required + +- *Type:* constructs.IConstruct + +the dependencies to add. + +--- + +##### `addJsonPatch` + +```typescript +public addJsonPatch(ops: JsonPatch): void +``` + +Applies a set of RFC-6902 JSON-Patch operations to the manifest synthesized for this API object. + +*Example* + +```typescript + kubePod.addJsonPatch(JsonPatch.replace('/spec/enableServiceLinks', true)); +``` + + +###### `ops`Required + +- *Type:* cdk8s.JsonPatch + +The JSON-Patch operations to apply. + +--- + +##### `toJson` + +```typescript +public toJson(): any +``` + +Renders the object to Kubernetes JSON. + +#### Static Functions + +| **Name** | **Description** | +| --- | --- | +| isConstruct | Checks if `x` is a construct. | +| isApiObject | Return whether the given object is an `ApiObject`. | +| of | Returns the `ApiObject` named `Resource` which is a child of the given construct. | +| manifest | Renders a Kubernetes manifest for `TaskRun`. | + +--- + +##### ~~`isConstruct`~~ + +```typescript +import { TaskRun } from 'cdk8s-pipelines' + +TaskRun.isConstruct(x: any) +``` + +Checks if `x` is a construct. + +###### `x`Required + +- *Type:* any + +Any object. + +--- + +##### `isApiObject` + +```typescript +import { TaskRun } from 'cdk8s-pipelines' + +TaskRun.isApiObject(o: any) +``` + +Return whether the given object is an `ApiObject`. + +We do attribute detection since we can't reliably use 'instanceof'. + +###### `o`Required + +- *Type:* any + +The object to check. + +--- + +##### `of` + +```typescript +import { TaskRun } from 'cdk8s-pipelines' + +TaskRun.of(c: IConstruct) +``` + +Returns the `ApiObject` named `Resource` which is a child of the given construct. + +If `c` is an `ApiObject`, it is returned directly. Throws an +exception if the construct does not have a child named `Default` _or_ if +this child is not an `ApiObject`. + +###### `c`Required + +- *Type:* constructs.IConstruct + +The higher-level construct. + +--- + +##### `manifest` + +```typescript +import { TaskRun } from 'cdk8s-pipelines' + +TaskRun.manifest(props?: TaskProps) +``` + +Renders a Kubernetes manifest for `TaskRun`. + +This can be used to inline resource manifests inside other objects (e.g. as templates). + +###### `props`Optional + +- *Type:* TaskProps + +initialization props. + +--- + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| node | constructs.Node | The tree node. | +| apiGroup | string | The group portion of the API version (e.g. `authorization.k8s.io`). | +| apiVersion | string | The object's API version (e.g. `authorization.k8s.io/v1`). | +| chart | cdk8s.Chart | The chart in which this object is defined. | +| kind | string | The object kind. | +| metadata | cdk8s.ApiObjectMetadataDefinition | Metadata associated with this API object. | +| name | string | The name of the API object. | + +--- + +##### `node`Required + +```typescript +public readonly node: Node; +``` + +- *Type:* constructs.Node + +The tree node. + +--- + +##### `apiGroup`Required + +```typescript +public readonly apiGroup: string; +``` + +- *Type:* string + +The group portion of the API version (e.g. `authorization.k8s.io`). + +--- + +##### `apiVersion`Required + +```typescript +public readonly apiVersion: string; +``` + +- *Type:* string + +The object's API version (e.g. `authorization.k8s.io/v1`). + +--- + +##### `chart`Required + +```typescript +public readonly chart: Chart; +``` + +- *Type:* cdk8s.Chart + +The chart in which this object is defined. + +--- + +##### `kind`Required + +```typescript +public readonly kind: string; +``` + +- *Type:* string + +The object kind. + +--- + +##### `metadata`Required + +```typescript +public readonly metadata: ApiObjectMetadataDefinition; +``` + +- *Type:* cdk8s.ApiObjectMetadataDefinition + +Metadata associated with this API object. + +--- + +##### `name`Required + +```typescript +public readonly name: string; +``` + +- *Type:* string + +The name of the API object. + +If a name is specified in `metadata.name` this will be the name returned. +Otherwise, a name will be generated by calling +`Chart.of(this).generatedObjectName(this)`, which by default uses the +construct path to generate a DNS-compatible name for the resource. + +--- + +#### Constants + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| GVK | cdk8s.GroupVersionKind | Returns the apiVersion and kind for "TaskRun". | + +--- + +##### `GVK`Required + +```typescript +public readonly GVK: GroupVersionKind; +``` + +- *Type:* cdk8s.GroupVersionKind + +Returns the apiVersion and kind for "TaskRun". + +--- + ## Structs ### BuilderOptions @@ -1947,80 +2275,284 @@ public readonly refParams: PipelineParam[]; --- -##### `refWorkspaces`Optional +##### `refWorkspaces`Optional + +```typescript +public readonly refWorkspaces: PipelineTaskWorkspace[]; +``` + +- *Type:* PipelineTaskWorkspace[] + +--- + +### PipelineTaskWorkspace + +#### Initializer + +```typescript +import { PipelineTaskWorkspace } from 'cdk8s-pipelines' + +const pipelineTaskWorkspace: PipelineTaskWorkspace = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| name | string | *No description.* | +| workspace | string | *No description.* | + +--- + +##### `name`Optional + +```typescript +public readonly name: string; +``` + +- *Type:* string + +--- + +##### `workspace`Optional + +```typescript +public readonly workspace: string; +``` + +- *Type:* string + +--- + +### PipelineWorkspace + +A workspace for a pipeline. + +See https://tekton.dev/docs/pipelines/pipelines/#specifying-workspaces +and https://tekton.dev/docs/pipelines/workspaces/#using-workspaces-in-pipelines. + +#### Initializer + +```typescript +import { PipelineWorkspace } from 'cdk8s-pipelines' + +const pipelineWorkspace: PipelineWorkspace = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| name | string | *No description.* | +| description | string | The description of the workspace. | + +--- + +##### `name`Optional + +```typescript +public readonly name: string; +``` + +- *Type:* string + +--- + +##### `description`Optional + +```typescript +public readonly description: string; +``` + +- *Type:* string + +The description of the workspace. + +--- + +### ResolverParam + +A Resolver parameter value. + +#### Initializer + +```typescript +import { ResolverParam } from 'cdk8s-pipelines' + +const resolverParam: ResolverParam = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| name | string | *No description.* | +| value | string | The value of the resolver parameter. | + +--- + +##### `name`Optional + +```typescript +public readonly name: string; +``` + +- *Type:* string + +--- + +##### `value`Optional + +```typescript +public readonly value: string; +``` + +- *Type:* string + +The value of the resolver parameter. + +--- + +### TaskEnvValueSource + +The source for a `env` `valueFrom`. + +#### Initializer + +```typescript +import { TaskEnvValueSource } from 'cdk8s-pipelines' + +const taskEnvValueSource: TaskEnvValueSource = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| secretKeyRef | NameKeyPair | *No description.* | + +--- + +##### `secretKeyRef`Required + +```typescript +public readonly secretKeyRef: NameKeyPair; +``` + +- *Type:* NameKeyPair + +--- + +### TaskParam + +A Task parameter value. + +#### Initializer + +```typescript +import { TaskParam } from 'cdk8s-pipelines' + +const taskParam: TaskParam = { ... } +``` + +#### Properties + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| name | string | *No description.* | +| value | string | The value of the task parameter. | + +--- + +##### `name`Optional + +```typescript +public readonly name: string; +``` + +- *Type:* string + +--- + +##### `value`Optional ```typescript -public readonly refWorkspaces: PipelineTaskWorkspace[]; +public readonly value: string; ``` -- *Type:* PipelineTaskWorkspace[] +- *Type:* string + +The value of the task parameter. --- -### PipelineTaskWorkspace +### TaskProps -#### Initializer +Properties used to create the Task. + +#### Initializer ```typescript -import { PipelineTaskWorkspace } from 'cdk8s-pipelines' +import { TaskProps } from 'cdk8s-pipelines' -const pipelineTaskWorkspace: PipelineTaskWorkspace = { ... } +const taskProps: TaskProps = { ... } ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| name | string | *No description.* | -| workspace | string | *No description.* | +| metadata | cdk8s.ApiObjectMetadata | The object [metadata](https://kubernetes.io/docs/concepts/overview/working-with-objects/#required-fields) that conforms to standard Kubernetes metadata. | +| spec | TaskSpec | The `spec` is the configuration of the `Task` object. | --- -##### `name`Optional +##### `metadata`Optional ```typescript -public readonly name: string; +public readonly metadata: ApiObjectMetadata; ``` -- *Type:* string +- *Type:* cdk8s.ApiObjectMetadata + +The object [metadata](https://kubernetes.io/docs/concepts/overview/working-with-objects/#required-fields) that conforms to standard Kubernetes metadata. --- -##### `workspace`Optional +##### `spec`Optional ```typescript -public readonly workspace: string; +public readonly spec: TaskSpec; ``` -- *Type:* string +- *Type:* TaskSpec ---- +The `spec` is the configuration of the `Task` object. -### PipelineWorkspace +--- -A workspace for a pipeline. +### TaskRunParam -See https://tekton.dev/docs/pipelines/pipelines/#specifying-workspaces -and https://tekton.dev/docs/pipelines/workspaces/#using-workspaces-in-pipelines. +The parameters for a particular `TaskRun`. -#### Initializer +#### Initializer ```typescript -import { PipelineWorkspace } from 'cdk8s-pipelines' +import { TaskRunParam } from 'cdk8s-pipelines' -const pipelineWorkspace: PipelineWorkspace = { ... } +const taskRunParam: TaskRunParam = { ... } ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| name | string | *No description.* | -| description | string | The description of the workspace. | +| name | string | *No description.* | +| value | string | The value of the parameter in this `TaskRun`. | --- -##### `name`Optional +##### `name`Optional ```typescript public readonly name: string; @@ -2030,176 +2562,179 @@ public readonly name: string; --- -##### `description`Optional +##### `value`Required ```typescript -public readonly description: string; +public readonly value: string; ``` - *Type:* string -The description of the workspace. +The value of the parameter in this `TaskRun`. --- -### ResolverParam - -A Resolver parameter value. +### TaskRunProps -#### Initializer +#### Initializer ```typescript -import { ResolverParam } from 'cdk8s-pipelines' +import { TaskRunProps } from 'cdk8s-pipelines' -const resolverParam: ResolverParam = { ... } +const taskRunProps: TaskRunProps = { ... } ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| name | string | *No description.* | -| value | string | The value of the resolver parameter. | +| metadata | cdk8s.ApiObjectMetadata | *No description.* | +| spec | TaskRunSpec | Specifies the configuration information for this `TaskRun` object. | --- -##### `name`Optional +##### `metadata`Optional ```typescript -public readonly name: string; +public readonly metadata: ApiObjectMetadata; ``` -- *Type:* string +- *Type:* cdk8s.ApiObjectMetadata --- -##### `value`Optional +##### `spec`Optional ```typescript -public readonly value: string; +public readonly spec: TaskRunSpec; ``` -- *Type:* string +- *Type:* TaskRunSpec -The value of the resolver parameter. +Specifies the configuration information for this `TaskRun` object. --- -### TaskEnvValueSource +### TaskRunSpec -The source for a `env` `valueFrom`. +The details for the `TaskRun`. -#### Initializer +> [https://tekton.dev/docs/pipelines/taskruns/#configuring-a-taskrun](https://tekton.dev/docs/pipelines/taskruns/#configuring-a-taskrun) + +#### Initializer ```typescript -import { TaskEnvValueSource } from 'cdk8s-pipelines' +import { TaskRunSpec } from 'cdk8s-pipelines' -const taskEnvValueSource: TaskEnvValueSource = { ... } +const taskRunSpec: TaskRunSpec = { ... } ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| secretKeyRef | NameKeyPair | *No description.* | +| taskRef | RemoteRef \| TaskRef | Required `Task` reference. | +| params | TaskRunParam[] | *No description.* | +| serviceAccountName | string | Specifies a `ServiceAccount` object that supplies specific execution credentials for the `Task`. | +| workspaces | TaskRunWorkspace[] | *No description.* | --- -##### `secretKeyRef`Required +##### `taskRef`Required ```typescript -public readonly secretKeyRef: NameKeyPair; +public readonly taskRef: RemoteRef | TaskRef; ``` -- *Type:* NameKeyPair - ---- +- *Type:* RemoteRef | TaskRef -### TaskParam +Required `Task` reference. -A Task parameter value. +--- -#### Initializer +##### `params`Optional ```typescript -import { TaskParam } from 'cdk8s-pipelines' - -const taskParam: TaskParam = { ... } +public readonly params: TaskRunParam[]; ``` -#### Properties - -| **Name** | **Type** | **Description** | -| --- | --- | --- | -| name | string | *No description.* | -| value | string | The value of the task parameter. | +- *Type:* TaskRunParam[] --- -##### `name`Optional +##### `serviceAccountName`Optional ```typescript -public readonly name: string; +public readonly serviceAccountName: string; ``` - *Type:* string +Specifies a `ServiceAccount` object that supplies specific execution credentials for the `Task`. + --- -##### `value`Optional +##### `workspaces`Optional ```typescript -public readonly value: string; +public readonly workspaces: TaskRunWorkspace[]; ``` -- *Type:* string - -The value of the task parameter. +- *Type:* TaskRunWorkspace[] --- -### TaskProps +### TaskRunWorkspace -Properties used to create the Task. +The `Workspace` configuration for a `TaskRun`. -#### Initializer +> [https://tekton.dev/docs/pipelines/taskruns/#specifying-workspaces](https://tekton.dev/docs/pipelines/taskruns/#specifying-workspaces) + +#### Initializer ```typescript -import { TaskProps } from 'cdk8s-pipelines' +import { TaskRunWorkspace } from 'cdk8s-pipelines' -const taskProps: TaskProps = { ... } +const taskRunWorkspace: TaskRunWorkspace = { ... } ``` #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| metadata | cdk8s.ApiObjectMetadata | The object [metadata](https://kubernetes.io/docs/concepts/overview/working-with-objects/#required-fields) that conforms to standard Kubernetes metadata. | -| spec | TaskSpec | The `spec` is the configuration of the `Task` object. | +| name | string | *No description.* | +| persistentVolumeClaim | PersistentVolumeClaimRef | *No description.* | +| subPath | string | *No description.* | --- -##### `metadata`Optional +##### `name`Optional ```typescript -public readonly metadata: ApiObjectMetadata; +public readonly name: string; ``` -- *Type:* cdk8s.ApiObjectMetadata - -The object [metadata](https://kubernetes.io/docs/concepts/overview/working-with-objects/#required-fields) that conforms to standard Kubernetes metadata. +- *Type:* string --- -##### `spec`Optional +##### `persistentVolumeClaim`Required ```typescript -public readonly spec: TaskSpec; +public readonly persistentVolumeClaim: PersistentVolumeClaimRef; ``` -- *Type:* TaskSpec +- *Type:* PersistentVolumeClaimRef -The `spec` is the configuration of the `Task` object. +--- + +##### `subPath`Required + +```typescript +public readonly subPath: string; +``` + +- *Type:* string --- @@ -3884,6 +4419,175 @@ public readonly name: string; --- +### TaskRunBuilder + +Builds a `TaskRun` using the supplied configuration. + +> [https://tekton.dev/docs/pipelines/taskruns/](https://tekton.dev/docs/pipelines/taskruns/) + +#### Initializers + +```typescript +import { TaskRunBuilder } from 'cdk8s-pipelines' + +new TaskRunBuilder(scope: Construct, id: string, task: IRemoteResolver | TaskBuilder) +``` + +| **Name** | **Type** | **Description** | +| --- | --- | --- | +| scope | constructs.Construct | The `Construct` in which to create the `TaskRun`. | +| id | string | The logical ID of the `TaskRun` construct. | +| task | IRemoteResolver \| TaskBuilder | *No description.* | + +--- + +##### `scope`Required + +- *Type:* constructs.Construct + +The `Construct` in which to create the `TaskRun`. + +--- + +##### `id`Required + +- *Type:* string + +The logical ID of the `TaskRun` construct. + +--- + +##### `task`Required + +- *Type:* IRemoteResolver | TaskBuilder + +--- + +#### Methods + +| **Name** | **Description** | +| --- | --- | +| buildTaskRun | Builds the `TaskRun` for the configured `Task` used in the constructor. | +| withClusterRoleBindingProps | *No description.* | +| withRunParam | Adds a run parameter to the `TaskRun`. | +| withServiceAccount | Uses the provided role name for the `serviceAccountName` on the `TaskRun`. | +| withWorkspace | Allows you to specify the name of a `PersistentVolumeClaim` but does not do any compile-time validation on the volume claim's name or existence. | + +--- + +##### `buildTaskRun` + +```typescript +public buildTaskRun(opts?: BuilderOptions): void +``` + +Builds the `TaskRun` for the configured `Task` used in the constructor. + +If the `TaskRun` references a remote task, consistency checks for parameters +and workspaces expected by the pipeline are omitted. + +###### `opts`Optional + +- *Type:* BuilderOptions + +--- + +##### `withClusterRoleBindingProps` + +```typescript +public withClusterRoleBindingProps(props: ApiObjectProps): TaskRunBuilder +``` + +###### `props`Required + +- *Type:* cdk8s.ApiObjectProps + +--- + +##### `withRunParam` + +```typescript +public withRunParam(name: string, value: string): TaskRunBuilder +``` + +Adds a run parameter to the `TaskRun`. + +It will throw an error if you try +to add a parameter that does not exist on the task. +If the `TaskRun` references a remote task, consistency checks are omitted. + +###### `name`Required + +- *Type:* string + +The name of the parameter added to the task run. + +--- + +###### `value`Required + +- *Type:* string + +The value of the parameter added to the task run. + +--- + +##### `withServiceAccount` + +```typescript +public withServiceAccount(sa: string): TaskRunBuilder +``` + +Uses the provided role name for the `serviceAccountName` on the `TaskRun`. + +If this method is not called prior to `buildTaskRun()`, +then the default service account will be used, which is _default:pipeline_. + +###### `sa`Required + +- *Type:* string + +The name of the service account (`serviceAccountName`) to use. + +--- + +##### `withWorkspace` + +```typescript +public withWorkspace(name: string, claimName: string, subPath: string): TaskRunBuilder +``` + +Allows you to specify the name of a `PersistentVolumeClaim` but does not do any compile-time validation on the volume claim's name or existence. + +> [https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim](https://kubernetes.io/docs/tasks/configure-pod-container/configure-persistent-volume-storage/#create-a-persistentvolumeclaim) + +###### `name`Required + +- *Type:* string + +The name of the workspace in the `TaskRun` that will be used by the `Task`. + +--- + +###### `claimName`Required + +- *Type:* string + +The name of the `PersistentVolumeClaim` to use for the `workspace`. + +--- + +###### `subPath`Required + +- *Type:* string + +The sub path on the `persistentVolumeClaim` to use for the `workspace`. + +--- + + + + ### TaskStepBuilder Creates a `Step` in a `Task`. diff --git a/test/__snapshots__/taskbuilder.test.ts.snap b/test/__snapshots__/taskbuilder.test.ts.snap index 663a4a1..e3c5140 100644 --- a/test/__snapshots__/taskbuilder.test.ts.snap +++ b/test/__snapshots__/taskbuilder.test.ts.snap @@ -1,5 +1,93 @@ // Jest Snapshot v1, https://goo.gl/fbAQLP +exports[`TaskBuilderTest CustomTaskRunBuilder 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "Task", + "metadata": { + "annotations": undefined, + "labels": undefined, + "name": "echo-input", + }, + "spec": { + "description": undefined, + "params": [ + { + "default": "", + "description": "", + "name": "input", + }, + ], + "results": [], + "steps": [ + { + "env": undefined, + "image": "ubuntu", + "name": "step", + "script": "#!/usr/bin/env bash +echo $(params.input)", + "workingDir": undefined, + }, + ], + "workspaces": [ + { + "description": "The files cloned by the task", + "name": "output", + }, + ], + }, + }, + { + "apiVersion": "rbac.authorization.k8s.io/v1", + "kind": "ClusterRoleBinding", + "metadata": { + "name": "task-admin-default-crb", + "namespace": "default", + }, + "roleRef": { + "kind": "ClusterRole", + "name": "cluster-admin", + }, + "subjects": [ + { + "kind": "ServiceAccount", + "name": "default", + "namespace": "default", + }, + ], + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "TaskRun", + "metadata": { + "name": "echo-input-run", + }, + "spec": { + "params": [ + { + "name": "input", + "value": "Hello World!", + }, + ], + "serviceAccountName": "default:default", + "taskRef": { + "name": "echo-input", + }, + "workspaces": [ + { + "name": "output", + "persistentVolumeClaim": { + "claimName": "myPVC", + }, + "subPath": "", + }, + ], + }, + }, +] +`; + exports[`TaskBuilderTest ObjectTaskBuilder 1`] = ` [ { @@ -240,6 +328,117 @@ exports[`TaskBuilderTest TaskBuilderBasic 1`] = ` ] `; +exports[`TaskBuilderTest TaskRunBuilder 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "Task", + "metadata": { + "annotations": undefined, + "labels": undefined, + "name": "echo-input", + }, + "spec": { + "description": undefined, + "params": [ + { + "default": "", + "description": "", + "name": "input", + }, + ], + "results": [], + "steps": [ + { + "env": undefined, + "image": "ubuntu", + "name": "step", + "script": "#!/usr/bin/env bash +echo $(params.input)", + "workingDir": undefined, + }, + ], + "workspaces": [ + { + "description": "The files cloned by the task", + "name": "output", + }, + ], + }, + }, + { + "apiVersion": "tekton.dev/v1", + "kind": "TaskRun", + "metadata": { + "name": "echo-input-run", + }, + "spec": { + "params": [ + { + "name": "input", + "value": "Hello World!", + }, + ], + "serviceAccountName": "default:pipeline", + "taskRef": { + "name": "echo-input", + }, + "workspaces": [ + { + "name": "output", + "persistentVolumeClaim": { + "claimName": "myPVC", + }, + "subPath": "", + }, + ], + }, + }, +] +`; + +exports[`TaskBuilderTest TaskRunResolver 1`] = ` +[ + { + "apiVersion": "tekton.dev/v1", + "kind": "TaskRun", + "metadata": { + "name": "git-clone-run", + }, + "spec": { + "params": [], + "serviceAccountName": "default:pipeline", + "taskRef": { + "params": [ + { + "name": "name", + "value": "git-clone", + }, + { + "name": "namespace", + "value": "default", + }, + { + "name": "kind", + "value": "task", + }, + ], + "resolver": "cluster", + }, + "workspaces": [ + { + "name": "output", + "persistentVolumeClaim": { + "claimName": "myPVC", + }, + "subPath": "", + }, + ], + }, + }, +] +`; + exports[`TaskBuilderTest TestIBMCloudSecretsManagerGet 1`] = ` [ { diff --git a/test/taskbuilder.test.ts b/test/taskbuilder.test.ts index 92fd580..03ca263 100644 --- a/test/taskbuilder.test.ts +++ b/test/taskbuilder.test.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import { Chart, Testing } from 'cdk8s'; import { ChartProps } from 'cdk8s/lib/chart'; import { Construct } from 'constructs'; -import { usingBuildParameter, usingWorkspacePath, secretKeyRef, TaskBuilder, TaskStepBuilder, valueFrom, WorkspaceBuilder, ParameterBuilder, fromPipelineParam } from '../src'; +import { usingBuildParameter, usingWorkspacePath, secretKeyRef, TaskBuilder, TaskStepBuilder, valueFrom, WorkspaceBuilder, ParameterBuilder, fromPipelineParam, TaskRunBuilder, createRoleBindingProps, ClusterRemoteResolver } from '../src'; /** * Using "ansible-runner" as the reference task that I want this test builder to @@ -195,6 +195,135 @@ class TestPullRequestTaskBuild extends Chart { } +class TestTaskRunBuilder extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'echo-input') + .withWorkspace(new WorkspaceBuilder('output') + .withDescription('The files cloned by the task')) + .withStringParam(new ParameterBuilder('input')) + .withStep(new TaskStepBuilder() + .withName('step') + .withImage('ubuntu') + .fromScriptData('#!/usr/bin/env bash\necho $(params.input)')); + myTask.buildTask(); + + new TaskRunBuilder(this, 'echo-input-run', myTask) + .withRunParam('input', 'Hello World!') + .withWorkspace('output', 'myPVC', '') + .buildTaskRun(); + } +} + +class TestCustomTaskRunBuilder extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'echo-input') + .withWorkspace(new WorkspaceBuilder('output') + .withDescription('The files cloned by the task')) + .withStringParam(new ParameterBuilder('input')) + .withStep(new TaskStepBuilder() + .withName('step') + .withImage('ubuntu') + .fromScriptData('#!/usr/bin/env bash\necho $(params.input)')); + myTask.buildTask(); + + const CRB = createRoleBindingProps( + 'task-admin-default-crb', + 'default', + 'cluster-admin', + 'default', + 'default'); + + const serviceAccount = 'default:default'; + + new TaskRunBuilder(this, 'echo-input-run', myTask) + .withRunParam('input', 'Hello World!') + .withWorkspace('output', 'myPVC', '') + .withClusterRoleBindingProps(CRB) + .withServiceAccount(serviceAccount) + .buildTaskRun({ includeDependencies: true }); + } +} + +class TestTaskRunBuilderParamError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'echo-hello') + .withStep(new TaskStepBuilder() + .withName('step') + .withImage('ubuntu') + .fromScriptData('#!/usr/bin/env bash\necho Hello')); + myTask.buildTask(); + + new TaskRunBuilder(this, 'echo-input-run', myTask) + .withRunParam('input', 'Hello World!') + .buildTaskRun(); + } +} + +class TestTaskRunBuilderParamError2 extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'echo-input') + .withStringParam(new ParameterBuilder('input')) + .withStep(new TaskStepBuilder() + .withName('step') + .withImage('ubuntu') + .fromScriptData('#!/usr/bin/env bash\necho $(params.input)')); + myTask.buildTask(); + + new TaskRunBuilder(this, 'echo-input-run', myTask) + .buildTaskRun(); + } +} + +class TestTaskRunBuilderWorkspaceError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const myTask = new TaskBuilder(this, 'echo-input') + .withWorkspace(new WorkspaceBuilder('output') + .withDescription('The files cloned by the task')) + .withStep(new TaskStepBuilder() + .withName('step') + .withImage('ubuntu') + .fromScriptData('#!/usr/bin/env bash\necho $(params.input)')); + myTask.buildTask(); + + new TaskRunBuilder(this, 'echo-input-run', myTask) + .buildTaskRun(); + } +} + +class TestTaskRunBuilderResolver extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const resolver = new ClusterRemoteResolver('task', 'git-clone', 'default'); + + new TaskRunBuilder(this, 'git-clone-run', resolver) + .withWorkspace('output', 'myPVC', '') + .buildTaskRun(); + } +} + +class TestTaskRunBuilderResolverError extends Chart { + constructor(scope: Construct, id: string, props?: ChartProps) { + super(scope, id, props); + + const resolver = new ClusterRemoteResolver('pipeline', 'git-clone', 'default'); + + new TaskRunBuilder(this, 'git-clone-run', resolver) + .withWorkspace('output', 'myPVC', '') + .buildTaskRun(); + } +} + describe('TaskBuilderTest', () => { test('TaskBuilderBasic', () => { const app = Testing.app(); @@ -226,4 +355,50 @@ describe('TaskBuilderTest', () => { const results = Testing.synth(chart); expect(results).toMatchSnapshot(); }); + test('TaskRunBuilder', () => { + const app = Testing.app(); + const chart = new TestTaskRunBuilder(app, 'taskrun'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); + test('CustomTaskRunBuilder', () => { + const app = Testing.app(); + const chart = new TestCustomTaskRunBuilder(app, 'custom-taskrun'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); + test('TaskRunExtraParamError', () => { + const app = Testing.app(); + const f = () => { + new TestTaskRunBuilderParamError(app, 'extra-param'); + }; + expect(f).toThrowError('TaskRun parameter \'input\' does not exist in task \'echo-hello\''); + }); + test('TaskRunMissingParamError', () => { + const app = Testing.app(); + const f = () => { + new TestTaskRunBuilderParamError2(app, 'missing-param'); + }; + expect(f).toThrowError('Task parameter \'input\' is not defined in TaskRun \'echo-input-run\''); + }); + test('TaskRunMissingWorkspaceError', () => { + const app = Testing.app(); + const f = () => { + new TestTaskRunBuilderWorkspaceError(app, 'missing-workspace'); + }; + expect(f).toThrowError('Task workspace \'output\' is not defined in TaskRun \'echo-input-run\''); + }); + test('TaskRunResolver', () => { + const app = Testing.app(); + const chart = new TestTaskRunBuilderResolver(app, 'resolver'); + const results = Testing.synth(chart); + expect(results).toMatchSnapshot(); + }); + test('TaskRunResolverError', () => { + const app = Testing.app(); + const f = () => { + new TestTaskRunBuilderResolverError(app, 'resolver-error'); + }; + expect(f).toThrowError('Remote resource must be of kind \'task\' in taskRef of TaskRun \'git-clone-run\''); + }); }); From 1fc54a37f67e6b328a66f3947ea111625aad652e Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Wed, 7 Aug 2024 12:07:17 -0400 Subject: [PATCH 27/30] Update default TaskRun serviceAccountName Signed-off-by: snehajais22 --- src/builders.ts | 3 ++- test/__snapshots__/taskbuilder.test.ts.snap | 6 +++--- test/taskbuilder.test.ts | 2 +- 3 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/builders.ts b/src/builders.ts index 7508c75..4d67de9 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -34,6 +34,7 @@ import { } from './tasks'; const DefaultPipelineServiceAccountName = 'default:pipeline'; +const DefaultTaskServiceAccountName = 'default'; /** * Creates the properties for a `ClusterRoleBinding` @@ -1009,7 +1010,7 @@ export class TaskRunBuilder { this._scope = scope; this._id = id; this._task = task; - this._sa = DefaultPipelineServiceAccountName; + this._sa = DefaultTaskServiceAccountName; this._crbProps = DefaultClusterRoleBindingProps; this._runParams = new Array(); this._runWorkspaces = new Array(); diff --git a/test/__snapshots__/taskbuilder.test.ts.snap b/test/__snapshots__/taskbuilder.test.ts.snap index e3c5140..60fb6b9 100644 --- a/test/__snapshots__/taskbuilder.test.ts.snap +++ b/test/__snapshots__/taskbuilder.test.ts.snap @@ -70,7 +70,7 @@ echo $(params.input)", "value": "Hello World!", }, ], - "serviceAccountName": "default:default", + "serviceAccountName": "default", "taskRef": { "name": "echo-input", }, @@ -379,7 +379,7 @@ echo $(params.input)", "value": "Hello World!", }, ], - "serviceAccountName": "default:pipeline", + "serviceAccountName": "default", "taskRef": { "name": "echo-input", }, @@ -407,7 +407,7 @@ exports[`TaskBuilderTest TaskRunResolver 1`] = ` }, "spec": { "params": [], - "serviceAccountName": "default:pipeline", + "serviceAccountName": "default", "taskRef": { "params": [ { diff --git a/test/taskbuilder.test.ts b/test/taskbuilder.test.ts index 03ca263..0e29fa8 100644 --- a/test/taskbuilder.test.ts +++ b/test/taskbuilder.test.ts @@ -237,7 +237,7 @@ class TestCustomTaskRunBuilder extends Chart { 'default', 'default'); - const serviceAccount = 'default:default'; + const serviceAccount = 'default'; new TaskRunBuilder(this, 'echo-input-run', myTask) .withRunParam('input', 'Hello World!') From a82a4b2e3f52eaa0fb8b2f176b7f26834918efdc Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 9 Aug 2024 12:57:05 -0400 Subject: [PATCH 28/30] Updated code examples in docs and method specs Signed-off-by: snehajais22 --- API.md | 47 +++++++++++++++++++++++++++++------------------ README.md | 35 ++++++++++++++++++++--------------- src/builders.ts | 13 +++++++++---- 3 files changed, 58 insertions(+), 37 deletions(-) diff --git a/API.md b/API.md index 4f6b515..3fbf21a 100644 --- a/API.md +++ b/API.md @@ -85,13 +85,16 @@ export class MyChart extends Chart { constructor(scope: Construct, id: string, props: ChartProps = { }) { super(scope, id, props); - new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + const pipelineParam = new ParameterBuilder('repo-url'); + + new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(new TaskBuilder(this, 'fetch-source') - .withName('git-clone') - .withWorkspace(new WorkspaceBuilder('output').withName('task-output')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('url').withDescription('the URL for the thing'))) + .withTask(new TaskBuilder(this, 'git-clone') + .withName('fetch-source') + .withWorkspace(new WorkspaceBuilder('output').withBinding('task-output')) + .withStringParam(new ParameterBuilder('url') + .withValue(fromPipelineParam(pipelineParam)) + .withDescription('the URL for the thing'))) .buildPipeline(); } } @@ -157,7 +160,7 @@ resources to create a Pipeline that closely matches the [example here](https://tekton.dev/docs/how-to-guides/kaniko-build-push/): ```typescript -new Pipeline(this, 'test-pipeline', { +new Pipeline(this, 'clone-build-push', { metadata: { name: 'clone-build-push', }, @@ -204,14 +207,16 @@ made for you automatically. Here is the same construct, but defined using the `PipelineBuilder`. ```typescript -new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') +const param = new ParameterBuilder('repo-url'); + +new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(new PipelineTaskBuilder() - .withName('fetch-source') - .withTaskReference('git-clone') - .withWorkspace('output', 'shared-data', 'The files cloned by the task') - .withStringParam('url', 'repo-url', '$(params.repo-url)')) + .withStringParam(param) + .withTask(new TaskBuilder(this, 'task-id') + .withName('fetch-source') + .referencingTask('git-clone') + .withWorkspace(new WorkspaceBuilder('output').withBinding('shared-data')) + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(param)))) .buildPipeline(); ``` @@ -219,7 +224,7 @@ The `build` method on the builders will validate the parameters and, if the object is valid, will create the construct, making sure to add `workspace` and `param` resources to the Task as well as the -Any resources that the `task` requires that needs to be defined at the `pipeline` +Any resources that the `task` requires that needs to be defined at the `pipeline` level. ## Related projects @@ -2699,7 +2704,7 @@ public readonly logicalID: string; - *Implements:* IRemoteResolver -Resolves the provided cluster-scoped task into yaml for the taskRef field. +Resolves the provided cluster-scoped `Task` or `Pipeline` into yaml for the taskRef or pipelineRef field, respectively. #### Initializers @@ -2948,6 +2953,8 @@ Sets the value for the parameter. - *Type:* string | IValueResolver +string value or ValueResolver for pipeline-level parameter. + --- @@ -4332,12 +4339,16 @@ Gets the name of the workspace. - *Implemented By:* ClusterRemoteResolver, IRemoteResolver +Resolves remote tasks or pipelines through different means. + +Can be implemented by user for git, hub, bundle, etc resolvers. + #### Properties | **Name** | **Type** | **Description** | | --- | --- | --- | -| remoteRef | RemoteRef | Gets the taskRef yaml for a remote Task. | +| remoteRef | RemoteRef | Gets the yaml reference for a remote object. | | kind | string | *No description.* | | params | ResolverParam[] | *No description.* | | resolver | string | *No description.* | @@ -4352,7 +4363,7 @@ public readonly remoteRef: RemoteRef; - *Type:* RemoteRef -Gets the taskRef yaml for a remote Task. +Gets the yaml reference for a remote object. --- diff --git a/README.md b/README.md index 675cec4..c34f9a7 100644 --- a/README.md +++ b/README.md @@ -85,13 +85,16 @@ export class MyChart extends Chart { constructor(scope: Construct, id: string, props: ChartProps = { }) { super(scope, id, props); - new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') + const pipelineParam = new ParameterBuilder('repo-url'); + + new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(new TaskBuilder(this, 'fetch-source') - .withName('git-clone') - .withWorkspace(new WorkspaceBuilder('output').withName('task-output')) - .withStringParam(new ParameterBuilder('url').withPiplineParameter('url').withDescription('the URL for the thing'))) + .withTask(new TaskBuilder(this, 'git-clone') + .withName('fetch-source') + .withWorkspace(new WorkspaceBuilder('output').withBinding('task-output')) + .withStringParam(new ParameterBuilder('url') + .withValue(fromPipelineParam(pipelineParam)) + .withDescription('the URL for the thing'))) .buildPipeline(); } } @@ -157,7 +160,7 @@ resources to create a Pipeline that closely matches the [example here](https://tekton.dev/docs/how-to-guides/kaniko-build-push/): ```typescript -new Pipeline(this, 'test-pipeline', { +new Pipeline(this, 'clone-build-push', { metadata: { name: 'clone-build-push', }, @@ -204,14 +207,16 @@ made for you automatically. Here is the same construct, but defined using the `PipelineBuilder`. ```typescript -new PipelineBuilder(this, 'my-pipeline') - .withName('clone-build-push') +const param = new ParameterBuilder('repo-url'); + +new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') - .withTask(new PipelineTaskBuilder() - .withName('fetch-source') - .withTaskReference('git-clone') - .withWorkspace('output', 'shared-data', 'The files cloned by the task') - .withStringParam('url', 'repo-url', '$(params.repo-url)')) + .withStringParam(param) + .withTask(new TaskBuilder(this, 'task-id') + .withName('fetch-source') + .referencingTask('git-clone') + .withWorkspace(new WorkspaceBuilder('output').withBinding('shared-data')) + .withStringParam(new ParameterBuilder('url').withValue(fromPipelineParam(param)))) .buildPipeline(); ``` @@ -219,7 +224,7 @@ The `build` method on the builders will validate the parameters and, if the object is valid, will create the construct, making sure to add `workspace` and `param` resources to the Task as well as the -Any resources that the `task` requires that needs to be defined at the `pipeline` +Any resources that the `task` requires that needs to be defined at the `pipeline` level. ## Related projects diff --git a/src/builders.ts b/src/builders.ts index c1af9ea..ee84ba5 100644 --- a/src/builders.ts +++ b/src/builders.ts @@ -283,7 +283,7 @@ export class ParameterBuilder { /** * Sets the value for the parameter - * @param val + * @param val string value or ValueResolver for pipeline-level parameter */ public withValue(val: string | IValueResolver): ParameterBuilder { // If you are giving it a value here, then you do not @@ -651,19 +651,24 @@ export class TaskStepBuilder { } } +/** + * Resolves remote tasks or pipelines through different means. + * Can be implemented by user for git, hub, bundle, etc resolvers. + */ export interface IRemoteResolver { resolver?: string; params?: ResolverParam[]; kind?: string; /** - * Gets the taskRef yaml for a remote Task - * @returns RemoteTaskRef The yaml as an API Object + * Gets the yaml reference for a remote object + * @returns RemoteRef The yaml as an API Object */ get remoteRef(): RemoteRef; } /** - * Resolves the provided cluster-scoped task into yaml for the taskRef field. + * Resolves the provided cluster-scoped `Task` or `Pipeline` into yaml + * for the taskRef or pipelineRef field, respectively. */ export class ClusterRemoteResolver implements IRemoteResolver { resolver?: string; From 900432ed4353c034e85fb95c3785a00484a03b1d Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Fri, 9 Aug 2024 13:06:04 -0400 Subject: [PATCH 29/30] Minor fixes to example code Signed-off-by: snehajais22 --- API.md | 3 ++- README.md | 3 ++- 2 files changed, 4 insertions(+), 2 deletions(-) diff --git a/API.md b/API.md index 3fbf21a..2d37d0a 100644 --- a/API.md +++ b/API.md @@ -89,6 +89,7 @@ export class MyChart extends Chart { new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withStringParam(pipelineParam) .withTask(new TaskBuilder(this, 'git-clone') .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output').withBinding('task-output')) @@ -118,7 +119,7 @@ that extends `Chart`. For example, in this code: ```typescript const app = new App(); -new MyInstallPipeline(app, 'my-install-pipeline'); +new MyChart(app, 'my-install-pipeline'); app.synth(); ``` diff --git a/README.md b/README.md index c34f9a7..5107262 100644 --- a/README.md +++ b/README.md @@ -89,6 +89,7 @@ export class MyChart extends Chart { new PipelineBuilder(this, 'clone-build-push') .withDescription('This pipeline closes a repository, builds a Docker image, etc.') + .withStringParam(pipelineParam) .withTask(new TaskBuilder(this, 'git-clone') .withName('fetch-source') .withWorkspace(new WorkspaceBuilder('output').withBinding('task-output')) @@ -118,7 +119,7 @@ that extends `Chart`. For example, in this code: ```typescript const app = new App(); -new MyInstallPipeline(app, 'my-install-pipeline'); +new MyChart(app, 'my-install-pipeline'); app.synth(); ``` From cc2854098b7fa4dc5b5f52aef14fa84cff115760 Mon Sep 17 00:00:00 2001 From: snehajais22 Date: Tue, 13 Aug 2024 09:21:41 -0400 Subject: [PATCH 30/30] Bump to Major version release Signed-off-by: snehajais22 --- .projen/tasks.json | 3 ++- .projenrc.ts | 1 + 2 files changed, 3 insertions(+), 1 deletion(-) diff --git a/.projen/tasks.json b/.projen/tasks.json index bfa4d82..8fb5d07 100644 --- a/.projen/tasks.json +++ b/.projen/tasks.json @@ -196,7 +196,8 @@ "name": "release", "description": "Prepare a release from \"main\" branch", "env": { - "RELEASE": "true" + "RELEASE": "true", + "MIN_MAJOR": "2" }, "steps": [ { diff --git a/.projenrc.ts b/.projenrc.ts index f2cc7ab..850fea6 100644 --- a/.projenrc.ts +++ b/.projenrc.ts @@ -26,6 +26,7 @@ const project = new cdk8s.ConstructLibraryCdk8s({ gitignore: [ '.idea/', ], + minMajorVersion: 2, // deps: [], /* Runtime dependencies of this module. */ // description: undefined, /* The description is just a string that helps people understand the purpose of the package. */ // devDeps: [], /* Build dependencies for this module. */