diff --git a/API.md b/API.md
index 55d1600..1151541 100644
--- a/API.md
+++ b/API.md
@@ -2625,6 +2625,7 @@ const bashCDKPipelineOptions: BashCDKPipelineOptions = { ... }
| featureStages | StageOptions | This specifies details for feature stages. |
| independentStages | IndependentStage[] | This specifies details for independent stages. |
| personalStage | StageOptions | This specifies details for a personal stage. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos. |
| pkgNamespace | string | This field determines the NPM namespace to be used when packaging CDK cloud assemblies. |
| postSynthCommands | string[] | *No description.* |
| postSynthSteps | PipelineStep[] | *No description.* |
@@ -2725,6 +2726,19 @@ This specifies details for a personal stage.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* the project name if the project has a parent (monorepo subproject), otherwise no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos.
+
+---
+
##### `pkgNamespace`Optional
```typescript
@@ -2844,6 +2858,7 @@ const bashDriftDetectionWorkflowOptions: BashDriftDetectionWorkflowOptions = { .
| --- | --- | --- |
| stages | DriftDetectionStageOptions[] | Drift detection configurations for different environments. |
| name | string | Name of the workflow. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos. |
| schedule | string | Cron schedule for drift detection. |
| scriptPath | string | Path to the output script. |
@@ -2874,6 +2889,19 @@ Name of the workflow.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos.
+
+---
+
##### `schedule`Optional
```typescript
@@ -3030,6 +3058,7 @@ const cDKPipelineOptions: CDKPipelineOptions = { ... }
| featureStages | StageOptions | This specifies details for feature stages. |
| independentStages | IndependentStage[] | This specifies details for independent stages. |
| personalStage | StageOptions | This specifies details for a personal stage. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos. |
| pkgNamespace | string | This field determines the NPM namespace to be used when packaging CDK cloud assemblies. |
| postSynthCommands | string[] | *No description.* |
| postSynthSteps | PipelineStep[] | *No description.* |
@@ -3130,6 +3159,19 @@ This specifies details for a personal stage.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* the project name if the project has a parent (monorepo subproject), otherwise no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos.
+
+---
+
##### `pkgNamespace`Optional
```typescript
@@ -4063,6 +4105,7 @@ const driftDetectionWorkflowOptions: DriftDetectionWorkflowOptions = { ... }
| --- | --- | --- |
| stages | DriftDetectionStageOptions[] | Drift detection configurations for different environments. |
| name | string | Name of the workflow. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos. |
| schedule | string | Cron schedule for drift detection. |
---
@@ -4092,6 +4135,19 @@ Name of the workflow.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos.
+
+---
+
##### `schedule`Optional
```typescript
@@ -4296,6 +4352,7 @@ const githubCDKPipelineOptions: GithubCDKPipelineOptions = { ... }
| featureStages | StageOptions | This specifies details for feature stages. |
| independentStages | IndependentStage[] | This specifies details for independent stages. |
| personalStage | StageOptions | This specifies details for a personal stage. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos. |
| pkgNamespace | string | This field determines the NPM namespace to be used when packaging CDK cloud assemblies. |
| postSynthCommands | string[] | *No description.* |
| postSynthSteps | PipelineStep[] | *No description.* |
@@ -4400,6 +4457,19 @@ This specifies details for a personal stage.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* the project name if the project has a parent (monorepo subproject), otherwise no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos.
+
+---
+
##### `pkgNamespace`Optional
```typescript
@@ -4577,6 +4647,7 @@ const gitHubDriftDetectionWorkflowOptions: GitHubDriftDetectionWorkflowOptions =
| --- | --- | --- |
| stages | DriftDetectionStageOptions[] | Drift detection configurations for different environments. |
| name | string | Name of the workflow. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos. |
| schedule | string | Cron schedule for drift detection. |
| createIssues | boolean | Whether to create issues on drift detection. |
| permissions | {[ key: string ]: string} | Additional permissions for GitHub workflow. |
@@ -4608,6 +4679,19 @@ Name of the workflow.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos.
+
+---
+
##### `schedule`Optional
```typescript
@@ -4939,6 +5023,7 @@ const gitlabCDKPipelineOptions: GitlabCDKPipelineOptions = { ... }
| featureStages | StageOptions | This specifies details for feature stages. |
| independentStages | IndependentStage[] | This specifies details for independent stages. |
| personalStage | StageOptions | This specifies details for a personal stage. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos. |
| pkgNamespace | string | This field determines the NPM namespace to be used when packaging CDK cloud assemblies. |
| postSynthCommands | string[] | *No description.* |
| postSynthSteps | PipelineStep[] | *No description.* |
@@ -5041,6 +5126,19 @@ This specifies details for a personal stage.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* the project name if the project has a parent (monorepo subproject), otherwise no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos.
+
+---
+
##### `pkgNamespace`Optional
```typescript
@@ -5184,6 +5282,7 @@ const gitLabDriftDetectionWorkflowOptions: GitLabDriftDetectionWorkflowOptions =
| --- | --- | --- |
| stages | DriftDetectionStageOptions[] | Drift detection configurations for different environments. |
| name | string | Name of the workflow. |
+| pipelineName | string | A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos. |
| schedule | string | Cron schedule for drift detection. |
| image | string | Docker image to use for drift detection. |
| runnerTags | string[] | GitLab runner tags. |
@@ -5215,6 +5314,19 @@ Name of the workflow.
---
+##### `pipelineName`Optional
+
+```typescript
+public readonly pipelineName: string;
+```
+
+- *Type:* string
+- *Default:* no prefix
+
+A unique name for this pipeline, used as a prefix for workflow files and artifact names to prevent collisions in monorepos.
+
+---
+
##### `schedule`Optional
```typescript
diff --git a/src/awscdk/base.ts b/src/awscdk/base.ts
index edd8a37..9ca9cf4 100644
--- a/src/awscdk/base.ts
+++ b/src/awscdk/base.ts
@@ -122,6 +122,14 @@ export interface IamRoleConfig {
*/
export interface CDKPipelineOptions {
+ /**
+ * A unique name for this pipeline, used as a prefix for workflow files,
+ * concurrency groups, and artifact names to prevent collisions in monorepos.
+ *
+ * @default - the project name if the project has a parent (monorepo subproject), otherwise no prefix
+ */
+ readonly pipelineName?: string;
+
/**
* the name of the branch to deploy from
* @default main
@@ -203,6 +211,9 @@ export abstract class CDKPipeline extends Component {
public readonly stackPrefix: string;
public readonly branchName: string;
+ /** Prefix for workflow files, concurrency groups, and artifact names to prevent collisions in monorepos. */
+ protected readonly namePrefix: string;
+
constructor(protected app: awscdk.AwsCdkTypeScriptApp, protected baseOptions: CDKPipelineOptions) {
super(app);
@@ -215,6 +226,8 @@ export abstract class CDKPipeline extends Component {
// );
this.project.gitignore.exclude('/cdk-outputs-*.json');
+ const pipelineName = baseOptions.pipelineName ?? (app.parent ? app.name : undefined);
+ this.namePrefix = pipelineName ? `${pipelineName}-` : '';
this.stackPrefix = baseOptions.stackPrefix ?? app.name;
this.branchName = baseOptions.branchName ?? 'main'; // TODO use defaultReleaseBranch of NodeProject
diff --git a/src/awscdk/github.ts b/src/awscdk/github.ts
index 25e6bec..fffaf9c 100644
--- a/src/awscdk/github.ts
+++ b/src/awscdk/github.ts
@@ -83,7 +83,7 @@ export class GithubCDKPipeline extends CDKPipeline {
});
// Initialize the deployment workflow on GitHub.
- this.deploymentWorkflow = this.app.github!.addWorkflow('deploy');
+ this.deploymentWorkflow = this.app.github!.addWorkflow(`${this.namePrefix}deploy`);
this.deploymentWorkflow.on({
push: {
branches: [this.branchName],
@@ -146,7 +146,7 @@ export class GithubCDKPipeline extends CDKPipeline {
* Creates a workflow for deploying feature branches when PRs are labeled with 'feature-deployment'.
*/
private createFeatureDeployWorkflow(): void {
- const workflow = this.app.github!.addWorkflow('deploy-feature');
+ const workflow = this.app.github!.addWorkflow(`${this.namePrefix}deploy-feature`);
workflow.on({
pullRequestTarget: {
@@ -161,8 +161,8 @@ export class GithubCDKPipeline extends CDKPipeline {
this.provideDeployStep({ name: 'feature', env: this.baseOptions.featureStages!.env }),
new CdkOutputsSummaryStep(this.project, { stageName: 'feature' }),
new UploadArtifactStep(this.project, {
- name: 'cdk-outputs-feature',
- path: 'cdk-outputs-feature.json',
+ name: `${this.namePrefix}cdk-outputs-feature`,
+ path: `${this.namePrefix}cdk-outputs-feature.json`,
}),
].map(s => s.toGithub());
@@ -176,7 +176,7 @@ export class GithubCDKPipeline extends CDKPipeline {
idToken: JobPermission.WRITE,
}, ...(steps.flatMap(s => s.permissions).filter(p => p != undefined) as JobPermissions[])),
concurrency: {
- 'group': 'deploy-feature-${{ github.event.pull_request.number }}',
+ 'group': `${this.namePrefix}deploy-feature-\${{ github.event.pull_request.number }}`,
'cancel-in-progress': false,
},
env: {
@@ -203,7 +203,7 @@ export class GithubCDKPipeline extends CDKPipeline {
* Creates a workflow for destroying feature branches when PRs are closed or unlabeled.
*/
private createFeatureDestroyWorkflow(): void {
- const workflow = this.app.github!.addWorkflow('destroy-feature');
+ const workflow = this.app.github!.addWorkflow(`${this.namePrefix}destroy-feature`);
workflow.on({
pullRequestTarget: {
@@ -233,7 +233,7 @@ export class GithubCDKPipeline extends CDKPipeline {
idToken: JobPermission.WRITE,
}, ...(steps.flatMap(s => s.permissions).filter(p => p != undefined) as JobPermissions[])),
concurrency: {
- 'group': 'destroy-feature-${{ github.event.pull_request.number }}',
+ 'group': `${this.namePrefix}destroy-feature-\${{ github.event.pull_request.number }}`,
'cancel-in-progress': false,
},
env: {
@@ -265,7 +265,7 @@ export class GithubCDKPipeline extends CDKPipeline {
steps.push(this.provideSynthStep());
steps.push(new UploadArtifactStep(this.project, {
- name: 'cloud-assembly',
+ name: `${this.namePrefix}cloud-assembly`,
path: `${this.app.cdkConfig.cdkout}/`,
}));
@@ -307,7 +307,7 @@ export class GithubCDKPipeline extends CDKPipeline {
const steps = [
new SimpleCommandStep(this.project, ['git config --global user.name "github-actions" && git config --global user.email "github-actions@github.com"']),
new DownloadArtifactStep(this.project, {
- name: 'cloud-assembly',
+ name: `${this.namePrefix}cloud-assembly`,
path: `${this.app.cdkConfig.cdkout}/`,
}),
this.provideInstallStep(),
@@ -367,13 +367,13 @@ export class GithubCDKPipeline extends CDKPipeline {
this.provideDeployStep(stage),
new CdkOutputsSummaryStep(this.project, { stageName: stage.name }),
new UploadArtifactStep(this.project, {
- name: `cdk-outputs-${stage.name}`,
+ name: `${this.namePrefix}cdk-outputs-${stage.name}`,
path: `cdk-outputs-${stage.name}.json`,
}),
].map(s => s.toGithub());
// Create new workflow for deployment
- const stageWorkflow = this.app.github!.addWorkflow(`release-${stage.name}`);
+ const stageWorkflow = this.app.github!.addWorkflow(`${this.namePrefix}release-${stage.name}`);
stageWorkflow.on({
workflowDispatch: {
inputs: {
@@ -392,7 +392,7 @@ export class GithubCDKPipeline extends CDKPipeline {
environment: stage.githubEnvironment ?? stage.name,
},
concurrency: {
- 'group': `deploy-${stage.name}`,
+ 'group': `${this.namePrefix}deploy-${stage.name}`,
'cancel-in-progress': false,
},
env: {
@@ -430,14 +430,14 @@ export class GithubCDKPipeline extends CDKPipeline {
) {
const steps = [
new DownloadArtifactStep(this.project, {
- name: 'cloud-assembly',
+ name: `${this.namePrefix}cloud-assembly`,
path: `${this.app.cdkConfig.cdkout}/`,
}),
this.provideInstallStep(),
this.provideDeployStep(stage),
new CdkOutputsSummaryStep(this.project, { stageName: stage.name }),
new UploadArtifactStep(this.project, {
- name: `cdk-outputs-${stage.name}`,
+ name: `${this.namePrefix}cdk-outputs-${stage.name}`,
path: `cdk-outputs-${stage.name}.json`,
}),
].map(s => s.toGithub());
@@ -449,7 +449,7 @@ export class GithubCDKPipeline extends CDKPipeline {
environment: stage.githubEnvironment ?? stage.name,
},
concurrency: {
- 'group': `deploy-${stage.name}`,
+ 'group': `${this.namePrefix}deploy-${stage.name}`,
'cancel-in-progress': false,
},
needs: [`assetUpload${useGithubEnvironmentsForAssetUpload ? `-${stage.name}` : ''}`, ...steps.flatMap(s => s.needs), ...jobDependencies],
@@ -491,13 +491,13 @@ export class GithubCDKPipeline extends CDKPipeline {
this.provideDeployStep(stage),
new CdkOutputsSummaryStep(this.project, { stageName: stage.name }),
new UploadArtifactStep(this.project, {
- name: `cdk-outputs-${stage.name}`,
+ name: `${this.namePrefix}cdk-outputs-${stage.name}`,
path: `cdk-outputs-${stage.name}.json`,
}),
].map(s => s.toGithub());
// Create new workflow for deployment
- const stageWorkflow = this.app.github!.addWorkflow(`deploy-${stage.name}`);
+ const stageWorkflow = this.app.github!.addWorkflow(`${this.namePrefix}deploy-${stage.name}`);
stageWorkflow.on({
workflowDispatch: {},
});
@@ -506,7 +506,7 @@ export class GithubCDKPipeline extends CDKPipeline {
needs: steps.flatMap(s => s.needs),
runsOn: this.options.runnerTags ?? DEFAULT_RUNNER_TAGS,
concurrency: {
- 'group': `deploy-${stage.name}`,
+ 'group': `${this.namePrefix}deploy-${stage.name}`,
'cancel-in-progress': false,
},
env: {
diff --git a/src/awscdk/gitlab.ts b/src/awscdk/gitlab.ts
index 9758e60..dd77879 100644
--- a/src/awscdk/gitlab.ts
+++ b/src/awscdk/gitlab.ts
@@ -97,7 +97,7 @@ export class GitlabCDKPipeline extends CDKPipeline {
*/
protected setupSnippets() {
this.config.addJobs({
- '.artifacts_cdk': {
+ [`.${this.namePrefix}artifacts_cdk`]: {
artifacts: {
when: gitlab.CacheWhen.ON_SUCCESS,
expireIn: '30 days',
@@ -106,7 +106,7 @@ export class GitlabCDKPipeline extends CDKPipeline {
paths: ['cdk.out'],
},
},
- '.artifacts_cdkdeploy': {
+ [`.${this.namePrefix}artifacts_cdkdeploy`]: {
artifacts: {
when: gitlab.CacheWhen.ON_SUCCESS,
expireIn: '30 days',
@@ -115,7 +115,7 @@ export class GitlabCDKPipeline extends CDKPipeline {
paths: ['cdk-outputs-*.json'],
},
},
- '.aws_base': {
+ [`.${this.namePrefix}aws_base`]: {
image: { name: this.jobImage },
idTokens: {
AWS_TOKEN: {
@@ -150,8 +150,8 @@ export class GitlabCDKPipeline extends CDKPipeline {
this.config.addStages('synth');
this.config.addJobs({
- synth: {
- extends: ['.aws_base', '.artifacts_cdk', ...gitlabSteps.flatMap(s => s.extensions)],
+ [`${this.namePrefix}synth`]: {
+ extends: [`.${this.namePrefix}aws_base`, `.${this.namePrefix}artifacts_cdk`, ...gitlabSteps.flatMap(s => s.extensions)],
needs: gitlabSteps.flatMap(s => s.needs),
stage: 'synth',
tags: this.options.runnerTags?.synth ?? this.options.runnerTags?.default,
@@ -181,11 +181,11 @@ export class GitlabCDKPipeline extends CDKPipeline {
this.config.addStages('publish_assets');
this.config.addJobs({
- publish_assets: {
- extends: ['.aws_base', ...gitlabSteps.flatMap(s => s.extensions)],
+ [`${this.namePrefix}publish_assets`]: {
+ extends: [`.${this.namePrefix}aws_base`, ...gitlabSteps.flatMap(s => s.extensions)],
stage: 'publish_assets',
tags: this.options.runnerTags?.assetPublishing ?? this.options.runnerTags?.default,
- needs: [{ job: 'synth', artifacts: true }, ...gitlabSteps.flatMap(s => s.needs)],
+ needs: [{ job: `${this.namePrefix}synth`, artifacts: true }, ...gitlabSteps.flatMap(s => s.needs)],
script: gitlabSteps.flatMap(s => s.commands),
variables: gitlabSteps.reduce((acc, step) => ({ ...acc, ...step.env }), {}),
},
@@ -216,24 +216,24 @@ export class GitlabCDKPipeline extends CDKPipeline {
this.config.addStages(stage.name);
this.config.addJobs({
...(stage.diffType !== CdkDiffType.NONE) && {
- [`diff-${stage.name}`]: {
- extends: ['.aws_base', ...diffSteps.flatMap(s => s.extensions)],
+ [`${this.namePrefix}diff-${stage.name}`]: {
+ extends: [`.${this.namePrefix}aws_base`, ...diffSteps.flatMap(s => s.extensions)],
stage: stage.name,
tags: this.options.runnerTags?.diff?.[stage.name] ?? this.options.runnerTags?.deployment?.[stage.name] ?? this.options.runnerTags?.default,
only: {
refs: [this.branchName],
},
needs: [
- { job: 'synth', artifacts: true },
- { job: 'publish_assets' },
+ { job: `${this.namePrefix}synth`, artifacts: true },
+ { job: `${this.namePrefix}publish_assets` },
...diffSteps.flatMap(s => s.needs),
],
script: diffSteps.flatMap(s => s.commands),
variables: diffSteps.reduce((acc, step) => ({ ...acc, ...step.env }), {}),
},
},
- [`deploy-${stage.name}`]: {
- extends: ['.aws_base', '.artifacts_cdkdeploy', ...deploySteps.flatMap(s => s.extensions)],
+ [`${this.namePrefix}deploy-${stage.name}`]: {
+ extends: [`.${this.namePrefix}aws_base`, `.${this.namePrefix}artifacts_cdkdeploy`, ...deploySteps.flatMap(s => s.extensions)],
stage: stage.name,
tags: this.options.runnerTags?.deployment?.[stage.name] ?? this.options.runnerTags?.default,
...stage.manualApproval && {
@@ -243,9 +243,9 @@ export class GitlabCDKPipeline extends CDKPipeline {
refs: [this.branchName],
},
needs: [
- { job: 'synth', artifacts: true },
- { job: 'publish_assets' },
- ...(stage.diffType !== CdkDiffType.NONE) ? [{ job: `diff-${stage.name}` }] : [],
+ { job: `${this.namePrefix}synth`, artifacts: true },
+ { job: `${this.namePrefix}publish_assets` },
+ ...(stage.diffType !== CdkDiffType.NONE) ? [{ job: `${this.namePrefix}diff-${stage.name}` }] : [],
...deploySteps.flatMap(s => s.needs),
],
script: deploySteps.flatMap(s => s.commands),
@@ -271,8 +271,8 @@ export class GitlabCDKPipeline extends CDKPipeline {
this.config.addStages(stage.name);
this.config.addJobs({
- [`deploy-${stage.name}`]: {
- extends: ['.aws_base', '.artifacts_cdkdeploy', ...steps.flatMap(s => s.extensions)],
+ [`${this.namePrefix}deploy-${stage.name}`]: {
+ extends: [`.${this.namePrefix}aws_base`, `.${this.namePrefix}artifacts_cdkdeploy`, ...steps.flatMap(s => s.extensions)],
stage: stage.name,
tags: this.options.runnerTags?.deployment?.[stage.name] ?? this.options.runnerTags?.default,
...stage.deployOnPush && {
diff --git a/src/drift/base.ts b/src/drift/base.ts
index 6c858d4..e3b877a 100644
--- a/src/drift/base.ts
+++ b/src/drift/base.ts
@@ -56,6 +56,14 @@ export interface DriftErrorHandler {
}
export interface DriftDetectionWorkflowOptions {
+ /**
+ * A unique name for this pipeline, used as a prefix for workflow files
+ * and artifact names to prevent collisions in monorepos.
+ *
+ * @default - no prefix
+ */
+ readonly pipelineName?: string;
+
/**
* Name of the workflow
* @default "drift-detection"
@@ -79,9 +87,13 @@ export abstract class DriftDetectionWorkflow extends Component {
public readonly schedule: string;
protected readonly stages: DriftDetectionStageOptions[];
+ /** Prefix for workflow files and artifact names to prevent collisions in monorepos. */
+ protected readonly namePrefix: string;
+
constructor(project: Project, options: DriftDetectionWorkflowOptions) {
super(project);
+ this.namePrefix = options.pipelineName ? `${options.pipelineName}-` : '';
this.name = options.name ?? 'drift-detection';
this.schedule = options.schedule ?? '0 0 * * *';
this.stages = options.stages;
diff --git a/src/drift/github.ts b/src/drift/github.ts
index 787215e..1272b5e 100644
--- a/src/drift/github.ts
+++ b/src/drift/github.ts
@@ -27,7 +27,7 @@ export class GitHubDriftDetectionWorkflow extends DriftDetectionWorkflow {
this.permissions = options.permissions;
this.createIssues = options.createIssues ?? false;
- this.workflow = (this.project as GitHubProject).github!.addWorkflow('drift-detection');
+ this.workflow = (this.project as GitHubProject).github!.addWorkflow(`${this.namePrefix}drift-detection`);
this.workflow.on({
schedule: [{
cron: this.schedule,
@@ -82,8 +82,8 @@ export class GitHubDriftDetectionWorkflow extends DriftDetectionWorkflow {
name: 'Upload results',
uses: 'actions/upload-artifact@v4',
with: {
- name: `drift-results-${stage.name}`,
- path: `drift-results-${stage.name}.json`,
+ name: `${this.namePrefix}drift-results-${stage.name}`,
+ path: `${this.namePrefix}drift-results-${stage.name}.json`,
},
},
...(this.createIssues ? [{
@@ -127,7 +127,7 @@ export class GitHubDriftDetectionWorkflow extends DriftDetectionWorkflow {
private generateIssueCreationScript(stage: DriftDetectionStageOptions): string {
return `
const fs = require('fs');
-const resultsFile = 'drift-results-${stage.name}.json';
+const resultsFile = '${this.namePrefix}drift-results-${stage.name}.json';
if (!fs.existsSync(resultsFile)) {
console.log('No results file found');
diff --git a/src/drift/gitlab.ts b/src/drift/gitlab.ts
index 21d4f1f..2123e0c 100644
--- a/src/drift/gitlab.ts
+++ b/src/drift/gitlab.ts
@@ -32,7 +32,7 @@ export class GitLabDriftDetectionWorkflow extends DriftDetectionWorkflow {
this.config.addStages('drift-detection', 'summary');
this.config.addJobs({
- '.drift-detection': {
+ [`.${this.namePrefix}drift-detection`]: {
stage: 'drift-detection',
tags: this.runnerTags,
image: { name: this.image },
@@ -60,14 +60,14 @@ export class GitLabDriftDetectionWorkflow extends DriftDetectionWorkflow {
// Add job for each stage
for (const stage of this.stages) {
- const jobName = `drift:${stage.name}`;
+ const jobName = `${this.namePrefix}drift:${stage.name}`;
const driftStep = new DriftDetectionStep(this.project, stage);
const stepConfig = driftStep.toGitlab();
this.config.addJobs({
[jobName]: {
- extends: ['.drift-detection'],
+ extends: [`.${this.namePrefix}drift-detection`],
variables: {
...stepConfig.env,
},
@@ -79,10 +79,10 @@ export class GitLabDriftDetectionWorkflow extends DriftDetectionWorkflow {
// Add summary job
this.config.addJobs({
- 'drift:summary': {
+ [`${this.namePrefix}drift:summary`]: {
stage: 'summary',
tags: this.runnerTags,
- needs: this.stages.map(s => `drift:${s.name}`),
+ needs: this.stages.map(s => `${this.namePrefix}drift:${s.name}`),
only: {
refs: ['schedules'],
variables: ['$CI_PIPELINE_SOURCE == "schedule"', '$DRIFT_DETECTION == "true"'],
diff --git a/test/__snapshots__/github.test.ts.snap b/test/__snapshots__/github.test.ts.snap
index 251950f..9ad3801 100644
--- a/test/__snapshots__/github.test.ts.snap
+++ b/test/__snapshots__/github.test.ts.snap
@@ -1666,6 +1666,425 @@ jobs:
"
`;
+exports[`Github snapshot with explicit pipelineName 1`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+name: backend-deploy
+on:
+ push:
+ branches:
+ - main
+ workflow_dispatch: {}
+jobs:
+ synth:
+ name: Synth CDK application
+ needs: []
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+ - run: npx projen install:ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: synthRole
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen build
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4.6.2
+ with:
+ name: backend-cloud-assembly
+ path: cdk.out/
+ assetUpload:
+ name: Publish assets to AWS
+ needs: synth
+ runs-on: ubuntu-latest
+ permissions:
+ id-token: write
+ contents: write
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ with:
+ fetch-depth: 0
+ - run: git config --global user.name "github-actions" && git config --global user.email "github-actions@github.com"
+ - name: Download Artifact
+ uses: actions/download-artifact@v5
+ with:
+ name: backend-cloud-assembly
+ path: cdk.out/
+ - run: npx projen install:ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: publishRole
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen publish:assets
+ - run: npx projen bump
+ - run: npx projen release:push-assembly
+ deploy-dev:
+ name: Deploy stage dev to AWS
+ needs: assetUpload
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ concurrency:
+ group: backend-deploy-dev
+ cancel-in-progress: false
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ - name: Download Artifact
+ uses: actions/download-artifact@v5
+ with:
+ name: backend-cloud-assembly
+ path: cdk.out/
+ - run: npx projen install:ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: devRole
+ role-session-name: GitHubAction
+ aws-region: eu-central-1
+ - run: npx projen deploy:dev
+ - name: Add CDK outputs to summary - dev
+ run: |-
+ if [ -f "cdk-outputs-dev.json" ]; then
+ echo "## CDK Stack Outputs - dev" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Check if the file has content
+ if [ -s "cdk-outputs-dev.json" ]; then
+ # Parse JSON and create a markdown table
+ echo "| Stack | Output Key | Output Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|------------|--------------|" >> $GITHUB_STEP_SUMMARY
+
+ # Use jq to parse the JSON and format as table rows
+ jq -r 'to_entries | .[] | .key as $stack | .value | to_entries | .[] | "| \\($stack) | \\(.key) | \\(.value) |"' "cdk-outputs-dev.json" >> $GITHUB_STEP_SUMMARY
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "No outputs found for this deployment." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ else
+ echo "## CDK Stack Outputs - dev" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Outputs file not found: cdk-outputs-dev.json" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4.6.2
+ with:
+ name: backend-cdk-outputs-dev
+ path: cdk-outputs-dev.json
+"
+`;
+
+exports[`Github snapshot with explicit pipelineName 2`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+name: backend-deploy-feature
+on:
+ pull_request_target:
+ types:
+ - synchronize
+ - labeled
+ - opened
+ - reopened
+ workflow_dispatch: {}
+jobs:
+ synth-and-deploy:
+ name: Synth and deploy CDK application to feature stage
+ needs: []
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ concurrency:
+ group: backend-deploy-feature-\${{ github.event.pull_request.number }}
+ cancel-in-progress: false
+ env:
+ CI: "true"
+ BRANCH: \${{ github.head_ref }}
+ if: contains(join(github.event.pull_request.labels.*.name, ','), 'feature-deployment')
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ - run: npx projen install:ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: synthRole
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen build
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen deploy:feature
+ - name: Add CDK outputs to summary - feature
+ run: |-
+ if [ -f "cdk-outputs-feature.json" ]; then
+ echo "## CDK Stack Outputs - feature" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Check if the file has content
+ if [ -s "cdk-outputs-feature.json" ]; then
+ # Parse JSON and create a markdown table
+ echo "| Stack | Output Key | Output Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|------------|--------------|" >> $GITHUB_STEP_SUMMARY
+
+ # Use jq to parse the JSON and format as table rows
+ jq -r 'to_entries | .[] | .key as $stack | .value | to_entries | .[] | "| \\($stack) | \\(.key) | \\(.value) |"' "cdk-outputs-feature.json" >> $GITHUB_STEP_SUMMARY
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "No outputs found for this deployment." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ else
+ echo "## CDK Stack Outputs - feature" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Outputs file not found: cdk-outputs-feature.json" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4.6.2
+ with:
+ name: backend-cdk-outputs-feature
+ path: backend-cdk-outputs-feature.json
+"
+`;
+
+exports[`Github snapshot with explicit pipelineName 3`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+name: backend-destroy-feature
+on:
+ pull_request_target:
+ types:
+ - closed
+ - unlabeled
+ workflow_dispatch: {}
+jobs:
+ destroy-feature:
+ name: Destroy CDK feature stage
+ needs: []
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ concurrency:
+ group: backend-destroy-feature-\${{ github.event.pull_request.number }}
+ cancel-in-progress: false
+ env:
+ CI: "true"
+ BRANCH: \${{ github.head_ref }}
+ if: github.event.action == 'closed' || (github.event.action == 'unlabeled' && github.event.label.name == 'feature-deployment')
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ - run: npx projen install:ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: synthRole
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen build
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen destroy:feature
+"
+`;
+
+exports[`Github snapshot with explicit pipelineName 4`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+name: backend-release-prod
+on:
+ workflow_dispatch:
+ inputs:
+ version:
+ description: Package version
+ required: true
+jobs:
+ deploy:
+ name: Release stage prod to AWS
+ needs: []
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ concurrency:
+ group: backend-deploy-prod
+ cancel-in-progress: false
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ - run: npx projen install:ci
+ - run: yarn add @assembly/testapp@\${{github.event.inputs.version}}
+ - run: mv ./node_modules/@assembly/testapp cdk.out
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: prodRole
+ role-session-name: GitHubAction
+ aws-region: eu-central-1
+ - run: npx projen deploy:prod
+ - name: Add CDK outputs to summary - prod
+ run: |-
+ if [ -f "cdk-outputs-prod.json" ]; then
+ echo "## CDK Stack Outputs - prod" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Check if the file has content
+ if [ -s "cdk-outputs-prod.json" ]; then
+ # Parse JSON and create a markdown table
+ echo "| Stack | Output Key | Output Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|------------|--------------|" >> $GITHUB_STEP_SUMMARY
+
+ # Use jq to parse the JSON and format as table rows
+ jq -r 'to_entries | .[] | .key as $stack | .value | to_entries | .[] | "| \\($stack) | \\(.key) | \\(.value) |"' "cdk-outputs-prod.json" >> $GITHUB_STEP_SUMMARY
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "No outputs found for this deployment." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ else
+ echo "## CDK Stack Outputs - prod" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Outputs file not found: cdk-outputs-prod.json" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4.6.2
+ with:
+ name: backend-cdk-outputs-prod
+ path: cdk-outputs-prod.json
+"
+`;
+
+exports[`Github snapshot with explicit pipelineName 5`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+name: backend-deploy-sandbox
+on:
+ workflow_dispatch: {}
+jobs:
+ deploy:
+ name: Release stage sandbox to AWS
+ needs: []
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ concurrency:
+ group: backend-deploy-sandbox
+ cancel-in-progress: false
+ env:
+ CI: "true"
+ steps:
+ - uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Checkout
+ uses: actions/checkout@v5
+ - run: npx projen install:ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: synthRole
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: npx projen build
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-session-name: GitHubAction
+ aws-region: eu-central-1
+ - run: npx projen diff:sandbox
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-session-name: GitHubAction
+ aws-region: eu-central-1
+ - run: npx projen deploy:sandbox
+ - name: Add CDK outputs to summary - sandbox
+ run: |-
+ if [ -f "cdk-outputs-sandbox.json" ]; then
+ echo "## CDK Stack Outputs - sandbox" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Check if the file has content
+ if [ -s "cdk-outputs-sandbox.json" ]; then
+ # Parse JSON and create a markdown table
+ echo "| Stack | Output Key | Output Value |" >> $GITHUB_STEP_SUMMARY
+ echo "|-------|------------|--------------|" >> $GITHUB_STEP_SUMMARY
+
+ # Use jq to parse the JSON and format as table rows
+ jq -r 'to_entries | .[] | .key as $stack | .value | to_entries | .[] | "| \\($stack) | \\(.key) | \\(.value) |"' "cdk-outputs-sandbox.json" >> $GITHUB_STEP_SUMMARY
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+ else
+ echo "No outputs found for this deployment." >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ else
+ echo "## CDK Stack Outputs - sandbox" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "Outputs file not found: cdk-outputs-sandbox.json" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+ fi
+ - name: Upload Artifact
+ uses: actions/upload-artifact@v4.6.2
+ with:
+ name: backend-cdk-outputs-sandbox
+ path: cdk-outputs-sandbox.json
+"
+`;
+
exports[`Github snapshot with feature stages 1`] = `
"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
diff --git a/test/__snapshots__/gitlab.test.ts.snap b/test/__snapshots__/gitlab.test.ts.snap
index f71c73d..350409d 100644
--- a/test/__snapshots__/gitlab.test.ts.snap
+++ b/test/__snapshots__/gitlab.test.ts.snap
@@ -270,6 +270,202 @@ exports[`Gitlab snapshot 2`] = `
}
`;
+exports[`Gitlab snapshot with explicit pipelineName 1`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+stages:
+ - synth
+ - publish_assets
+ - dev
+ - prod
+ - sandbox
+.backend-artifacts_cdk:
+ artifacts:
+ when: on_success
+ expire_in: 30 days
+ name: CDK Assembly - $CI_JOB_NAME-$CI_COMMIT_REF_SLUG
+ untracked: false
+ paths:
+ - cdk.out
+.backend-artifacts_cdkdeploy:
+ artifacts:
+ when: on_success
+ expire_in: 30 days
+ name: CDK Outputs - $CI_JOB_NAME-$CI_COMMIT_REF_SLUG
+ untracked: false
+ paths:
+ - cdk-outputs-*.json
+.backend-aws_base:
+ image:
+ name: "image: jsii/superchain:1-buster-slim-node18"
+ id_tokens:
+ AWS_TOKEN:
+ aud: https://sts.amazonaws.com
+ variables:
+ CI: "true"
+backend-synth:
+ extends:
+ - .backend-aws_base
+ - .backend-artifacts_cdk
+ needs: []
+ stage: synth
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "synthRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen build
+ variables: {}
+backend-publish_assets:
+ extends:
+ - .backend-aws_base
+ stage: publish_assets
+ needs:
+ - job: backend-synth
+ artifacts: true
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "publishRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen publish:assets
+ variables: {}
+backend-diff-dev:
+ extends:
+ - .backend-aws_base
+ stage: dev
+ only:
+ refs:
+ - main
+ needs:
+ - job: backend-synth
+ artifacts: true
+ - job: backend-publish_assets
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "devRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen fastdiff:dev
+ variables:
+ AWS_REGION: eu-central-1
+backend-deploy-dev:
+ extends:
+ - .backend-aws_base
+ - .backend-artifacts_cdkdeploy
+ stage: dev
+ only:
+ refs:
+ - main
+ needs:
+ - job: backend-synth
+ artifacts: true
+ - job: backend-publish_assets
+ - job: backend-diff-dev
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "devRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen deploy:dev
+ - if [ -f "cdk-outputs-dev.json" ]; then
+ - ' echo -e "\\e[0Ksection_start:\`date +%s\`:cdk_outputs_dev[collapsed=false]\\r\\e[0K📦 CDK Stack Outputs - dev"'
+ - ' if [ -s "cdk-outputs-dev.json" ]; then'
+ - ' echo ""'
+ - ' echo "Stack Outputs:"'
+ - ' echo "=============="'
+ - " jq -r 'to_entries | .[] | \\"\\\\n📚 Stack: \\\\(.key)\\\\n\\" + (.value | to_entries | .[] | \\" • \\\\(.key): \\\\(.value)\\")' \\"cdk-outputs-dev.json\\""
+ - ' echo ""'
+ - " else"
+ - ' echo "No outputs found for this deployment."'
+ - " fi"
+ - ' echo -e "\\e[0Ksection_end:\`date +%s\`:cdk_outputs_dev\\r\\e[0K"'
+ - else
+ - ' echo "⚠️ CDK outputs file not found: cdk-outputs-dev.json"'
+ - fi
+ variables:
+ AWS_REGION: eu-central-1
+backend-diff-prod:
+ extends:
+ - .backend-aws_base
+ stage: prod
+ only:
+ refs:
+ - main
+ needs:
+ - job: backend-synth
+ artifacts: true
+ - job: backend-publish_assets
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "prodRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen diff:prod
+ variables:
+ AWS_REGION: eu-central-1
+backend-deploy-prod:
+ extends:
+ - .backend-aws_base
+ - .backend-artifacts_cdkdeploy
+ stage: prod
+ when: manual
+ only:
+ refs:
+ - main
+ needs:
+ - job: backend-synth
+ artifacts: true
+ - job: backend-publish_assets
+ - job: backend-diff-prod
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "prodRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen deploy:prod
+ - if [ -f "cdk-outputs-prod.json" ]; then
+ - ' echo -e "\\e[0Ksection_start:\`date +%s\`:cdk_outputs_prod[collapsed=false]\\r\\e[0K📦 CDK Stack Outputs - prod"'
+ - ' if [ -s "cdk-outputs-prod.json" ]; then'
+ - ' echo ""'
+ - ' echo "Stack Outputs:"'
+ - ' echo "=============="'
+ - " jq -r 'to_entries | .[] | \\"\\\\n📚 Stack: \\\\(.key)\\\\n\\" + (.value | to_entries | .[] | \\" • \\\\(.key): \\\\(.value)\\")' \\"cdk-outputs-prod.json\\""
+ - ' echo ""'
+ - " else"
+ - ' echo "No outputs found for this deployment."'
+ - " fi"
+ - ' echo -e "\\e[0Ksection_end:\`date +%s\`:cdk_outputs_prod\\r\\e[0K"'
+ - else
+ - ' echo "⚠️ CDK outputs file not found: cdk-outputs-prod.json"'
+ - fi
+ variables:
+ AWS_REGION: eu-central-1
+backend-deploy-sandbox:
+ extends:
+ - .backend-aws_base
+ - .backend-artifacts_cdkdeploy
+ stage: sandbox
+ only:
+ refs:
+ - main
+ needs: []
+ script:
+ - npx projen install:ci
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "synthRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen build
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "undefined" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen diff:sandbox
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "undefined" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - npx projen deploy:sandbox
+ - if [ -f "cdk-outputs-sandbox.json" ]; then
+ - ' echo -e "\\e[0Ksection_start:\`date +%s\`:cdk_outputs_sandbox[collapsed=false]\\r\\e[0K📦 CDK Stack Outputs - sandbox"'
+ - ' if [ -s "cdk-outputs-sandbox.json" ]; then'
+ - ' echo ""'
+ - ' echo "Stack Outputs:"'
+ - ' echo "=============="'
+ - " jq -r 'to_entries | .[] | \\"\\\\n📚 Stack: \\\\(.key)\\\\n\\" + (.value | to_entries | .[] | \\" • \\\\(.key): \\\\(.value)\\")' \\"cdk-outputs-sandbox.json\\""
+ - ' echo ""'
+ - " else"
+ - ' echo "No outputs found for this deployment."'
+ - " fi"
+ - ' echo -e "\\e[0Ksection_end:\`date +%s\`:cdk_outputs_sandbox\\r\\e[0K"'
+ - else
+ - ' echo "⚠️ CDK outputs file not found: cdk-outputs-sandbox.json"'
+ - fi
+ variables:
+ AWS_REGION: eu-central-1
+"
+`;
+
exports[`Gitlab snapshot with independent stage 1`] = `
"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
diff --git a/test/drift/__snapshots__/drift-detection.test.ts.snap b/test/drift/__snapshots__/drift-detection.test.ts.snap
index cf1a443..fb14ee4 100644
--- a/test/drift/__snapshots__/drift-detection.test.ts.snap
+++ b/test/drift/__snapshots__/drift-detection.test.ts.snap
@@ -576,6 +576,116 @@ jobs:
"
`;
+exports[`GitHubDriftDetectionWorkflow should prefix workflow and artifact names with pipelineName 1`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+name: backend-drift-detection
+on:
+ schedule:
+ - cron: 0 0 * * *
+ workflow_dispatch:
+ inputs:
+ stage:
+ description: Stage to check for drift (leave empty for all)
+ required: false
+ type: choice
+ options:
+ - production
+jobs:
+ drift-production:
+ name: Drift Detection - production
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ id-token: write
+ env:
+ AWS_DEFAULT_REGION: us-east-1
+ DRIFT_DETECTION_OUTPUT: drift-results-production.json
+ AWS_REGION: us-east-1
+ STAGE_NAME: production
+ if: \${{ github.event_name == 'schedule' || github.event.inputs.stage == '' || github.event.inputs.stage == 'production' }}
+ steps:
+ - name: Checkout
+ uses: actions/checkout@v5
+ - name: Setup Node.js
+ uses: actions/setup-node@v5
+ with:
+ node-version: "20"
+ - name: Install dependencies
+ run: npm ci
+ - name: AWS Credentials
+ uses: aws-actions/configure-aws-credentials@v5
+ with:
+ role-to-assume: arn:aws:iam::123456789012:role/ProdRole
+ role-session-name: GitHubAction
+ aws-region: us-east-1
+ - run: detect-drift --region us-east-1
+ - name: Upload results
+ uses: actions/upload-artifact@v4
+ with:
+ name: backend-drift-results-production
+ path: backend-drift-results-production.json
+ drift-summary:
+ name: Drift Detection Summary
+ needs: drift-production
+ runs-on: ubuntu-latest
+ permissions:
+ contents: read
+ steps:
+ - name: Download all artifacts
+ uses: actions/download-artifact@v5
+ with:
+ path: drift-results
+ - name: Generate summary
+ run: |
+
+ #!/bin/bash
+ echo "## Drift Detection Summary" >> $GITHUB_STEP_SUMMARY
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ total_stacks=0
+ total_drifted=0
+ total_errors=0
+
+ for file in drift-results-*.json; do
+ if [[ -f "$file" ]]; then
+ stage=$(basename $(dirname "$file"))
+ echo "### Stage: $stage" >> $GITHUB_STEP_SUMMARY
+
+ # Parse JSON and generate summary
+ jq -r '
+ . as $results |
+ "- Total stacks: " + ($results | length | tostring) + "\\n" +
+ "- Drifted: " + ([$results[] | select(.driftStatus == "DRIFTED")] | length | tostring) + "\\n" +
+ "- Errors: " + ([$results[] | select(.error)] | length | tostring) + "\\n" +
+ ([$results[] | select(.driftStatus == "DRIFTED")] |
+ if length > 0 then
+ "\\n**Drifted stacks:**\\n" +
+ (map(" - " + .stackName + " (" + ((.driftedResources // []) | length | tostring) + " resources)") | join("\\n"))
+ else "" end)
+ ' "$file" >> $GITHUB_STEP_SUMMARY
+
+ echo "" >> $GITHUB_STEP_SUMMARY
+
+ # Count totals
+ total_stacks=$((total_stacks + $(jq 'length' "$file")))
+ total_drifted=$((total_drifted + $(jq '[.[] | select(.driftStatus == "DRIFTED")] | length' "$file")))
+ total_errors=$((total_errors + $(jq '[.[] | select(.error)] | length' "$file")))
+ fi
+ done
+
+ echo "### Overall Summary" >> $GITHUB_STEP_SUMMARY
+ echo "- Total stacks checked: $total_stacks" >> $GITHUB_STEP_SUMMARY
+ echo "- Total drifted stacks: $total_drifted" >> $GITHUB_STEP_SUMMARY
+ echo "- Total errors: $total_errors" >> $GITHUB_STEP_SUMMARY
+
+ if [[ $total_drifted -gt 0 ]]; then
+ echo "" >> $GITHUB_STEP_SUMMARY
+ echo "⚠️ **Action required:** Drift detected in $total_drifted stacks" >> $GITHUB_STEP_SUMMARY
+ fi
+"
+`;
+
exports[`GitHubDriftDetectionWorkflow should support custom schedule 1`] = `
"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
@@ -786,4 +896,115 @@ exports[`GitLabDriftDetectionWorkflow should add GitLab runner tags when specifi
exports[`GitLabDriftDetectionWorkflow should create GitLab pipeline 1`] = `undefined`;
+exports[`GitLabDriftDetectionWorkflow should prefix job names with pipelineName 1`] = `
+"# ~~ Generated by projen. To modify, edit .projenrc.js and run "npx projen".
+
+stages:
+ - drift-detection
+ - summary
+.backend-drift-detection:
+ stage: drift-detection
+ tags: []
+ image:
+ name: node:18
+ id_tokens:
+ AWS_TOKEN:
+ aud: https://sts.amazonaws.com
+ only:
+ refs:
+ - schedules
+ variables:
+ - $CI_PIPELINE_SOURCE == "schedule"
+ - $DRIFT_DETECTION == "true"
+ before_script:
+ - apt-get update && apt-get install -y python3 python3-pip
+ - pip3 install awscli
+ - npm ci
+ artifacts:
+ paths:
+ - drift-results-*.json
+ expire_in: 1 week
+ when: always
+backend-drift:production:
+ extends:
+ - .backend-drift-detection
+ variables:
+ AWS_REGION: us-east-1
+ AWS_DEFAULT_REGION: us-east-1
+ DRIFT_DETECTION_OUTPUT: drift-results-production.json
+ STAGE_NAME: production
+ script:
+ - export $(printf "AWS_ACCESS_KEY_ID=%s AWS_SECRET_ACCESS_KEY=%s AWS_SESSION_TOKEN=%s" $(aws sts assume-role-with-web-identity --role-arn "arn:aws:iam::123456789012:role/ProdRole" --role-session-name "GitLabRunner-\${CI_PROJECT_ID}-\${CI_PIPELINE_ID}}" --web-identity-token \${AWS_TOKEN} --duration-seconds 3600 --query 'Credentials.[AccessKeyId,SecretAccessKey,SessionToken]' --output text))
+ - detect-drift --region us-east-1
+ allow_failure: true
+backend-drift:summary:
+ stage: summary
+ tags: []
+ needs:
+ - backend-drift:production
+ only:
+ refs:
+ - schedules
+ variables:
+ - $CI_PIPELINE_SOURCE == "schedule"
+ - $DRIFT_DETECTION == "true"
+ script:
+ - echo "## Drift Detection Summary"
+ - echo ""
+ - |
+
+ total_stacks=0
+ total_drifted=0
+ total_errors=0
+
+ for file in drift-results-*.json; do
+ if [[ -f "$file" ]]; then
+ stage=$(echo $file | sed 's/drift-results-//;s/.json//')
+ echo "### Stage: $stage"
+
+ # Count results
+ stacks=$(jq 'length' "$file")
+ drifted=$(jq '[.[] | select(.driftStatus == "DRIFTED")] | length' "$file")
+ errors=$(jq '[.[] | select(.error)] | length' "$file")
+
+ echo "- Total stacks: $stacks"
+ echo "- Drifted: $drifted"
+ echo "- Errors: $errors"
+
+ # Show drifted stacks
+ if [[ $drifted -gt 0 ]]; then
+ echo ""
+ echo "**Drifted stacks:**"
+ jq -r '.[] | select(.driftStatus == "DRIFTED") | " - " + .stackName + " (" + ((.driftedResources // []) | length | tostring) + " resources)"' "$file"
+ fi
+
+ echo ""
+
+ # Accumulate totals
+ total_stacks=$((total_stacks + stacks))
+ total_drifted=$((total_drifted + drifted))
+ total_errors=$((total_errors + errors))
+ fi
+ done
+
+ echo "### Overall Summary"
+ echo "- Total stacks checked: $total_stacks"
+ echo "- Total drifted stacks: $total_drifted"
+ echo "- Total errors: $total_errors"
+
+ if [[ $total_drifted -gt 0 ]]; then
+ echo ""
+ echo "⚠️ **Action required:** Drift detected in $total_drifted stacks"
+
+ # Send notification if webhook is configured
+ if [[ -n "$DRIFT_NOTIFICATION_WEBHOOK" ]]; then
+ curl -X POST "$DRIFT_NOTIFICATION_WEBHOOK" \\
+ -H "Content-Type: application/json" \\
+ -d "{\\"text\\": \\"Drift detected in $total_drifted stacks. Check pipeline $CI_PIPELINE_URL for details.\\"}" || true
+ fi
+ fi
+ when: always
+"
+`;
+
exports[`GitLabDriftDetectionWorkflow should support custom docker image 1`] = `undefined`;
diff --git a/test/drift/drift-detection.test.ts b/test/drift/drift-detection.test.ts
index fdb119a..02b5b6a 100644
--- a/test/drift/drift-detection.test.ts
+++ b/test/drift/drift-detection.test.ts
@@ -139,6 +139,31 @@ describe('GitHubDriftDetectionWorkflow', () => {
expect(snapshot['.github/workflows/drift-detection.yml']).toMatchSnapshot();
});
+ it('should prefix workflow and artifact names with pipelineName', () => {
+ new GitHubDriftDetectionWorkflow(project, {
+ pipelineName: 'backend',
+ stages: [
+ {
+ name: 'production',
+ region: 'us-east-1',
+ roleArn: 'arn:aws:iam::123456789012:role/ProdRole',
+ },
+ ],
+ });
+
+ const snapshot = synthSnapshot(project);
+
+ // Workflow file should be prefixed
+ expect(snapshot['.github/workflows/backend-drift-detection.yml']).toBeDefined();
+ expect(snapshot['.github/workflows/drift-detection.yml']).toBeUndefined();
+
+ // Artifact names should be prefixed
+ const workflowYml = snapshot['.github/workflows/backend-drift-detection.yml'];
+ expect(workflowYml).toContain('backend-drift-results-production');
+
+ expect(workflowYml).toMatchSnapshot();
+ });
+
it('should support disabling issue creation', () => {
new GitHubDriftDetectionWorkflow(project, {
createIssues: false,
@@ -194,6 +219,33 @@ describe('GitLabDriftDetectionWorkflow', () => {
expect(snapshot['.gitlab/drift-detection.yml']).toMatchSnapshot();
});
+ it('should prefix job names with pipelineName', () => {
+ new GitLabDriftDetectionWorkflow(project, {
+ pipelineName: 'backend',
+ stages: [
+ {
+ name: 'production',
+ region: 'us-east-1',
+ roleArn: 'arn:aws:iam::123456789012:role/ProdRole',
+ },
+ ],
+ });
+
+ const snapshot = synthSnapshot(project);
+
+ // Find the gitlab ci file (could be .gitlab-ci.yml or .gitlab/drift-detection.yml)
+ const gitlabCiKey = Object.keys(snapshot).find(k => k.includes('gitlab'));
+ expect(gitlabCiKey).toBeDefined();
+ const gitlabCi = snapshot[gitlabCiKey!];
+
+ // Job names should be prefixed, hidden jobs preserve dot prefix
+ expect(gitlabCi).toContain('.backend-drift-detection');
+ expect(gitlabCi).toContain('backend-drift:production');
+ expect(gitlabCi).toContain('backend-drift:summary');
+
+ expect(gitlabCi).toMatchSnapshot();
+ });
+
it('should support custom docker image', () => {
new GitLabDriftDetectionWorkflow(project, {
image: 'custom/node:18-aws',
diff --git a/test/github.test.ts b/test/github.test.ts
index 9c577bf..15585a7 100644
--- a/test/github.test.ts
+++ b/test/github.test.ts
@@ -699,6 +699,126 @@ test('Github snapshot with jump roles', () => {
expect(snapshot['src/app.ts']).toContain('StringParameter');
});
+test('Github snapshot with explicit pipelineName', () => {
+ const p = new AwsCdkTypeScriptApp({
+ cdkVersion: '2.132.0',
+ defaultReleaseBranch: 'main',
+ name: 'testapp',
+ });
+
+ new GithubCDKPipeline(p, {
+ pipelineName: 'backend',
+ iamRoleArns: {
+ synth: 'synthRole',
+ assetPublishing: 'publishRole',
+ deployment: {
+ dev: 'devRole',
+ prod: 'prodRole',
+ },
+ },
+ pkgNamespace: '@assembly',
+ stages: [{
+ name: 'dev',
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }, {
+ name: 'prod',
+ manualApproval: true,
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }],
+ featureStages: {
+ env: {
+ account: '123456789012',
+ region: 'us-east-1',
+ },
+ },
+ independentStages: [{
+ name: 'sandbox',
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }],
+ });
+
+ const snapshot = synthSnapshot(p);
+
+ // Workflow files should be prefixed
+ expect(snapshot['.github/workflows/backend-deploy.yml']).toBeDefined();
+ expect(snapshot['.github/workflows/backend-deploy-feature.yml']).toBeDefined();
+ expect(snapshot['.github/workflows/backend-destroy-feature.yml']).toBeDefined();
+ expect(snapshot['.github/workflows/backend-release-prod.yml']).toBeDefined();
+ expect(snapshot['.github/workflows/backend-deploy-sandbox.yml']).toBeDefined();
+
+ // Old unprefixed files should not exist
+ expect(snapshot['.github/workflows/deploy.yml']).toBeUndefined();
+ expect(snapshot['.github/workflows/deploy-feature.yml']).toBeUndefined();
+ expect(snapshot['.github/workflows/destroy-feature.yml']).toBeUndefined();
+ expect(snapshot['.github/workflows/release-prod.yml']).toBeUndefined();
+ expect(snapshot['.github/workflows/deploy-sandbox.yml']).toBeUndefined();
+
+ // Verify artifact names and concurrency groups are prefixed
+ const deployYml = snapshot['.github/workflows/backend-deploy.yml'];
+ expect(deployYml).toContain('backend-cloud-assembly');
+ expect(deployYml).toContain('backend-cdk-outputs-dev');
+ expect(deployYml).toContain('backend-deploy-dev');
+
+ const featureDeployYml = snapshot['.github/workflows/backend-deploy-feature.yml'];
+ expect(featureDeployYml).toContain('backend-cdk-outputs-feature');
+ expect(featureDeployYml).toContain('backend-deploy-feature-');
+
+ const featureDestroyYml = snapshot['.github/workflows/backend-destroy-feature.yml'];
+ expect(featureDestroyYml).toContain('backend-destroy-feature-');
+
+ const releaseYml = snapshot['.github/workflows/backend-release-prod.yml'];
+ expect(releaseYml).toContain('backend-cdk-outputs-prod');
+ expect(releaseYml).toContain('backend-deploy-prod');
+
+ const sandboxYml = snapshot['.github/workflows/backend-deploy-sandbox.yml'];
+ expect(sandboxYml).toContain('backend-cdk-outputs-sandbox');
+ expect(sandboxYml).toContain('backend-deploy-sandbox');
+
+ expect(deployYml).toMatchSnapshot();
+ expect(featureDeployYml).toMatchSnapshot();
+ expect(featureDestroyYml).toMatchSnapshot();
+ expect(releaseYml).toMatchSnapshot();
+ expect(sandboxYml).toMatchSnapshot();
+});
+
+test('Github snapshot with no pipelineName on standalone project', () => {
+ const p = new AwsCdkTypeScriptApp({
+ cdkVersion: '2.132.0',
+ defaultReleaseBranch: 'main',
+ name: 'testapp',
+ });
+
+ new GithubCDKPipeline(p, {
+ iamRoleArns: {
+ synth: 'synthRole',
+ assetPublishing: 'publishRole',
+ },
+ stages: [{
+ name: 'dev',
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }],
+ });
+
+ const snapshot = synthSnapshot(p);
+
+ // Standalone project without parent should have no prefix
+ expect(snapshot['.github/workflows/deploy.yml']).toBeDefined();
+ expect(snapshot['.github/workflows/deploy.yml']).toContain('cloud-assembly');
+ expect(snapshot['.github/workflows/deploy.yml']).not.toContain('testapp-cloud-assembly');
+});
+
test('Github snapshot with pnpm package manager', () => {
const p = new AwsCdkTypeScriptApp({
cdkVersion: '2.132.0',
diff --git a/test/gitlab.test.ts b/test/gitlab.test.ts
index 691a233..4feb03a 100644
--- a/test/gitlab.test.ts
+++ b/test/gitlab.test.ts
@@ -276,4 +276,102 @@ test('Gitlab snapshot with versioning enabled', () => {
expect(snapshot['src/app.ts']).toContain('addVersioningToStack');
expect(snapshot['src/app.ts']).toContain('CfnOutput');
expect(snapshot['src/app.ts']).toContain('StringParameter');
+});
+
+test('Gitlab snapshot with explicit pipelineName', () => {
+ const p = new AwsCdkTypeScriptApp({
+ cdkVersion: '2.132.0',
+ defaultReleaseBranch: 'main',
+ name: 'testapp',
+ });
+
+ new GitlabCDKPipeline(p, {
+ pipelineName: 'backend',
+ iamRoleArns: {
+ synth: 'synthRole',
+ assetPublishing: 'publishRole',
+ deployment: {
+ dev: 'devRole',
+ prod: 'prodRole',
+ },
+ },
+ stages: [{
+ name: 'dev',
+ diffType: CdkDiffType.FAST,
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }, {
+ name: 'prod',
+ manualApproval: true,
+ diffType: CdkDiffType.FULL,
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }],
+ independentStages: [{
+ name: 'sandbox',
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ deployOnPush: true,
+ }],
+ });
+
+ const snapshot = synthSnapshot(p);
+ const gitlabCi = snapshot['.gitlab-ci.yml'];
+
+ // Verify job names are prefixed
+ expect(gitlabCi).toContain('backend-synth');
+ expect(gitlabCi).toContain('backend-publish_assets');
+ expect(gitlabCi).toContain('backend-diff-dev');
+ expect(gitlabCi).toContain('backend-deploy-dev');
+ expect(gitlabCi).toContain('backend-deploy-prod');
+ expect(gitlabCi).toContain('backend-deploy-sandbox');
+
+ // Verify hidden job names preserve dot prefix
+ expect(gitlabCi).toContain('.backend-aws_base');
+ expect(gitlabCi).toContain('.backend-artifacts_cdk');
+ expect(gitlabCi).toContain('.backend-artifacts_cdkdeploy');
+
+ // Verify extends references use the prefixed hidden names
+ expect(gitlabCi).not.toContain('extends:\n - .aws_base');
+
+ expect(gitlabCi).toMatchSnapshot();
+});
+
+test('Gitlab snapshot with no pipelineName on standalone project', () => {
+ const p = new AwsCdkTypeScriptApp({
+ cdkVersion: '2.132.0',
+ defaultReleaseBranch: 'main',
+ name: 'testapp',
+ });
+
+ new GitlabCDKPipeline(p, {
+ iamRoleArns: {
+ synth: 'synthRole',
+ assetPublishing: 'publishRole',
+ deployment: {
+ dev: 'devRole',
+ },
+ },
+ stages: [{
+ name: 'dev',
+ env: {
+ account: '123456789012',
+ region: 'eu-central-1',
+ },
+ }],
+ });
+
+ const snapshot = synthSnapshot(p);
+ const gitlabCi = snapshot['.gitlab-ci.yml'];
+
+ // Standalone project without parent should have no prefix
+ expect(gitlabCi).toContain('.aws_base');
+ expect(gitlabCi).not.toContain('testapp-synth');
+ expect(gitlabCi).not.toContain('.testapp-aws_base');
});
\ No newline at end of file