export async function deployStack()

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();
}