in packages/@aws-cdk/toolkit-lib/lib/api/deployments/deploy-stack.ts [203:347]
export async function deployStack(options: DeployStackOptions, ioHelper: IoHelper): Promise<DeployStackResult> {
const stackArtifact = options.stack;
const stackEnv = options.resolvedEnvironment;
options.sdk.appendCustomUserAgent(options.extraUserAgent);
const cfn = options.sdk.cloudFormation();
const deployName = options.deployName || stackArtifact.stackName;
let cloudFormationStack = await CloudFormationStack.lookup(cfn, deployName);
if (cloudFormationStack.stackStatus.isCreationFailure) {
await ioHelper.notify(IO.DEFAULT_TOOLKIT_DEBUG.msg(
`Found existing stack ${deployName} that had previously failed creation. Deleting it before attempting to re-create it.`,
));
await cfn.deleteStack({ StackName: deployName });
const deletedStack = await waitForStackDelete(cfn, ioHelper, deployName);
if (deletedStack && deletedStack.stackStatus.name !== 'DELETE_COMPLETE') {
throw new ToolkitError(
`Failed deleting stack ${deployName} that had previously failed creation (current state: ${deletedStack.stackStatus})`,
);
}
// Update variable to mark that the stack does not exist anymore, but avoid
// doing an actual lookup in CloudFormation (which would be silly to do if
// we just deleted it).
cloudFormationStack = CloudFormationStack.doesNotExist(cfn, deployName);
}
// Detect "legacy" assets (which remain in the metadata) and publish them via
// an ad-hoc asset manifest, while passing their locations via template
// parameters.
const legacyAssets = new AssetManifestBuilder();
const assetParams = await addMetadataAssetsToManifest(
ioHelper,
stackArtifact,
legacyAssets,
options.envResources,
options.reuseAssets,
);
const finalParameterValues = { ...options.parameters, ...assetParams };
const templateParams = TemplateParameters.fromTemplate(stackArtifact.template);
const stackParams = options.usePreviousParameters
? templateParams.updateExisting(finalParameterValues, cloudFormationStack.parameters)
: templateParams.supplyAll(finalParameterValues);
const hotswapMode = options.hotswap ?? HotswapMode.FULL_DEPLOYMENT;
const hotswapPropertyOverrides = options.hotswapPropertyOverrides ?? new HotswapPropertyOverrides();
if (await canSkipDeploy(options, cloudFormationStack, stackParams.hasChanges(cloudFormationStack.parameters), ioHelper)) {
await ioHelper.notify(IO.DEFAULT_TOOLKIT_DEBUG.msg(`${deployName}: skipping deployment (use --force to override)`));
// if we can skip deployment and we are performing a hotswap, let the user know
// that no hotswap deployment happened
if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) {
await ioHelper.notify(IO.DEFAULT_TOOLKIT_INFO.msg(
format(
`\n ${ICON} %s\n`,
chalk.bold('hotswap deployment skipped - no changes were detected (use --force to override)'),
),
));
}
return {
type: 'did-deploy-stack',
noOp: true,
outputs: cloudFormationStack.outputs,
stackArn: cloudFormationStack.stackId,
};
} else {
await ioHelper.notify(IO.DEFAULT_TOOLKIT_DEBUG.msg(`${deployName}: deploying...`));
}
const bodyParameter = await makeBodyParameter(
ioHelper,
stackArtifact,
options.resolvedEnvironment,
legacyAssets,
options.envResources,
options.overrideTemplate,
);
let bootstrapStackName: string | undefined;
try {
bootstrapStackName = (await options.envResources.lookupToolkit()).stackName;
} catch (e) {
await ioHelper.notify(IO.DEFAULT_TOOLKIT_DEBUG.msg(`Could not determine the bootstrap stack name: ${e}`));
}
await publishAssets(legacyAssets.toManifest(stackArtifact.assembly.directory), options.sdkProvider, stackEnv, {
parallel: options.assetParallelism,
allowCrossAccount: await determineAllowCrossAccountAssetPublishing(options.sdk, ioHelper, bootstrapStackName),
}, ioHelper);
if (hotswapMode !== HotswapMode.FULL_DEPLOYMENT) {
// attempt to short-circuit the deployment if possible
try {
const hotswapDeploymentResult = await tryHotswapDeployment(
options.sdkProvider,
ioHelper,
stackParams.values,
cloudFormationStack,
stackArtifact,
hotswapMode,
hotswapPropertyOverrides,
);
if (hotswapDeploymentResult) {
return hotswapDeploymentResult;
}
await ioHelper.notify(IO.DEFAULT_TOOLKIT_INFO.msg(format(
'Could not perform a hotswap deployment, as the stack %s contains non-Asset changes',
stackArtifact.displayName,
)));
} catch (e) {
if (!(e instanceof CfnEvaluationException)) {
throw e;
}
await ioHelper.notify(IO.DEFAULT_TOOLKIT_INFO.msg(format(
'Could not perform a hotswap deployment, because the CloudFormation template could not be resolved: %s',
formatErrorMessage(e),
)));
}
if (hotswapMode === HotswapMode.FALL_BACK) {
await ioHelper.notify(IO.DEFAULT_TOOLKIT_INFO.msg('Falling back to doing a full deployment'));
options.sdk.appendCustomUserAgent('cdk-hotswap/fallback');
} else {
return {
type: 'did-deploy-stack',
noOp: true,
stackArn: cloudFormationStack.stackId,
outputs: cloudFormationStack.outputs,
};
}
}
// could not short-circuit the deployment, perform a full CFN deploy instead
const fullDeployment = new FullCloudFormationDeployment(
options,
cloudFormationStack,
stackArtifact,
stackParams,
bodyParameter,
ioHelper,
);
return fullDeployment.performDeployment();
}