From c9b394af24c0b981ea0db0686b45cfaca7927515 Mon Sep 17 00:00:00 2001 From: Alex Kennedy Date: Mon, 5 Oct 2026 04:23:21 +0000 Subject: [PATCH] feat(pipeline): add preDeploySteps to run steps at the start of every deploy job Co-Authored-By: Claude Opus 5.5 Claude-Session: https://claude.ai/code/session_014QPF7k3P56ZTy24Fs6cfpi --- API.md | 14 +++++ src/pipeline.ts | 11 ++++ test/__snapshots__/github.test.ts.snap | 73 ++++++++++++++++++++++++++ test/github.test.ts | 28 ++++++++++ 4 files changed, 126 insertions(+) diff --git a/API.md b/API.md index 9682d84c..8444d2c9 100644 --- a/API.md +++ b/API.md @@ -1781,6 +1781,7 @@ const gitHubWorkflowProps: GitHubWorkflowProps = { ... } | jobSettings | JobSettings | Job level settings that will be applied to all jobs in the workflow, including synth and asset deploy jobs. | | postBuildSteps | JobStep[] | GitHub workflow steps to execute after build. | | preBuildSteps | JobStep[] | GitHub workflow steps to execute before build. | +| preDeploySteps | JobStep[] | GitHub workflow steps to execute at the start of every deploy job, before AWS credentials are configured. | | requireApproval | string | What approval level is required for deployments? | | runner | Runner | The type of runner that the entire workflow runs on. | | workflowName | string | Name of the workflow. | @@ -1942,6 +1943,19 @@ GitHub workflow steps to execute before build. --- +##### `preDeploySteps`Optional + +```typescript +public readonly preDeploySteps: JobStep[]; +``` + +- *Type:* JobStep[] +- *Default:* [] + +GitHub workflow steps to execute at the start of every deploy job, before AWS credentials are configured. + +--- + ##### `requireApproval`Optional ```typescript diff --git a/src/pipeline.ts b/src/pipeline.ts index 3ea4b30a..e775b101 100644 --- a/src/pipeline.ts +++ b/src/pipeline.ts @@ -105,6 +105,14 @@ export interface GitHubWorkflowProps extends PipelineBaseProps, AwsCredsCommonPr */ readonly postBuildSteps?: github.JobStep[]; + /** + * GitHub workflow steps to execute at the start of every deploy job, before + * AWS credentials are configured. + * + * @default [] + */ + readonly preDeploySteps?: github.JobStep[]; + /** * What approval level is required for deployments? By default this is * `never` to ensure that all automated deployments succeed. @@ -158,6 +166,7 @@ export class GitHubWorkflow extends PipelineBase { private readonly buildRunner: github.Runner; private readonly preBuildSteps: github.JobStep[]; private readonly postBuildSteps: github.JobStep[]; + private readonly preDeploySteps: github.JobStep[]; private readonly deployArgs: string[]; private readonly diffFirst: boolean; private readonly jobOutputs: Record = {}; @@ -185,6 +194,7 @@ export class GitHubWorkflow extends PipelineBase { this.buildContainer = props.buildContainer; this.preBuildSteps = props.preBuildSteps ?? []; this.postBuildSteps = props.postBuildSteps ?? []; + this.preDeploySteps = props.preDeploySteps ?? []; this.jobSettings = props.jobSettings; this.diffFirst = props.diffFirst ?? false; this.deployArgs = props.deployArgs ?? []; @@ -484,6 +494,7 @@ export class GitHubWorkflow extends PipelineBase { needs: this.renderDependencies(node), runsOn: this.runner.runsOn, steps: [ + ...this.preDeploySteps, ...this.stepsToUnpackageAssembly, ...awsCredentials.credentialSteps(region), ...this.stepsToDeploy(stack), diff --git a/test/__snapshots__/github.test.ts.snap b/test/__snapshots__/github.test.ts.snap index 32424c0e..83fba126 100644 --- a/test/__snapshots__/github.test.ts.snap +++ b/test/__snapshots__/github.test.ts.snap @@ -824,6 +824,79 @@ jobs: " `; +exports[`single wave/stage/stack - with pre-deploy steps 1`] = ` +"# AUTOMATICALLY GENERATED FILE, DO NOT EDIT MANUALLY. +# Generated by AWS CDK and [cdk-pipelines-github](https://github.com/cdklabs/cdk-pipelines-github) + +name: deploy +on: + push: + branches: + - main + workflow_dispatch: {} +jobs: + Build-Build: + name: Synthesize + permissions: + contents: read + id-token: none + runs-on: ubuntu-latest + needs: [] + env: {} + steps: + - name: Checkout + uses: actions/checkout@v4 + - name: Authenticate Via GitHub Secrets + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: us-east-1 + role-duration-seconds: 1800 + role-skip-session-tagging: true + aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }} + - name: Build + run: "" + - name: Package cdk.out + run: tar -zcf cdk.out.tgz github.out + - name: Upload cdk.out + uses: actions/upload-artifact@v4 + with: + name: cdk.out + path: cdk.out.tgz + include-hidden-files: true + retention-days: 1 + if-no-files-found: error + MyStack-MyStack-Deploy: + name: Deploy MyStack/MyStack + permissions: + contents: read + id-token: none + needs: + - Build-Build + runs-on: ubuntu-latest + steps: + - name: Pre Deploy + run: echo pre-deploy + - name: Download cdk.out + uses: actions/download-artifact@v4 + with: + name: cdk.out + - name: Unpackage cdk.out + run: tar -zxf cdk.out.tgz + - name: Authenticate Via GitHub Secrets + uses: aws-actions/configure-aws-credentials@v4 + with: + aws-region: us-east-1 + role-duration-seconds: 1800 + role-skip-session-tagging: true + aws-access-key-id: \${{ secrets.AWS_ACCESS_KEY_ID }} + aws-secret-access-key: \${{ secrets.AWS_SECRET_ACCESS_KEY }} + - id: Deploy + run: npx cdk --app github.out deploy MyStack/MyStack --require-approval=never + --exclusively +" +`; + exports[`single wave/stage/stack 1`] = ` "# AUTOMATICALLY GENERATED FILE, DO NOT EDIT MANUALLY. # Generated by AWS CDK and [cdk-pipelines-github](https://github.com/cdklabs/cdk-pipelines-github) diff --git a/test/github.test.ts b/test/github.test.ts index d6fca241..6b90c71e 100644 --- a/test/github.test.ts +++ b/test/github.test.ts @@ -315,6 +315,34 @@ test('single wave/stage/stack', () => { expect(readFileSync(pipeline.workflowPath, 'utf-8')).toMatchSnapshot(); }); }); +test('single wave/stage/stack - with pre-deploy steps', () => { + withTemporaryDirectory((dir) => { + const pipeline = new GitHubWorkflow(app, 'Pipeline', { + workflowPath: `${dir}/.github/workflows/deploy.yml`, + synth: new ShellStep('Build', { + commands: [], + }), + preDeploySteps: [{ name: 'Pre Deploy', run: 'echo pre-deploy' }], + }); + + const stage = new Stage(app, 'MyStack', { + env: { account: '111111111111', region: 'us-east-1' }, + }); + + new Stack(stage, 'MyStack'); + + pipeline.addStage(stage); + + app.synth(); + + const workflow = readFileSync(pipeline.workflowPath, 'utf-8'); + const deployJob = workflow.slice(workflow.indexOf('name: Deploy MyStack/MyStack')); + expect(deployJob.indexOf('name: Pre Deploy')).toBeGreaterThan(-1); + expect(deployJob.indexOf('name: Pre Deploy')).toBeLessThan(deployJob.indexOf('name: Download cdk.out')); + expect(workflow.split('name: Pre Deploy').length - 1).toBe(1); + expect(workflow).toMatchSnapshot(); + }); +}); test('single wave/stage/stack - with diff enabled', () => { withTemporaryDirectory((dir) => { const pipeline = new GitHubWorkflow(app, 'Pipeline', {