Search code examples
aws-cdkaws-codepipelinecicd

Adding a stage to CodePipeline throws errors


I am creating a code pipeline as follows -

import * as cdk from "aws-cdk-lib";
import { CodeBuildStep, CodePipeline, CodePipelineSource } from "aws-cdk-lib/pipelines";
...

export class Pipeline extends cdk.Stack {
  constructor(scope: Construct, id: string, props?: cdk.StackProps) {
    super(scope, id, props);

    ...

    const pipeline = new CodePipeline(this, "Pipeline", {
      pipelineName: "pipeline",
      synth: new CodeBuildStep("SynthStep", {
        input: CodePipelineSource.codeCommit(repo, "mainline"),
        buildEnvironment: {
          computeType: CodeBuild.ComputeType.MEDIUM,
          buildImage: CodeBuild.LinuxBuildImage.STANDARD_5_0,
        },
        partialBuildSpec: buildSpec,
        commands: [],
        role: codeBuildSynthRole,
        primaryOutputDirectory: "cdk/cdk.out",
      }),
      crossAccountKeys: true,
      selfMutation: true,
      dockerEnabledForSelfMutation: true,
    });

    const review = new ReviewPipelineStage(this, "Review", {
      sourceFileSet: pipeline.cloudAssemblyFileSet,
    });

    const reviewStage = pipeline.addStage(review);

    const gitleaksReviewAction = new GitleaksReviewAction(
      this,
      "GitleaksReviewAction",
      {
        sourceFileSet: pipeline.cloudAssemblyFileSet,
      }
    );

    reviewStage.addPost(gitleaksReviewAction.action);
    gitleaksReviewAction.gitleaksImage.repository.grantPull(
      gitleaksReviewAction.action.project
    );
  }
}

I am trying to add a stage for Gitleaks and the GitleaksReviewAction construct is as follows -

export interface GitleaksReviewActionProps {
  sourceFileSet: FileSet;
}

export class GitleaksReviewAction extends Construct {
  public readonly action: CodeBuildStep;
  public readonly gitleaksImage: DockerImageAsset;

  constructor(scope: Construct, id: string, props: GitleaksReviewActionProps) {
    super(scope, id);
    this.gitleaksImage = new DockerImageAsset(this, "gitleaksDockerAsset", {
      directory: path.join(__dirname, "../assets/gitleaks"),
    });
    this.action = new CodeBuildStep("Gitleaks", {
      input: props.sourceFileSet,
      commands: [
        "find . -type d -exec chmod 777 {} \\;",
        "find . -type f -exec chmod 666 {} \\;",
        `aws ecr get-login-password --region $AWS_REGION | docker login -u AWS --password-stdin ${this.gitleaksImage.imageUri}`,
        `docker run -v $(pwd):/repo ${this.gitleaksImage.imageUri} --path=/repo --repo-config-path=config/gitleaks/gitleaks.toml --verbose`,
      ],
      buildEnvironment: {
        buildImage: codebuild.LinuxBuildImage.STANDARD_5_0,
        privileged: true,
      },
    });
  }
}

The ReviewPipelineStage is as follows -

export interface ReviewPipelineStageProps extends StageProps {
  sourceFileSet: FileSet;
}

export class ReviewPipelineStage extends Stage {
  constructor(scope: Construct, id: string, props: ReviewPipelineStageProps) {
    super(scope, id, props);

    new GitleaksReviewAction(this, "GitleaksReviewAction", {
      sourceFileSet: props.sourceFileSet,
    });
  }
}

As i do a cdk synth i get an error -

throw new Error(`${construct.constructor?.name ?? 'Construct'} at '${Node.of(construct).path}' should be created in the scope of a Stack, but no Stack found`);

I am not sure if i should be using the other construct aws_codepipeline to define the stages or is this the right way to create a stage. Any ideas?


Solution

  • The issue is that you're creating a Construct in the scope of a Stage. You can't do that, you can only create Stacks in the scope of a Stage. Constructs have to be created in the scope of a Stack.

    The issue is here:

    export class ReviewPipelineStage extends Stage {
      constructor(scope: Construct, id: string, props: ReviewPipelineStageProps) {
        super(scope, id, props);
    
        new GitleaksReviewAction(this, "GitleaksReviewAction", {
          sourceFileSet: props.sourceFileSet,
        });
      }
    }
    

    this is a Stage, not a Stack.

    To fix, this, you have to create a Stack in your stage, and create your Constructs in there.