@aws-cdk/aws-codepipeline
Version:
Better interface to AWS Code Pipeline
835 lines • 122 kB
JavaScript
"use strict";
var _a;
Object.defineProperty(exports, "__esModule", { value: true });
exports.Pipeline = void 0;
const jsiiDeprecationWarnings = require("../.warnings.jsii.js");
const JSII_RTTI_SYMBOL_1 = Symbol.for("jsii.rtti");
const notifications = require("@aws-cdk/aws-codestarnotifications");
const events = require("@aws-cdk/aws-events");
const iam = require("@aws-cdk/aws-iam");
const kms = require("@aws-cdk/aws-kms");
const s3 = require("@aws-cdk/aws-s3");
const core_1 = require("@aws-cdk/core");
const action_1 = require("./action");
const codepipeline_generated_1 = require("./codepipeline.generated");
const cross_region_support_stack_1 = require("./private/cross-region-support-stack");
const full_action_descriptor_1 = require("./private/full-action-descriptor");
const rich_action_1 = require("./private/rich-action");
const stage_1 = require("./private/stage");
const validation_1 = require("./private/validation");
class PipelineBase extends core_1.Resource {
/**
* Defines an event rule triggered by this CodePipeline.
*
* @param id Identifier for this event handler.
* @param options Additional options to pass to the event rule.
*/
onEvent(id, options = {}) {
const rule = new events.Rule(this, id, options);
rule.addTarget(options.target);
rule.addEventPattern({
source: ['aws.codepipeline'],
resources: [this.pipelineArn],
});
return rule;
}
/**
* Defines an event rule triggered by the "CodePipeline Pipeline Execution
* State Change" event emitted from this pipeline.
*
* @param id Identifier for this event handler.
* @param options Additional options to pass to the event rule.
*/
onStateChange(id, options = {}) {
const rule = this.onEvent(id, options);
rule.addEventPattern({
detailType: ['CodePipeline Pipeline Execution State Change'],
});
return rule;
}
bindAsNotificationRuleSource(_scope) {
return {
sourceArn: this.pipelineArn,
};
}
notifyOn(id, target, options) {
return new notifications.NotificationRule(this, id, {
...options,
source: this,
targets: [target],
});
}
notifyOnExecutionStateChange(id, target, options) {
return this.notifyOn(id, target, {
...options,
events: [
action_1.PipelineNotificationEvents.PIPELINE_EXECUTION_FAILED,
action_1.PipelineNotificationEvents.PIPELINE_EXECUTION_CANCELED,
action_1.PipelineNotificationEvents.PIPELINE_EXECUTION_STARTED,
action_1.PipelineNotificationEvents.PIPELINE_EXECUTION_RESUMED,
action_1.PipelineNotificationEvents.PIPELINE_EXECUTION_SUCCEEDED,
action_1.PipelineNotificationEvents.PIPELINE_EXECUTION_SUPERSEDED,
],
});
}
notifyOnAnyStageStateChange(id, target, options) {
return this.notifyOn(id, target, {
...options,
events: [
action_1.PipelineNotificationEvents.STAGE_EXECUTION_CANCELED,
action_1.PipelineNotificationEvents.STAGE_EXECUTION_FAILED,
action_1.PipelineNotificationEvents.STAGE_EXECUTION_RESUMED,
action_1.PipelineNotificationEvents.STAGE_EXECUTION_STARTED,
action_1.PipelineNotificationEvents.STAGE_EXECUTION_SUCCEEDED,
],
});
}
notifyOnAnyActionStateChange(id, target, options) {
return this.notifyOn(id, target, {
...options,
events: [
action_1.PipelineNotificationEvents.ACTION_EXECUTION_CANCELED,
action_1.PipelineNotificationEvents.ACTION_EXECUTION_FAILED,
action_1.PipelineNotificationEvents.ACTION_EXECUTION_STARTED,
action_1.PipelineNotificationEvents.ACTION_EXECUTION_SUCCEEDED,
],
});
}
notifyOnAnyManualApprovalStateChange(id, target, options) {
return this.notifyOn(id, target, {
...options,
events: [
action_1.PipelineNotificationEvents.MANUAL_APPROVAL_FAILED,
action_1.PipelineNotificationEvents.MANUAL_APPROVAL_NEEDED,
action_1.PipelineNotificationEvents.MANUAL_APPROVAL_SUCCEEDED,
],
});
}
}
/**
* An AWS CodePipeline pipeline with its associated IAM role and S3 bucket.
*
* @example
* // create a pipeline
* import * as codecommit from '@aws-cdk/aws-codecommit';
*
* const pipeline = new codepipeline.Pipeline(this, 'Pipeline');
*
* // add a stage
* const sourceStage = pipeline.addStage({ stageName: 'Source' });
*
* // add a source action to the stage
* declare const repo: codecommit.Repository;
* declare const sourceArtifact: codepipeline.Artifact;
* sourceStage.addAction(new codepipeline_actions.CodeCommitSourceAction({
* actionName: 'Source',
* output: sourceArtifact,
* repository: repo,
* }));
*
* // ... add more stages
*/
class Pipeline extends PipelineBase {
constructor(scope, id, props = {}) {
super(scope, id, {
physicalName: props.pipelineName,
});
this._stages = new Array();
this._crossRegionSupport = {};
this._crossAccountSupport = {};
try {
jsiiDeprecationWarnings._aws_cdk_aws_codepipeline_PipelineProps(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, Pipeline);
}
throw error;
}
validation_1.validateName('Pipeline', this.physicalName);
// only one of artifactBucket and crossRegionReplicationBuckets can be supplied
if (props.artifactBucket && props.crossRegionReplicationBuckets) {
throw new Error('Only one of artifactBucket and crossRegionReplicationBuckets can be specified!');
}
// @deprecated(v2): switch to default false
this.crossAccountKeys = props.crossAccountKeys ?? true;
this.enableKeyRotation = props.enableKeyRotation;
// Cross account keys must be set for key rotation to be enabled
if (this.enableKeyRotation && !this.crossAccountKeys) {
throw new Error("Setting 'enableKeyRotation' to true also requires 'crossAccountKeys' to be enabled");
}
this.reuseCrossRegionSupportStacks = props.reuseCrossRegionSupportStacks ?? true;
// If a bucket has been provided, use it - otherwise, create a bucket.
let propsBucket = this.getArtifactBucketFromProps(props);
if (!propsBucket) {
let encryptionKey;
if (this.crossAccountKeys) {
encryptionKey = new kms.Key(this, 'ArtifactsBucketEncryptionKey', {
// remove the key - there is a grace period of a few days before it's gone for good,
// that should be enough for any emergency access to the bucket artifacts
removalPolicy: core_1.RemovalPolicy.DESTROY,
enableKeyRotation: this.enableKeyRotation,
});
// add an alias to make finding the key in the console easier
new kms.Alias(this, 'ArtifactsBucketEncryptionKeyAlias', {
aliasName: this.generateNameForDefaultBucketKeyAlias(),
targetKey: encryptionKey,
removalPolicy: core_1.RemovalPolicy.DESTROY,
});
}
propsBucket = new s3.Bucket(this, 'ArtifactsBucket', {
bucketName: core_1.PhysicalName.GENERATE_IF_NEEDED,
encryptionKey,
encryption: encryptionKey ? s3.BucketEncryption.KMS : s3.BucketEncryption.KMS_MANAGED,
enforceSSL: true,
blockPublicAccess: new s3.BlockPublicAccess(s3.BlockPublicAccess.BLOCK_ALL),
removalPolicy: core_1.RemovalPolicy.RETAIN,
});
}
this.artifactBucket = propsBucket;
// If a role has been provided, use it - otherwise, create a role.
this.role = props.role || new iam.Role(this, 'Role', {
assumedBy: new iam.ServicePrincipal('codepipeline.amazonaws.com'),
});
this.codePipeline = new codepipeline_generated_1.CfnPipeline(this, 'Resource', {
artifactStore: core_1.Lazy.any({ produce: () => this.renderArtifactStoreProperty() }),
artifactStores: core_1.Lazy.any({ produce: () => this.renderArtifactStoresProperty() }),
stages: core_1.Lazy.any({ produce: () => this.renderStages() }),
disableInboundStageTransitions: core_1.Lazy.any({ produce: () => this.renderDisabledTransitions() }, { omitEmptyArray: true }),
roleArn: this.role.roleArn,
restartExecutionOnUpdate: props && props.restartExecutionOnUpdate,
name: this.physicalName,
});
// this will produce a DependsOn for both the role and the policy resources.
this.codePipeline.node.addDependency(this.role);
this.artifactBucket.grantReadWrite(this.role);
this.pipelineName = this.getResourceNameAttribute(this.codePipeline.ref);
this.pipelineVersion = this.codePipeline.attrVersion;
this.crossRegionBucketsPassed = !!props.crossRegionReplicationBuckets;
for (const [region, replicationBucket] of Object.entries(props.crossRegionReplicationBuckets || {})) {
this._crossRegionSupport[region] = {
replicationBucket,
stack: core_1.Stack.of(replicationBucket),
};
}
// Does not expose a Fn::GetAtt for the ARN so we'll have to make it ourselves
this.pipelineArn = core_1.Stack.of(this).formatArn({
service: 'codepipeline',
resource: this.pipelineName,
});
for (const stage of props.stages || []) {
this.addStage(stage);
}
}
/**
* Import a pipeline into this app.
*
* @param scope the scope into which to import this pipeline
* @param id the logical ID of the returned pipeline construct
* @param pipelineArn The ARN of the pipeline (e.g. `arn:aws:codepipeline:us-east-1:123456789012:MyDemoPipeline`)
*/
static fromPipelineArn(scope, id, pipelineArn) {
class Import extends PipelineBase {
constructor() {
super(...arguments);
this.pipelineName = core_1.Stack.of(scope).splitArn(pipelineArn, core_1.ArnFormat.SLASH_RESOURCE_NAME).resource;
this.pipelineArn = pipelineArn;
}
}
return new Import(scope, id);
}
/**
* Creates a new Stage, and adds it to this Pipeline.
*
* @param props the creation properties of the new Stage
* @returns the newly created Stage
*/
addStage(props) {
try {
jsiiDeprecationWarnings._aws_cdk_aws_codepipeline_StageOptions(props);
}
catch (error) {
if (process.env.JSII_DEBUG !== "1" && error.name === "DeprecationError") {
Error.captureStackTrace(error, this.addStage);
}
throw error;
}
// check for duplicate Stages and names
if (this._stages.find(s => s.stageName === props.stageName)) {
throw new Error(`Stage with duplicate name '${props.stageName}' added to the Pipeline`);
}
const stage = new stage_1.Stage(props, this);
const index = props.placement
? this.calculateInsertIndexFromPlacement(props.placement)
: this.stageCount;
this._stages.splice(index, 0, stage);
return stage;
}
/**
* Adds a statement to the pipeline role.
*/
addToRolePolicy(statement) {
this.role.addToPrincipalPolicy(statement);
}
/**
* Get the number of Stages in this Pipeline.
*/
get stageCount() {
return this._stages.length;
}
/**
* Returns the stages that comprise the pipeline.
*
* **Note**: the returned array is a defensive copy,
* so adding elements to it has no effect.
* Instead, use the {@link addStage} method if you want to add more stages
* to the pipeline.
*/
get stages() {
return this._stages.slice();
}
/**
* Access one of the pipeline's stages by stage name
*/
stage(stageName) {
for (const stage of this._stages) {
if (stage.stageName === stageName) {
return stage;
}
}
throw new Error(`Pipeline does not contain a stage named '${stageName}'. Available stages: ${this._stages.map(s => s.stageName).join(', ')}`);
}
/**
* Returns all of the {@link CrossRegionSupportStack}s that were generated automatically
* when dealing with Actions that reside in a different region than the Pipeline itself.
*
*/
get crossRegionSupport() {
const ret = {};
Object.keys(this._crossRegionSupport).forEach((key) => {
ret[key] = this._crossRegionSupport[key];
});
return ret;
}
/** @internal */
_attachActionToPipeline(stage, action, actionScope) {
const richAction = new rich_action_1.RichAction(action, this);
// handle cross-region actions here
const crossRegionInfo = this.ensureReplicationResourcesExistFor(richAction);
// get the role for the given action, handling if it's cross-account
const actionRole = this.getRoleForAction(stage, richAction, actionScope);
// // CodePipeline Variables
validation_1.validateNamespaceName(richAction.actionProperties.variablesNamespace);
// bind the Action (type h4x)
const actionConfig = richAction.bind(actionScope, stage, {
role: actionRole ? actionRole : this.role,
bucket: crossRegionInfo.artifactBucket,
});
return new full_action_descriptor_1.FullActionDescriptor({
// must be 'action', not 'richAction',
// as those are returned by the IStage.actions property,
// and it's important customers of Pipeline get the same instance
// back as they added to the pipeline
action,
actionConfig,
actionRole,
actionRegion: crossRegionInfo.region,
});
}
/**
* Validate the pipeline structure
*
* Validation happens according to the rules documented at
*
* https://docs.aws.amazon.com/codepipeline/latest/userguide/reference-pipeline-structure.html#pipeline-requirements
* @override
*/
validate() {
return [
...this.validateSourceActionLocations(),
...this.validateHasStages(),
...this.validateStages(),
...this.validateArtifacts(),
];
}
ensureReplicationResourcesExistFor(action) {
if (!action.isCrossRegion) {
return {
artifactBucket: this.artifactBucket,
};
}
// The action has a specific region,
// require the pipeline to have a known region as well.
this.requireRegion();
// source actions have to be in the same region as the pipeline
if (action.actionProperties.category === action_1.ActionCategory.SOURCE) {
throw new Error(`Source action '${action.actionProperties.actionName}' must be in the same region as the pipeline`);
}
// check whether we already have a bucket in that region,
// either passed from the outside or previously created
const crossRegionSupport = this.obtainCrossRegionSupportFor(action);
// the stack containing the replication bucket must be deployed before the pipeline
core_1.Stack.of(this).addDependency(crossRegionSupport.stack);
// The Pipeline role must be able to replicate to that bucket
crossRegionSupport.replicationBucket.grantReadWrite(this.role);
return {
artifactBucket: crossRegionSupport.replicationBucket,
region: action.effectiveRegion,
};
}
/**
* Get or create the cross-region support construct for the given action
*/
obtainCrossRegionSupportFor(action) {
// this method is never called for non cross-region actions
const actionRegion = action.effectiveRegion;
let crossRegionSupport = this._crossRegionSupport[actionRegion];
if (!crossRegionSupport) {
// we need to create scaffolding resources for this region
const otherStack = action.resourceStack;
crossRegionSupport = this.createSupportResourcesForRegion(otherStack, actionRegion);
this._crossRegionSupport[actionRegion] = crossRegionSupport;
}
return crossRegionSupport;
}
createSupportResourcesForRegion(otherStack, actionRegion) {
// if we have a stack from the resource passed - use that!
if (otherStack) {
// check if the stack doesn't have this magic construct already
const id = `CrossRegionReplicationSupport-d823f1d8-a990-4e5c-be18-4ac698532e65-${actionRegion}`;
let crossRegionSupportConstruct = otherStack.node.tryFindChild(id);
if (!crossRegionSupportConstruct) {
crossRegionSupportConstruct = new cross_region_support_stack_1.CrossRegionSupportConstruct(otherStack, id, {
createKmsKey: this.crossAccountKeys,
enableKeyRotation: this.enableKeyRotation,
});
}
return {
replicationBucket: crossRegionSupportConstruct.replicationBucket,
stack: otherStack,
};
}
// otherwise - create a stack with the resources needed for replication across regions
const pipelineStack = core_1.Stack.of(this);
const pipelineAccount = pipelineStack.account;
if (core_1.Token.isUnresolved(pipelineAccount)) {
throw new Error("You need to specify an explicit account when using CodePipeline's cross-region support");
}
const app = this.supportScope();
const supportStackId = `cross-region-stack-${this.reuseCrossRegionSupportStacks ? pipelineAccount : pipelineStack.stackName}:${actionRegion}`;
let supportStack = app.node.tryFindChild(supportStackId);
if (!supportStack) {
supportStack = new cross_region_support_stack_1.CrossRegionSupportStack(app, supportStackId, {
pipelineStackName: pipelineStack.stackName,
region: actionRegion,
account: pipelineAccount,
synthesizer: this.getCrossRegionSupportSynthesizer(),
createKmsKey: this.crossAccountKeys,
enableKeyRotation: this.enableKeyRotation,
});
}
return {
stack: supportStack,
replicationBucket: supportStack.replicationBucket,
};
}
getCrossRegionSupportSynthesizer() {
if (this.stack.synthesizer instanceof core_1.DefaultStackSynthesizer) {
// if we have the new synthesizer,
// we need a bootstrapless copy of it,
// because we don't want to require bootstrapping the environment
// of the pipeline account in this replication region
return new core_1.BootstraplessSynthesizer({
deployRoleArn: this.stack.synthesizer.deployRoleArn,
cloudFormationExecutionRoleArn: this.stack.synthesizer.cloudFormationExecutionRoleArn,
});
}
else {
// any other synthesizer: just return undefined
// (ie., use the default based on the context settings)
return undefined;
}
}
generateNameForDefaultBucketKeyAlias() {
const prefix = 'alias/codepipeline-';
const maxAliasLength = 256;
const uniqueId = core_1.Names.uniqueId(this);
// take the last 256 - (prefix length) characters of uniqueId
const startIndex = Math.max(0, uniqueId.length - (maxAliasLength - prefix.length));
return prefix + uniqueId.substring(startIndex).toLowerCase();
}
/**
* Gets the role used for this action,
* including handling the case when the action is supposed to be cross-account.
*
* @param stage the stage the action belongs to
* @param action the action to return/create a role for
* @param actionScope the scope, unique to the action, to create new resources in
*/
getRoleForAction(stage, action, actionScope) {
const pipelineStack = core_1.Stack.of(this);
let actionRole = this.getRoleFromActionPropsOrGenerateIfCrossAccount(stage, action);
if (!actionRole && this.isAwsOwned(action)) {
// generate a Role for this specific Action
actionRole = new iam.Role(actionScope, 'CodePipelineActionRole', {
assumedBy: new iam.AccountPrincipal(pipelineStack.account),
});
}
// the pipeline role needs assumeRole permissions to the action role
const grant = actionRole?.grantAssumeRole(this.role);
grant?.applyBefore(this.codePipeline);
return actionRole;
}
getRoleFromActionPropsOrGenerateIfCrossAccount(stage, action) {
const pipelineStack = core_1.Stack.of(this);
// if we have a cross-account action, the pipeline's bucket must have a KMS key
// (otherwise we can't configure cross-account trust policies)
if (action.isCrossAccount) {
const artifactBucket = this.ensureReplicationResourcesExistFor(action).artifactBucket;
if (!artifactBucket.encryptionKey) {
throw new Error(`Artifact Bucket must have a KMS Key to add cross-account action '${action.actionProperties.actionName}' ` +
`(pipeline account: '${renderEnvDimension(this.env.account)}', action account: '${renderEnvDimension(action.effectiveAccount)}'). ` +
'Create Pipeline with \'crossAccountKeys: true\' (or pass an existing Bucket with a key)');
}
}
// if a Role has been passed explicitly, always use it
// (even if the backing resource is from a different account -
// this is how the user can override our default support logic)
if (action.actionProperties.role) {
if (this.isAwsOwned(action)) {
// the role has to be deployed before the pipeline
// (our magical cross-stack dependencies will not work,
// because the role might be from a different environment),
// but _only_ if it's a new Role -
// an imported Role should not add the dependency
if (action.actionProperties.role instanceof iam.Role) {
const roleStack = core_1.Stack.of(action.actionProperties.role);
pipelineStack.addDependency(roleStack);
}
return action.actionProperties.role;
}
else {
// ...except if the Action is not owned by 'AWS',
// as that would be rejected by CodePipeline at deploy time
throw new Error("Specifying a Role is not supported for actions with an owner different than 'AWS' - " +
`got '${action.actionProperties.owner}' (Action: '${action.actionProperties.actionName}' in Stage: '${stage.stageName}')`);
}
}
// if we don't have a Role passed,
// and the action is cross-account,
// generate a Role in that other account stack
const otherAccountStack = this.getOtherStackIfActionIsCrossAccount(action);
if (!otherAccountStack) {
return undefined;
}
// generate a role in the other stack, that the Pipeline will assume for executing this action
const ret = new iam.Role(otherAccountStack, `${core_1.Names.uniqueId(this)}-${stage.stageName}-${action.actionProperties.actionName}-ActionRole`, {
assumedBy: new iam.AccountPrincipal(pipelineStack.account),
roleName: core_1.PhysicalName.GENERATE_IF_NEEDED,
});
// the other stack with the role has to be deployed before the pipeline stack
// (CodePipeline verifies you can assume the action Role on creation)
pipelineStack.addDependency(otherAccountStack);
return ret;
}
/**
* Returns the Stack this Action belongs to if this is a cross-account Action.
* If this Action is not cross-account (i.e., it lives in the same account as the Pipeline),
* it returns undefined.
*
* @param action the Action to return the Stack for
*/
getOtherStackIfActionIsCrossAccount(action) {
const targetAccount = action.actionProperties.resource
? action.actionProperties.resource.env.account
: action.actionProperties.account;
if (targetAccount === undefined) {
// if the account of the Action is not specified,
// then it defaults to the same account the pipeline itself is in
return undefined;
}
// check whether the action's account is a static string
if (core_1.Token.isUnresolved(targetAccount)) {
if (core_1.Token.isUnresolved(this.env.account)) {
// the pipeline is also env-agnostic, so that's fine
return undefined;
}
else {
throw new Error(`The 'account' property must be a concrete value (action: '${action.actionProperties.actionName}')`);
}
}
// At this point, we know that the action's account is a static string.
// In this case, the pipeline's account must also be a static string.
if (core_1.Token.isUnresolved(this.env.account)) {
throw new Error('Pipeline stack which uses cross-environment actions must have an explicitly set account');
}
// at this point, we know that both the Pipeline's account,
// and the action-backing resource's account are static strings
// if they are identical - nothing to do (the action is not cross-account)
if (this.env.account === targetAccount) {
return undefined;
}
// at this point, we know that the action is certainly cross-account,
// so we need to return a Stack in its account to create the helper Role in
const candidateActionResourceStack = action.actionProperties.resource
? core_1.Stack.of(action.actionProperties.resource)
: undefined;
if (candidateActionResourceStack?.account === targetAccount) {
// we always use the "latest" action-backing resource's Stack for this account,
// even if a different one was used earlier
this._crossAccountSupport[targetAccount] = candidateActionResourceStack;
return candidateActionResourceStack;
}
let targetAccountStack = this._crossAccountSupport[targetAccount];
if (!targetAccountStack) {
const stackId = `cross-account-support-stack-${targetAccount}`;
const app = this.supportScope();
targetAccountStack = app.node.tryFindChild(stackId);
if (!targetAccountStack) {
const actionRegion = action.actionProperties.resource
? action.actionProperties.resource.env.region
: action.actionProperties.region;
const pipelineStack = core_1.Stack.of(this);
targetAccountStack = new core_1.Stack(app, stackId, {
stackName: `${pipelineStack.stackName}-support-${targetAccount}`,
env: {
account: targetAccount,
region: actionRegion ?? pipelineStack.region,
},
});
}
this._crossAccountSupport[targetAccount] = targetAccountStack;
}
return targetAccountStack;
}
isAwsOwned(action) {
const owner = action.actionProperties.owner;
return !owner || owner === 'AWS';
}
getArtifactBucketFromProps(props) {
if (props.artifactBucket) {
return props.artifactBucket;
}
if (props.crossRegionReplicationBuckets) {
const pipelineRegion = this.requireRegion();
return props.crossRegionReplicationBuckets[pipelineRegion];
}
return undefined;
}
calculateInsertIndexFromPlacement(placement) {
// check if at most one placement property was provided
const providedPlacementProps = ['rightBefore', 'justAfter', 'atIndex']
.filter((prop) => placement[prop] !== undefined);
if (providedPlacementProps.length > 1) {
throw new Error('Error adding Stage to the Pipeline: ' +
'you can only provide at most one placement property, but ' +
`'${providedPlacementProps.join(', ')}' were given`);
}
if (placement.rightBefore !== undefined) {
const targetIndex = this.findStageIndex(placement.rightBefore);
if (targetIndex === -1) {
throw new Error('Error adding Stage to the Pipeline: ' +
`the requested Stage to add it before, '${placement.rightBefore.stageName}', was not found`);
}
return targetIndex;
}
if (placement.justAfter !== undefined) {
const targetIndex = this.findStageIndex(placement.justAfter);
if (targetIndex === -1) {
throw new Error('Error adding Stage to the Pipeline: ' +
`the requested Stage to add it after, '${placement.justAfter.stageName}', was not found`);
}
return targetIndex + 1;
}
return this.stageCount;
}
findStageIndex(targetStage) {
return this._stages.findIndex(stage => stage === targetStage);
}
validateSourceActionLocations() {
const errors = new Array();
let firstStage = true;
for (const stage of this._stages) {
const onlySourceActionsPermitted = firstStage;
for (const action of stage.actionDescriptors) {
errors.push(...validation_1.validateSourceAction(onlySourceActionsPermitted, action.category, action.actionName, stage.stageName));
}
firstStage = false;
}
return errors;
}
validateHasStages() {
if (this.stageCount < 2) {
return ['Pipeline must have at least two stages'];
}
return [];
}
validateStages() {
const ret = new Array();
for (const stage of this._stages) {
ret.push(...stage.validate());
}
return ret;
}
validateArtifacts() {
const ret = new Array();
const producers = {};
const firstConsumers = {};
for (const [stageIndex, stage] of enumerate(this._stages)) {
// For every output artifact, get the producer
for (const action of stage.actionDescriptors) {
const actionLoc = new PipelineLocation(stageIndex, stage, action);
for (const outputArtifact of action.outputs) {
// output Artifacts always have a name set
const name = outputArtifact.artifactName;
if (producers[name]) {
ret.push(`Both Actions '${producers[name].actionName}' and '${action.actionName}' are producting Artifact '${name}'. Every artifact can only be produced once.`);
continue;
}
producers[name] = actionLoc;
}
// For every input artifact, get the first consumer
for (const inputArtifact of action.inputs) {
const name = inputArtifact.artifactName;
if (!name) {
ret.push(`Action '${action.actionName}' is using an unnamed input Artifact, which is not being produced in this pipeline`);
continue;
}
firstConsumers[name] = firstConsumers[name] ? firstConsumers[name].first(actionLoc) : actionLoc;
}
}
}
// Now validate that every input artifact is produced before it's
// being consumed.
for (const [artifactName, consumerLoc] of Object.entries(firstConsumers)) {
const producerLoc = producers[artifactName];
if (!producerLoc) {
ret.push(`Action '${consumerLoc.actionName}' is using input Artifact '${artifactName}', which is not being produced in this pipeline`);
continue;
}
if (consumerLoc.beforeOrEqual(producerLoc)) {
ret.push(`${consumerLoc} is consuming input Artifact '${artifactName}' before it is being produced at ${producerLoc}`);
}
}
return ret;
}
renderArtifactStoresProperty() {
if (!this.crossRegion) {
return undefined;
}
// add the Pipeline's artifact store
const primaryRegion = this.requireRegion();
this._crossRegionSupport[primaryRegion] = {
replicationBucket: this.artifactBucket,
stack: core_1.Stack.of(this),
};
return Object.entries(this._crossRegionSupport).map(([region, support]) => ({
region,
artifactStore: this.renderArtifactStore(support.replicationBucket),
}));
}
renderArtifactStoreProperty() {
if (this.crossRegion) {
return undefined;
}
return this.renderPrimaryArtifactStore();
}
renderPrimaryArtifactStore() {
return this.renderArtifactStore(this.artifactBucket);
}
renderArtifactStore(bucket) {
let encryptionKey;
const bucketKey = bucket.encryptionKey;
if (bucketKey) {
encryptionKey = {
type: 'KMS',
id: bucketKey.keyArn,
};
}
return {
type: 'S3',
location: bucket.bucketName,
encryptionKey,
};
}
get crossRegion() {
if (this.crossRegionBucketsPassed) {
return true;
}
return this._stages.some(stage => stage.actionDescriptors.some(action => action.region !== undefined));
}
renderStages() {
return this._stages.map(stage => stage.render());
}
renderDisabledTransitions() {
return this._stages
.filter(stage => !stage.transitionToEnabled)
.map(stage => ({
reason: stage.transitionDisabledReason,
stageName: stage.stageName,
}));
}
requireRegion() {
const region = this.env.region;
if (core_1.Token.isUnresolved(region)) {
throw new Error('Pipeline stack which uses cross-environment actions must have an explicitly set region');
}
return region;
}
supportScope() {
const scope = core_1.Stage.of(this);
if (!scope) {
throw new Error('Pipeline stack which uses cross-environment actions must be part of a CDK App or Stage');
}
return scope;
}
}
exports.Pipeline = Pipeline;
_a = JSII_RTTI_SYMBOL_1;
Pipeline[_a] = { fqn: "@aws-cdk/aws-codepipeline.Pipeline", version: "1.204.0" };
function enumerate(xs) {
const ret = new Array();
for (let i = 0; i < xs.length; i++) {
ret.push([i, xs[i]]);
}
return ret;
}
class PipelineLocation {
constructor(stageIndex, stage, action) {
this.stageIndex = stageIndex;
this.stage = stage;
this.action = action;
}
get stageName() {
return this.stage.stageName;
}
get actionName() {
return this.action.actionName;
}
/**
* Returns whether a is before or the same order as b
*/
beforeOrEqual(rhs) {
if (this.stageIndex !== rhs.stageIndex) {
return rhs.stageIndex < rhs.stageIndex;
}
return this.action.runOrder <= rhs.action.runOrder;
}
/**
* Returns the first location between this and the other one
*/
first(rhs) {
return this.beforeOrEqual(rhs) ? this : rhs;
}
toString() {
// runOrders are 1-based, so make the stageIndex also 1-based otherwise it's going to be confusing.
return `Stage ${this.stageIndex + 1} Action ${this.action.runOrder} ('${this.stageName}'/'${this.actionName}')`;
}
}
/**
* Render an env dimension without showing the ugly stringified tokens
*/
function renderEnvDimension(s) {
return core_1.Token.isUnresolved(s) ? '(current)' : s;
}
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicGlwZWxpbmUuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyJwaXBlbGluZS50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiOzs7Ozs7QUFBQSxvRUFBb0U7QUFDcEUsOENBQThDO0FBQzlDLHdDQUF3QztBQUN4Qyx3Q0FBd0M7QUFDeEMsc0NBQXNDO0FBQ3RDLHdDQWF1QjtBQUV2QixxQ0FBMkg7QUFDM0gscUVBQXVEO0FBQ3ZELHFGQUE0RztBQUM1Ryw2RUFBd0U7QUFDeEUsdURBQW1EO0FBQ25ELDJDQUF3QztBQUN4QyxxREFBaUc7QUFrSmpHLE1BQWUsWUFBYSxTQUFRLGVBQVE7SUFJMUM7Ozs7O09BS0c7SUFDSSxPQUFPLENBQUMsRUFBVSxFQUFFLFVBQWlDLEVBQUU7UUFDNUQsTUFBTSxJQUFJLEdBQUcsSUFBSSxNQUFNLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDaEQsSUFBSSxDQUFDLFNBQVMsQ0FBQyxPQUFPLENBQUMsTUFBTSxDQUFDLENBQUM7UUFDL0IsSUFBSSxDQUFDLGVBQWUsQ0FBQztZQUNuQixNQUFNLEVBQUUsQ0FBQyxrQkFBa0IsQ0FBQztZQUM1QixTQUFTLEVBQUUsQ0FBQyxJQUFJLENBQUMsV0FBVyxDQUFDO1NBQzlCLENBQUMsQ0FBQztRQUNILE9BQU8sSUFBSSxDQUFDO0tBQ2I7SUFFRDs7Ozs7O09BTUc7SUFDSSxhQUFhLENBQUMsRUFBVSxFQUFFLFVBQWlDLEVBQUU7UUFDbEUsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLE9BQU8sQ0FBQyxFQUFFLEVBQUUsT0FBTyxDQUFDLENBQUM7UUFDdkMsSUFBSSxDQUFDLGVBQWUsQ0FBQztZQUNuQixVQUFVLEVBQUUsQ0FBQyw4Q0FBOEMsQ0FBQztTQUM3RCxDQUFDLENBQUM7UUFDSCxPQUFPLElBQUksQ0FBQztLQUNiO0lBRU0sNEJBQTRCLENBQUMsTUFBaUI7UUFDbkQsT0FBTztZQUNMLFNBQVMsRUFBRSxJQUFJLENBQUMs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