Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add mutable defaultArtifact attribute to Pipeline class to use as default inputArtifact for all pipeline stages following build or sign stage #1473

Merged
merged 5 commits into from
Oct 11, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
101 changes: 101 additions & 0 deletions lib/__tests__/pipeline.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,9 @@ import {
aws_codepipeline_actions as cpipeline_actions,
} from 'aws-cdk-lib';
import { Capture, Template, Match } from 'aws-cdk-lib/assertions';
import { Role } from 'aws-cdk-lib/aws-iam';
import { Function } from 'aws-cdk-lib/aws-lambda';
import { Bucket } from 'aws-cdk-lib/aws-s3';
import { Construct } from 'constructs';
import * as delivlib from '../../lib';
import { determineRunOrder } from '../../lib/util';
Expand Down Expand Up @@ -336,6 +339,104 @@ test('metricActionFailures', () => {
expect(stack.resolve(pipeline.metricActionFailures({}))).toEqual(expectedMetrics);
});

test('signing output artifact is used as input artifact for all stages after signing stage', () => {
// GIVEN
const app = new App();
const stack = new Stack(app, 'TestStack');
const signingLambda = Function.fromFunctionName(stack, 'SigningLambda', 'signing-lambda');
const signingBucket = Bucket.fromBucketName(stack, 'SigningBucket', 'signing-bucket');
const accessRole = Role.fromRoleName(stack, 'AccessRole', 'access-role');
const pipeline = new delivlib.Pipeline(stack, 'Pipeline', {
repo: createTestRepo(stack),
pipelineName: 'TestPipeline',
});

// WHEN
pipeline.signNuGetWithSigner({
signingLambda,
signingBucket,
accessRole,
});

pipeline.publishToNuGet({
nugetApiKeySecret: {
secretArn: 'arn:aws:secretsmanager:us-east-1:123456789012:secret:nuget-secret',
},
});

pipeline.addTest('test1', {
scriptDirectory: path.join(__dirname, 'delivlib-tests/assume-role'),
entrypoint: 'test.sh',
});

// THEN
Template.fromStack(stack).hasResourceProperties('AWS::CodePipeline::Pipeline', {
Stages: [
{
Actions: [
Match.objectLike({
InputArtifacts: Match.absent(),
OutputArtifacts: [
{ Name: 'Source' },
],
}),
],
Name: 'Source',
},
{
Actions: [
Match.objectLike({
InputArtifacts: [
{ Name: 'Source' },
],
OutputArtifacts: [
{ Name: 'Artifact_Build_Build' },
],
}),
],
Name: 'Build',
},
{
Actions: [
Match.objectLike({
InputArtifacts: [
{ Name: 'Artifact_Build_Build' },
],
OutputArtifacts: [
{ Name: 'Artifact_Sign_NuGetSigningSign' },
],
}),
],
Name: 'Sign',
},
{
Actions: [
Match.objectLike({
InputArtifacts: [
{ Name: 'Artifact_Sign_NuGetSigningSign' },
],
OutputArtifacts: Match.absent(),
}),
],
Name: 'Publish',
},
{
Actions: [
Match.objectLike({
InputArtifacts: [
{ Name: 'Artifact_Sign_NuGetSigningSign' },
],
OutputArtifacts: [
{ Name: 'Artifact_c8383dfefa10c0c326ab2bfac48dcf263ea515d7e1' },
],
}),
],
Name: 'Test',
},
],
});
});

