aws-cdk-lib
Version:
Version 2 of the AWS Cloud Development Kit library
2 lines (1 loc) • 8.54 kB
JavaScript
Object.defineProperty(exports,"__esModule",{value:!0}),exports.PipelineGraph=void 0;var graph_1=()=>{var tmp=require("./graph");return graph_1=()=>tmp,tmp},pipeline_queries_1=()=>{var tmp=require("./pipeline-queries");return pipeline_queries_1=()=>tmp,tmp},core_1=()=>{var tmp=require("../../../core");return core_1=()=>tmp,tmp},blueprint_1=()=>{var tmp=require("../blueprint");return blueprint_1=()=>tmp,tmp};class PipelineGraph{constructor(pipeline,props={}){this.pipeline=pipeline,this.graph=graph_1().Graph.of("",{type:"group"}),this.added=new Map,this.assetNodes=new Map,this.assetNodesByType=new Map,this.stackOutputDependencies=new(graph_1()).DependencyBuilders,this.nodeDependencies=new(graph_1()).DependencyBuilders,this._fileAssetCtr=0,this._dockerAssetCtr=0,this.publishTemplate=props.publishTemplate??!1,this.prepareStep=props.prepareStep??!0,this.singlePublisher=props.singlePublisherPerAssetType??!1,this.queries=new(pipeline_queries_1()).PipelineQueries(pipeline),pipeline.synth instanceof blueprint_1().Step&&(this.synthNode=this.addBuildStep(pipeline.synth),this.synthNode?.data?.type==="step"&&(this.synthNode.data.isBuildStep=!0)),this.lastPreparationNode=this.synthNode;const cloudAssembly=pipeline.synth.primaryOutput?.primaryOutput;if(!cloudAssembly)throw new(core_1()).ValidationError(`The synth step must produce the cloud assembly artifact, but doesn't: ${pipeline.synth}`,this.pipeline);if(this.cloudAssemblyFileSet=cloudAssembly,props.selfMutation){const stage=graph_1().Graph.of("UpdatePipeline",{type:"group"});this.graph.add(stage),this.selfMutateNode=aGraphNode("SelfMutate",{type:"self-update"}),stage.add(this.selfMutateNode),this.selfMutateNode.dependOn(this.synthNode),this.lastPreparationNode=this.selfMutateNode}const waves=pipeline.waves.map(w=>this.addWave(w));for(let i=1;i<waves.length;i++)waves[i].dependOn(waves[i-1]);this.addMissingDependencyNodes()}isSynthNode(node){return this.synthNode===node}addBuildStep(step){return this.addStepNode(step,this.topLevelGraph("Build"))}addWave(wave){const retGraph=wave.stages.length===1?this.addStage(wave.stages[0]):graph_1().Graph.of(wave.id,{type:"group"},wave.stages.map(s=>this.addStage(s)));return this.addPrePost(wave.pre,wave.post,retGraph),retGraph.dependOn(this.lastPreparationNode),this.graph.add(retGraph),retGraph}addStage(stage){const retGraph=graph_1().Graph.of(stage.stageName,{type:"group"}),stackGraphs=new Map;for(const stack of stage.stacks){const stackGraphName=findUniqueName(retGraph,[this.simpleStackName(stack.stackName,stage.stageName),...stack.account?[stack.account]:[],...stack.region?[stack.region]:[]]),stackGraph=graph_1().Graph.of(stackGraphName,{type:"stack-group",stack}),prepareNode=this.prepareStep?aGraphNode("Prepare",{type:"prepare",stack}):void 0,deployNode=aGraphNode("Deploy",{type:"execute",stack,captureOutputs:this.queries.stackOutputsReferenced(stack).length>0,withoutChangeSet:prepareNode===void 0});retGraph.add(stackGraph),stackGraph.add(deployNode);let firstDeployNode;if(prepareNode?(stackGraph.add(prepareNode),deployNode.dependOn(prepareNode),firstDeployNode=prepareNode):firstDeployNode=deployNode,stack.changeSet.length>0)if(prepareNode)this.addChangeSetNode(stack.changeSet,prepareNode,deployNode,stackGraph);else throw new(core_1()).ValidationError(`Cannot use 'changeSet' steps for stack '${stack.stackName}': the pipeline does not support them or they have been disabled`,this.pipeline);const preNodes=this.addPrePost(stack.pre,stack.post,stackGraph);preNodes.nodes.length>0&&(firstDeployNode=preNodes),stackGraphs.set(stack,stackGraph);const cloudAssembly=this.cloudAssemblyFileSet;if(firstDeployNode.dependOn(this.addStepNode(cloudAssembly.producer,retGraph)),this.publishTemplate){if(!stack.templateAsset)throw new(core_1()).ValidationError(`"publishTemplate" is enabled, but stack ${stack.stackArtifactId} does not have a template asset`,this.pipeline);firstDeployNode.dependOn(this.publishAsset(stack.templateAsset))}for(const asset of stack.assets){const assetNode=this.publishAsset(asset);firstDeployNode.dependOn(assetNode)}this.queries.stackOutputsReferenced(stack).length>0&&this.stackOutputDependencies.for(stack).dependOn(deployNode)}for(const stack of stage.stacks)for(const dep of stack.stackDependencies){const stackNode=stackGraphs.get(stack),depNode=stackGraphs.get(dep);if(!stackNode)throw new(core_1()).ValidationError(`cannot find node for ${stack.stackName}`,this.pipeline);if(!depNode)throw new(core_1()).ValidationError(`cannot find node for ${dep.stackName}`,this.pipeline);stackNode.dependOn(depNode)}return this.addPrePost(stage.pre,stage.post,retGraph),retGraph}addChangeSetNode(changeSet,prepareNode,deployNode,graph){for(const c of changeSet){const changeSetNode=this.addStepNode(c,graph);changeSetNode?.dependOn(prepareNode),deployNode.dependOn(changeSetNode)}}addPrePost(pre,post,parent){const currentNodes=new(graph_1()).GraphNodeCollection(parent.nodes),preNodes=new(graph_1()).GraphNodeCollection(new Array);for(const p of pre){const preNode=this.addStepNode(p,parent);currentNodes.dependOn(preNode),preNodes.nodes.push(preNode)}for(const p of post)this.addStepNode(p,parent)?.dependOn(...currentNodes.nodes);return preNodes}topLevelGraph(name){let ret=this.graph.tryGetChild(name);return ret||(ret=new(graph_1()).Graph(name),this.graph.add(ret)),ret}addStepNode(step,parent){if(step===PipelineGraph.NO_STEP)return;const previous=this.added.get(step);if(previous)return previous;const node=aGraphNode(step.id,{type:"step",step});step.isSource&&(parent=this.topLevelGraph("Source")),parent.add(node),this.added.set(step,node);for(const dep of step.dependencies)this.nodeDependencies.for(dep).dependBy(node);this.nodeDependencies.for(step).dependOn(node);for(const output of step.consumedStackOutputs){const stack=this.queries.producingStack(output);this.stackOutputDependencies.for(stack).dependBy(node)}return node}addMissingDependencyNodes(){let attempts=20;for(;attempts-- >0;){const unsatisfied2=this.nodeDependencies.unsatisfiedBuilders().filter(([s])=>s!==PipelineGraph.NO_STEP);if(unsatisfied2.length===0)return;for(const[step,builder]of unsatisfied2){const leftMostConsumer=new(graph_1()).GraphNodeCollection(builder.consumers).first(),parent=leftMostConsumer.parentGraph;if(!parent)throw new(core_1()).ValidationError(`Consumer doesn't have a parent graph: ${leftMostConsumer}`,this.pipeline);this.addStepNode(step,parent)}}const unsatisfied=this.nodeDependencies.unsatisfiedBuilders();throw new(core_1()).ValidationError(["Recursion depth too large while adding dependency nodes:",unsatisfied.map(([step,builder])=>`${builder.consumersAsString()} awaiting ${step}.`)].join(" "),this.pipeline)}publishAsset(stackAsset){const assetsGraph=this.topLevelGraph("Assets");let assetNode=this.assetNodes.get(stackAsset.assetId);if(!assetNode)if(this.singlePublisher&&this.assetNodesByType.has(stackAsset.assetType))assetNode=this.assetNodesByType.get(stackAsset.assetType);else{const id=stackAsset.assetType===blueprint_1().AssetType.FILE?this.singlePublisher?"FileAsset":`FileAsset${++this._fileAssetCtr}`:this.singlePublisher?"DockerAsset":`DockerAsset${++this._dockerAssetCtr}`,displayName=this.singlePublisher?getDisplayNameForSinglePublishStep(stackAsset.assetType):stackAsset.displayName;assetNode=aGraphNode(id,{type:"publish-assets",assets:[]},displayName),assetsGraph.add(assetNode),assetNode.dependOn(this.lastPreparationNode),this.assetNodesByType.set(stackAsset.assetType,assetNode),this.assetNodes.set(stackAsset.assetId,assetNode)}const data=assetNode.data;if(data?.type!=="publish-assets")throw new(core_1()).ValidationError(`${assetNode} has the wrong data.type: ${data?.type}`,this.pipeline);return data.assets.some(a=>a.assetSelector===stackAsset.assetSelector)||data.assets.push(stackAsset),assetNode}simpleStackName(stackName,stageName){return stripPrefix(stackName,`${stageName}-`)}}exports.PipelineGraph=PipelineGraph,PipelineGraph.NO_STEP=new class extends blueprint_1().Step{}("NO_STEP");function aGraphNode(id,x,displayName){return graph_1().GraphNode.of(id,x,displayName)}function stripPrefix(s,prefix){return s.startsWith(prefix)?s.slice(prefix.length):s}function findUniqueName(parent,parts){for(let i=1;i<=parts.length;i++){const candidate=parts.slice(0,i).join(".");if(!parent.containsId(candidate))return candidate}return parts.join(".")}function getDisplayNameForSinglePublishStep(type){switch(type){case blueprint_1().AssetType.FILE:return"FileAssets";case blueprint_1().AssetType.DOCKER_IMAGE:return"DockerAssets"}}
;