Open dleavitt opened 4 months ago
Reproducible using following steps:
Settings
.
import * as cdk from 'aws-cdk-lib';
import * as codebuild from "aws-cdk-lib/aws-codebuild";
import * as codepipeline from "aws-cdk-lib/aws-codepipeline";
import * as codepipeline_actions from "aws-cdk-lib/aws-codepipeline-actions";
import * as ec2 from "aws-cdk-lib/aws-ec2";
import * as logs from 'aws-cdk-lib/aws-logs';
import { Construct } from 'constructs';
export class Issue31000Stack extends cdk.Stack { constructor(scope: Construct, id: string, props?: cdk.StackProps) { super(scope, id, props);
const vpc = ec2.Vpc.fromLookup(this, 'MyVpc', {isDefault: true});
new PipelineRepro(this, 'PipelineDemo',{
owner: '<<repository-owner>>',
repo: '<<repository-name>>',
connectionArn: 'arn:aws:codestar-connections:<<region>>:<<account-id>>:connection/<<connection-arn-guid>>',
vpc: vpc
});
} }
export interface PipelineReproProps { owner: string; repo: string; connectionArn: string; vpc: ec2.IVpc; }
export class PipelineRepro extends Construct { pipeline: codepipeline.Pipeline;
constructor( scope: Construct, id: string, { owner, repo, connectionArn, vpc }: PipelineReproProps, ) { super(scope, id);
const sourceOutput = new codepipeline.Artifact();
this.pipeline = new codepipeline.Pipeline(this, "Pipeline");
// Source Stage
//
const pullSourceAction =
new codepipeline_actions.CodeStarConnectionsSourceAction({
actionName: "PullSource",
connectionArn,
output: sourceOutput,
owner,
repo,
codeBuildCloneOutput: true,
});
this.pipeline.addTrigger({
providerType: codepipeline.ProviderType.CODE_STAR_SOURCE_CONNECTION,
gitConfiguration: {
sourceAction: pullSourceAction,
pullRequestFilter: [
{
events: [
codepipeline.GitPullRequestEvent.OPEN,
codepipeline.GitPullRequestEvent.UPDATED,
],
branchesIncludes: ["*"],
},
],
},
});
this.pipeline.addStage({
stageName: "Source",
actions: [pullSourceAction],
});
// Build Stage
//
const projectLogGroup = new logs.LogGroup(this, "ProjectLogGroup");
const project = new codebuild.PipelineProject(this, "Project", {
vpc,
buildSpec: codebuild.BuildSpec.fromObject({
version: 0.2,
env: { "exported-variables": ["GIT_BRANCH_NAME"] },
phases: {
build: { commands: ["env"] },
},
}),
logging: {
cloudWatch: {
logGroup: projectLogGroup
}
},
});
// will only succeed as part of the release created when the pipeline is
// first created, will fail when triggered through a pull request
const commitIdAction = new codepipeline_actions.CodeBuildAction({
actionName: "UsesBranchName",
input: sourceOutput,
project,
environmentVariables: {
GIT_BRANCH_NAME: { value: pullSourceAction.variables.branchName },
},
});
// will fail when release created and if doing a manual release. will
// succeed when triggered through a pull request
const sourceCommitIdAction = new codepipeline_actions.CodeBuildAction({
actionName: "UsesDestinationBranchName",
input: sourceOutput,
project,
environmentVariables: {
GIT_BRANCH_NAME: {
// workaround for accessing variable
value: pullSourceAction["variableExpression"](
"DestinationBranchName",
),
},
},
});
this.pipeline.addStage({
stageName: "Build",
actions: [commitIdAction, sourceCommitIdAction],
});
} }
... ... const app = new cdk.App();
new Issue31000Stack(app, 'Issue31000Stack', {
env: { account: '<
- Create a pull request in the source repository. CodePipeline will fail with the error `An action in this pipeline failed because one or more variables could not be resolved: Action name=UsesBranchName. This can result when a variable is referenced that does not exist. Validate the configuration for this action.`
<img width="1648" alt="Screenshot 2024-08-01 at 3 57 59 PM" src="https://github.com/user-attachments/assets/bc49f912-055e-463d-952c-b04d81ddc1a0">
Describe the bug
If you try to use
CodeStarConnectionsSourceAction.variables.branchName
on an execution triggered by a pull request, your build will fail with:Detail
5604 added a
.variables
getter to a number ofcodepipeline.Action
subclasses includingCodeStarConnectionsSourceAction
. It's hardcoded to return a particular set of variables, and seems to be the only public way to get at the action's variables.https://github.com/aws/aws-cdk/blob/9295a85a8fb893d7f5eae06108b68df864096c4c/packages/aws-cdk-lib/aws-codepipeline-actions/lib/codestar-connections/source-action.ts#L119-L128
These variables are correct in some cases, but aren't correct if the execution was triggered by a pull request (see screenshots below.)
With a
pullRequestFilter
in place,BranchName
will be unavailable, but four additional variables will be set:DestinationBranchName
,PullRequestId
,PullRequestTitle
,SourceBranchName
. There's no good way to get at these right now.Expected Behavior
One of the below:
a.
variables
should return the variables that the source action actually exports (likely impossible to implement at CDK level.) b.variables
should return only variables that are always safe to use. I'm not sure if there's a public spec indicating what variables are returned under what circumstances. ButBranchName
is not always defined by the source action and therefore isn't safe to us.More importantly, since the CDK doesn't/can't know what the possible variables are, I'd expect the underlying
variableExpression(variableName: string)
should be publicly accessible, like it is on some of the other actions: https://github.com/aws/aws-cdk/blob/9295a85a8fb893d7f5eae06108b68df864096c4c/packages/aws-cdk-lib/aws-codepipeline-actions/lib/codebuild/build-action.ts#L145-L147https://github.com/aws/aws-cdk/blob/9295a85a8fb893d7f5eae06108b68df864096c4c/packages/aws-cdk-lib/aws-codepipeline/lib/action.ts#L428-L431
Current Behavior
The cdk-provided
variables
getter misses some variables and returns others that may not exist. It doesn't provide any way to get at the missing ones.For missed variables, a workaround is to call the protected
variableExpression
method viaaction["variableExpression"]("DestinationBranchName")
or similar.For returned variables that don't exist (
BranchName
), workaround is not to use it.Reproduction Steps
Below is a construct that will reproduce the issue. Unfortunately, due to the way CodePipeline works, the minimal reproduction is not especially concise.
To use it, you'll need a CDK stack with a VPC and a CodeConnection to a git repo (I used a Github repo.)
Once you've got the stack deployed, open a pull request in the repo. It should trigger an execution. The
UsesBranchName
build action will fail, because the source action doesn't export theBranchName
variable.Now try manually triggering a execution with the "Release Change" button in the console. Here, the
UsesBranchName
build action will succeed, but theUsesDestinationBranchName
will fail, because theDestinationBranchName
won't have been defined (this will also happen when the pipeline is first created.)Reproduction: https://gist.github.com/dleavitt/7950f5073bb0ebe2f3fa5049a2f44ab8
Possible Solution
variable(variableName: string): string
method toCodeStarConnectionsSourceAction
, with the same implementation like this: https://github.com/aws/aws-cdk/blob/9295a85a8fb893d7f5eae06108b68df864096c4c/packages/aws-cdk-lib/aws-codepipeline-actions/lib/codebuild/build-action.ts#L145-L147Maybe add it directly to
Action
(or makevariableExpression
public) if there are other actions where the list of variables could be dynamic.BranchName
fromCodeStarConnectionsSourceAction.variables()
, since it's not always present and if missing attempting to use it will cause the build to fail.Additional Information/Context
From what I can tell, there's an underlying issue with the implementation of the CodeStarSourceConnection Action provider in CodePipeline. For a given pipeline and action:
Therefore the only variables that can be safely used are ones available in all cases (which
BranchName
is not.)I would love to be wrong about this, let me know if there's a workaround!
Variables from a non-pr trigger
Variables from a pull request trigger
CDK CLI Version
2.150.0 (build 3f93027)
Framework Version
No response
Node.js Version
v20.11.1
OS
MacOS 14.3 (23D56)
Language
TypeScript
Language Version
Typescript (5.4.5)
Other information
No response