function createTestPipelineForConcurrencyTests(stack: Stack, props?: delivlib.PipelineProps) {
const pipeline = new delivlib.Pipeline(stack, 'Pipeline', {
repo: createTestRepo(stack),
Expand Down
10 changes: 5 additions & 5 deletions lib/__tests__/signing.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,13 @@ describe('with standard pipeline', () => {
// GIVEN
const signingBucket = Bucket.fromBucketName(stack, 'SigningBucket', 'signing-bucket');
const signingLambda = Function.fromFunctionName(stack, 'SigningLambda', 'signing-lambda');
const signingAccessRole = Role.fromRoleName(stack, 'SigningAccessRole', 'signing-access-role');
const accessRole = Role.fromRoleName(stack, 'AccessRole', 'access-role');

// WHEN
pipeline.signNuGetWithSigner({
signingBucket,
signingLambda,
signingAccessRole,
accessRole,
});

// THEN
Expand All @@ -51,7 +51,7 @@ describe('with standard pipeline', () => {
{
Name: 'SCRIPT_S3_KEY',
Type: 'PLAINTEXT',
Value: '1ccec5da3e38e2c229307a68b2feb159deb67f714ce2209b3c680115486b707f.zip',
Value: '304990045086f467d5effaa1d1aa90d3f19411750a41f9cb37ab387399f92e39.zip',
},
{
Name: 'SIGNING_BUCKET_NAME',
Expand Down Expand Up @@ -83,7 +83,7 @@ describe('with standard pipeline', () => {
},
},
{
Name: 'SIGNING_ACCESS_ROLE_ARN',
Name: 'ACCESS_ROLE_ARN',
Type: 'PLAINTEXT',
Value: {
'Fn::Join': [
Expand All @@ -97,7 +97,7 @@ describe('with standard pipeline', () => {
{
Ref: 'AWS::AccountId',
},
':role/signing-access-role',
':role/access-role',
],
],
},
Expand Down
21 changes: 14 additions & 7 deletions lib/pipeline.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,7 @@ import * as signing from './signing';
import { determineRunOrder, flatMap } from './util';

const PUBLISH_STAGE_NAME = 'Publish';
const SIGINING_STAGE_NAME = 'Sign';
const SIGNING_STAGE_NAME = 'Sign';
const TEST_STAGE_NAME = 'Test';
const METRIC_NAMESPACE = 'CDK/Delivlib';
const FAILURE_METRIC_NAME = 'Failures';
Expand Down Expand Up @@ -207,6 +207,7 @@ export class Pipeline extends Construct {
public readonly pipeline: cpipeline.Pipeline;
private readonly branch: string;
private readonly notify?: sns.Topic;
private defaultArtifact: cpipeline.Artifact;
private stages: { [name: string]: cpipeline.IStage } = { };
private _signingOutput?: cpipeline.Artifact;

Expand Down Expand Up @@ -259,6 +260,7 @@ export class Pipeline extends Construct {
outputs: [buildOutput],
}));
this.buildOutput = buildOutput;
this.defaultArtifact = buildOutput;

if (props.notificationEmail) {
this.notify = new sns.Topic(this, 'NotificationsTopic');
Expand Down Expand Up @@ -311,7 +313,7 @@ export class Pipeline extends Construct {
const action = sh.addToPipeline(
stage,
options.actionName || `Action${id}`,
options.inputArtifact || this.buildOutput,
options.inputArtifact || this.defaultArtifact,
this.determineRunOrderForNewAction(stage));

if (options.failureNotification) {
Expand Down Expand Up @@ -346,7 +348,7 @@ export class Pipeline extends Construct {
const stage = this.getOrCreateStage(publishStageName);

publisher.addToPipeline(stage, `${publisher.node.id}Publish`, {
inputArtifact: options.inputArtifact || this.buildOutput,
inputArtifact: options.inputArtifact || this.defaultArtifact,
runOrder: this.determineRunOrderForNewAction(stage),
});
}
Expand All @@ -368,13 +370,14 @@ export class Pipeline extends Construct {
}

public addSigning(signer: signing.ISigner, options: signing.AddSigningOptions = {}) {
const signingStageName = options.stageName ?? SIGINING_STAGE_NAME;
const signingStageName = options.stageName ?? SIGNING_STAGE_NAME;
const stage = this.getOrCreateStage(signingStageName);

this._signingOutput = signer.addToPipeline(stage, `${signer.node.id}Sign`, {
inputArtifact: options.inputArtifact || this.buildOutput,
inputArtifact: options.inputArtifact || this.defaultArtifact,
runOrder: this.determineRunOrderForNewAction(stage),
});
this.defaultArtifact = this._signingOutput;
}

public signNuGetWithSigner(options: signing.SignNuGetWithSignerProps & signing.AddSigningOptions) {
Expand Down Expand Up @@ -623,7 +626,9 @@ export interface AddPublishOptions {
/**
* The input artifact to use
*
* @default Build output artifact
* @default Signing output artifact when a signing stage is added to the
* pipeline via `addSigning` or `signNuGetWithSigner`. Otherwise, the default
* will be the build output artifact.
*/
inputArtifact?: cpipeline.Artifact;

Expand Down Expand Up @@ -660,7 +665,9 @@ export interface AddShellableOptions extends ShellableProps {
/**
* The input artifact to use
*
* @default Build output artifact
* @default Signing output artifact when a signing stage is added to the
* pipeline via `addSigning` or `signNuGetWithSigner`. Otherwise, the default
* will be the build output artifact.
*/
inputArtifact?: cpipeline.Artifact;
}
4 changes: 2 additions & 2 deletions lib/signing.ts
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,7 @@ export interface SignNuGetWithSignerProps {
/**
* A role used provide access to the signing bucket and signing lambda
*/
readonly signingAccessRole: IRole;
readonly accessRole: IRole;

/**
* The build image to do the signing in
Expand All @@ -66,7 +66,7 @@ export class SignNuGetWithSigner extends Construct implements ISigner {
const environment = {
SIGNING_BUCKET_NAME: props.signingBucket.bucketName,
SIGNING_LAMBDA_ARN: props.signingLambda.functionArn,
SIGNING_ACCESS_ROLE_ARN: props.signingAccessRole.roleArn,
ACCESS_ROLE_ARN: props.accessRole.roleArn,
};

const shellable = new Shellable(this, 'Default', {
Expand Down
4 changes: 2 additions & 2 deletions lib/signing/nuget/sign.sh
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,8 @@ else
echo "!!! Neither an apt nor yum distribution - could not install jq, things might break!"
fi

if [ -n "${SIGNING_ACCESS_ROLE_ARN:-}" ]; then
ROLE=$(aws sts assume-role --role-arn "${SIGNING_ACCESS_ROLE_ARN:-}" --role-session-name "signer_access")
if [ -n "${ACCESS_ROLE_ARN:-}" ]; then
ROLE=$(aws sts assume-role --role-arn "${ACCESS_ROLE_ARN:-}" --role-session-name "signer_access")
export AWS_ACCESS_KEY_ID=$(echo $ROLE | jq -r .Credentials.AccessKeyId)
export AWS_SECRET_ACCESS_KEY=$(echo $ROLE | jq -r .Credentials.SecretAccessKey)
export AWS_SESSION_TOKEN=$(echo $ROLE | jq -r .Credentials.SessionToken)
Expand Down