UNPKG

@aws-cdk/aws-codepipeline

Version:

Better interface to AWS Code Pipeline

835 lines 122 kB
"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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