'How do I get the source artifact from CodePipeline on CDK?

I'm using the official AWS documentation to create a pipeline using CDK: https://docs.aws.amazon.com/cdk/latest/guide/cdk_pipeline.html#cdk_pipeline_define (with a slight variation to the docs, where I used a CodeStar connection, as the code comments recommend)

This automatically creates a self-mutating pipeline, with three stages -- Source, Synth, and UpdatePipeline. That's great.

I would like to add a new stage with a CodeBuild action. I'd like the CodeBuild action to be based on the buildspec.yml file in the source directory.

On the console, I can easily do this by clicking "Add new stage", "Add action", and selecting the input artifact from the dropdown menu.

However, on CDK, with this recommended setup there's no easy way to get access to the input artifacts.

I managed to do it by forcing buildPipeline() and doing this:

import * as cdk from "@aws-cdk/core";
import {
  CodePipeline,
  ShellStep,
  CodePipelineSource,
} from "@aws-cdk/pipelines";
import * as codebuild from "@aws-cdk/aws-codebuild";
import * as codepipelineActions from "@aws-cdk/aws-codepipeline-actions";


export class PipelineStack extends cdk.Stack {
  public readonly source: cdk.CfnOutput

  constructor(scope: cdk.Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    const source = CodePipelineSource.connection("someuser/somerepo", "master", {
      connectionArn: "arn:aws:codestar-connections:us-east-1:REDACTED:connection/REDACTED"
    });

    const synthShellStep = new ShellStep("Synth", {
      input: source,
      commands: [
          "cd infrastructure",
          "npm run ci",
          "npm run build",
          "npx cdk synth"
      ],
      "primaryOutputDirectory": "infrastructure/cdk.out"
    });

    const pipeline = new CodePipeline(this, "Pipeline", {
      pipelineName: "FancyPipeline",
      synth: synthShellStep
    });

    // Need to build the pipeline to access the
    // source artifact
    pipeline.buildPipeline();

    const sourceStage = pipeline.pipeline.stage("Source");
    if (sourceStage) {
      const sourceOutputs = sourceStage.actions[0].actionProperties.outputs;
      if (sourceOutputs && sourceOutputs.length > 0) {
        const sourceArtifact = sourceOutputs[0];


        const codeBuildProject = new codebuild.PipelineProject(this, 'DockerBuildProject', {
          environment: {
            privileged: true
          }
        });

        const buildAction = new codepipelineActions.CodeBuildAction({
          actionName: 'DockerBuild',
          project: codeBuildProject,
          input: sourceArtifact,
          environmentVariables: {
            AWS_DEFAULT_REGION: {
              value: this.region
            },
            AWS_ACCOUNT_ID: {
              value: this.account
            },
            IMAGE_REPO_NAME: {
              value: "somereponame"
            },
            IMAGE_TAG: {
              value: "latest"
            }
          }
        });

        pipeline.pipeline.addStage({
          stageName: "DockerBuildStage",
          actions: [buildAction],
        });
      }
    }
  }
}

But this feels overall pretty awkward, and I can't call addStage() on the CodePipeline construct anymore. Surely there's a better way to do what I'm trying to do?

Any help/advice would be appreciated. Thanks.



Solution 1:[1]

The codepipelineActions.CodeBuildAction method accepts a parameter titled outputs which is for the list of output Artifacts for this action. TypeScript source here. I think it's easier to follow in the python version of the docs though (link here).

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 shredGnar