sst 2.23.15 → 2.24.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,7 +1,7 @@
1
1
  import * as cxapi from "@aws-cdk/cx-api";
2
2
  import { AssetManifest, IManifestEntry } from "cdk-assets";
3
3
  import { Tag } from "sst-aws-cdk/lib/cdk-toolkit.js";
4
- import { BuildAssetsOptions, PublishAssetsOptions } from "./asset-publishing.js";
4
+ import { BuildAssetsOptions, PublishAssetsOptions } from "sst-aws-cdk/lib/util/asset-publishing.js";
5
5
  import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
6
6
  import { ISDK } from "sst-aws-cdk/lib/api/aws-auth/sdk.js";
7
7
  import { SdkProvider } from "sst-aws-cdk/lib/api/aws-auth/sdk-provider.js";
@@ -3,7 +3,7 @@ import * as cxapi from "@aws-cdk/cx-api";
3
3
  import * as cdk_assets from "cdk-assets";
4
4
  import { AssetManifest } from "cdk-assets";
5
5
  import { debug, warning } from "sst-aws-cdk/lib/logging.js";
6
- import { buildAssets, publishAssets, PublishingAws, EVENT_TO_LOGGER, } from "./asset-publishing.js";
6
+ import { buildAssets, publishAssets, PublishingAws, EVENT_TO_LOGGER, } from "sst-aws-cdk/lib/util/asset-publishing.js";
7
7
  import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
8
8
  import { deployStack, destroyStack, makeBodyParameterAndUpload, } from "./deploy-stack.js";
9
9
  import { loadCurrentTemplateWithNestedStacks, loadCurrentTemplate, } from "sst-aws-cdk/lib/api/nested-stack-helpers.js";
package/cli/ui/deploy.js CHANGED
@@ -155,7 +155,7 @@ function getApiAccessLogPermissionsHelper(error) {
155
155
  // note: this should be handled in SST as access log group names are now
156
156
  // hardcoded with /aws/vendedlogs/apis prefix.
157
157
  if (error.indexOf("Insufficient permissions to enable logging") > -1) {
158
- return `This is a common deploy error. Check out this GitHub issue for more details - https://github.com/serverless-stack/sst/issues/125`;
158
+ return `This is a common deploy error. Check out this GitHub issue for more details - https://github.com/sst/sst/issues/125`;
159
159
  }
160
160
  }
161
161
  function getAppSyncMultiResolverHelper(error) {
@@ -77,7 +77,7 @@ export declare class App extends CDKApp {
77
77
  defaultFunctionProps: (FunctionProps | ((stack: Stack) => FunctionProps))[];
78
78
  private _defaultRemovalPolicy?;
79
79
  /** @internal */
80
- get defaultRemovalPolicy(): "destroy" | "retain" | "snapshot" | undefined;
80
+ get defaultRemovalPolicy(): "destroy" | "retain" | "snapshot" | "retain-on-update-or-delete" | undefined;
81
81
  /**
82
82
  * @internal
83
83
  */
@@ -268,7 +268,7 @@ export interface FunctionProps extends Omit<FunctionOptions, "functionName" | "m
268
268
  *
269
269
  * Note that, if a Layer is created in a stack (say `stackA`) and is referenced in another stack (say `stackB`), SST automatically creates an SSM parameter in `stackA` with the Layer's ARN. And in `stackB`, SST reads the ARN from the SSM parameter, and then imports the Layer.
270
270
  *
271
- * This is to get around the limitation that a Lambda Layer ARN cannot be referenced across stacks via a stack export. The Layer ARN contains a version number that is incremented everytime the Layer is modified. When you refer to a Layer's ARN across stacks, a CloudFormation export is created. However, CloudFormation does not allow an exported value to be updated. Once exported, if you try to deploy the updated layer, the CloudFormation update will fail. You can read more about this issue here - https://github.com/serverless-stack/sst/issues/549.
271
+ * This is to get around the limitation that a Lambda Layer ARN cannot be referenced across stacks via a stack export. The Layer ARN contains a version number that is incremented everytime the Layer is modified. When you refer to a Layer's ARN across stacks, a CloudFormation export is created. However, CloudFormation does not allow an exported value to be updated. Once exported, if you try to deploy the updated layer, the CloudFormation update will fail. You can read more about this issue here - https://github.com/sst/sst/issues/549.
272
272
  *
273
273
  * @default no layers
274
274
  *
@@ -14,7 +14,7 @@ const SessionMemo = /* @__PURE__ */ Context.memo(() => {
14
14
  if (cookie)
15
15
  token = cookie;
16
16
  // WebSocket may also set the token in the protocol header
17
- // TODO: Once https://github.com/serverless-stack/sst/pull/2838 is merged,
17
+ // TODO: Once https://github.com/sst/sst/pull/2838 is merged,
18
18
  // then we should no longer need to check both casing for the header.
19
19
  const wsProtocol = ctxType === "ws"
20
20
  ? useHeader("sec-websocket-protocol") ||
@@ -15,7 +15,7 @@ const SessionMemo = /* @__PURE__ */ Context.memo(() => {
15
15
  if (cookie)
16
16
  token = cookie;
17
17
  // WebSocket may also set the token in the protocol header
18
- // TODO: Once https://github.com/serverless-stack/sst/pull/2838 is merged,
18
+ // TODO: Once https://github.com/sst/sst/pull/2838 is merged,
19
19
  // then we should no longer need to check both casing for the header.
20
20
  const wsProtocol = ctxType === "ws"
21
21
  ? useHeader("sec-websocket-protocol") ||
@@ -16,7 +16,7 @@ export const WebSocketApi =
16
16
  export function WebSocketApiHandler(cb) {
17
17
  return Handler("ws", async (evt, ctx) => {
18
18
  const result = await cb(evt, ctx);
19
- // TODO: Once https://github.com/serverless-stack/sst/pull/2838 is merged,
19
+ // TODO: Once https://github.com/sst/sst/pull/2838 is merged,
20
20
  // then we should no longer need to check both casing for the header.
21
21
  const token = useHeader("Sec-WebSocket-Protocol") ||
22
22
  useHeader("sec-websocket-protocol");
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "sideEffects": false,
3
3
  "name": "sst",
4
- "version": "2.23.15",
4
+ "version": "2.24.0",
5
5
  "bin": {
6
6
  "sst": "cli/sst.js"
7
7
  },
@@ -10,7 +10,7 @@
10
10
  "license": "MIT",
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "git+https://github.com/serverless-stack/sst.git",
13
+ "url": "git+https://github.com/sst/sst.git",
14
14
  "directory": "packages/cli"
15
15
  },
16
16
  "exports": {
@@ -25,12 +25,12 @@
25
25
  },
26
26
  "homepage": "https://sst.dev",
27
27
  "dependencies": {
28
- "@aws-cdk/aws-apigatewayv2-alpha": "^2.84.0-alpha.0",
29
- "@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.84.0-alpha.0",
30
- "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.84.0-alpha.0",
31
- "@aws-cdk/cloud-assembly-schema": "2.84.0",
32
- "@aws-cdk/cloudformation-diff": "2.84.0",
33
- "@aws-cdk/cx-api": "2.84.0",
28
+ "@aws-cdk/aws-apigatewayv2-alpha": "^2.91.0-alpha.0",
29
+ "@aws-cdk/aws-apigatewayv2-authorizers-alpha": "^2.91.0-alpha.0",
30
+ "@aws-cdk/aws-apigatewayv2-integrations-alpha": "^2.91.0-alpha.0",
31
+ "@aws-cdk/cloud-assembly-schema": "2.91.0",
32
+ "@aws-cdk/cloudformation-diff": "2.91.0",
33
+ "@aws-cdk/cx-api": "2.91.0",
34
34
  "@aws-crypto/sha256-js": "^5.0.0",
35
35
  "@aws-sdk/client-cloudformation": "^3.279.0",
36
36
  "@aws-sdk/client-ecs": "^3.279.0",
@@ -55,17 +55,17 @@
55
55
  "@smithy/signature-v4": "^2.0.1",
56
56
  "@trpc/server": "9.16.0",
57
57
  "adm-zip": "^0.5.10",
58
- "aws-cdk-lib": "2.84.0",
58
+ "aws-cdk-lib": "2.91.0",
59
59
  "aws-iot-device-sdk": "^2.2.12",
60
60
  "aws-sdk": "^2.1326.0",
61
61
  "builtin-modules": "3.2.0",
62
- "cdk-assets": "2.84.0",
62
+ "cdk-assets": "2.91.0",
63
63
  "chalk": "^5.2.0",
64
64
  "chokidar": "^3.5.3",
65
65
  "ci-info": "^3.7.0",
66
66
  "colorette": "^2.0.19",
67
67
  "conf": "^10.2.0",
68
- "constructs": "10.1.156",
68
+ "constructs": "10.2.69",
69
69
  "cross-spawn": "^7.0.3",
70
70
  "dendriform-immer-patch-optimiser": "^2.1.0",
71
71
  "dotenv": "^16.0.3",
@@ -87,7 +87,7 @@
87
87
  "ora": "^6.1.2",
88
88
  "react": "18.2.0",
89
89
  "remeda": "^1.3.0",
90
- "sst-aws-cdk": "2.84.0",
90
+ "sst-aws-cdk": "2.91.0",
91
91
  "tree-kill": "^1.2.2",
92
92
  "undici": "^5.12.0",
93
93
  "uuid": "^9.0.0",
@@ -131,7 +131,7 @@
131
131
  }
132
132
  },
133
133
  "bugs": {
134
- "url": "https://github.com/serverless-stack/sst/issues"
134
+ "url": "https://github.com/sst/sst/issues"
135
135
  },
136
136
  "main": "index.js",
137
137
  "directories": {
@@ -18,8 +18,8 @@ mv build/target/dependency/aws-lambda-java-serialization-1.0.0.jar release/
18
18
  # - AWS_LAMBDA_RUNTIME_API has to be of the format "host:port", subpath is not support (ie. "host:port/path")
19
19
  # - official JAR uses NativeClient that cannot be run on user's machine
20
20
  rm -rf aws-lambda-java-libs
21
- git clone https://github.com/serverless-stack/aws-lambda-java-libs.git
21
+ git clone https://github.com/sst/aws-lambda-java-libs.git
22
22
  cd aws-lambda-java-libs/aws-lambda-java-runtime-interface-client
23
23
  mvn -Dmaven.test.skip=true install
24
24
  cd ../..
25
- mv aws-lambda-java-libs/aws-lambda-java-runtime-interface-client/target/aws-lambda-java-runtime-interface-client-1.1.0.jar release/
25
+ mv aws-lambda-java-libs/aws-lambda-java-runtime-interface-client/target/aws-lambda-java-runtime-interface-client-1.1.0.jar release/
@@ -1,74 +0,0 @@
1
- import * as cxapi from "@aws-cdk/cx-api";
2
- import * as AWS from "aws-sdk";
3
- import * as cdk_assets from "cdk-assets";
4
- import { SdkProvider } from "sst-aws-cdk/lib/api/aws-auth/sdk-provider.js";
5
- export interface PublishAssetsOptions {
6
- /**
7
- * Print progress at 'debug' level
8
- */
9
- readonly quiet?: boolean;
10
- /**
11
- * Whether to build assets before publishing.
12
- *
13
- * @default true To remain backward compatible.
14
- */
15
- readonly buildAssets?: boolean;
16
- /**
17
- * Whether to build/publish assets in parallel
18
- *
19
- * @default true To remain backward compatible.
20
- */
21
- readonly parallel?: boolean;
22
- }
23
- /**
24
- * Use cdk-assets to publish all assets in the given manifest.
25
- */
26
- export declare function publishAssets(manifest: cdk_assets.AssetManifest, sdk: SdkProvider, targetEnv: cxapi.Environment, options?: PublishAssetsOptions): Promise<void>;
27
- export interface BuildAssetsOptions {
28
- /**
29
- * Print progress at 'debug' level
30
- */
31
- readonly quiet?: boolean;
32
- /**
33
- * Build assets in parallel
34
- *
35
- * @default true
36
- */
37
- readonly parallel?: boolean;
38
- }
39
- /**
40
- * Use cdk-assets to build all assets in the given manifest.
41
- */
42
- export declare function buildAssets(manifest: cdk_assets.AssetManifest, sdk: SdkProvider, targetEnv: cxapi.Environment, options?: BuildAssetsOptions): Promise<void>;
43
- export declare class PublishingAws implements cdk_assets.IAws {
44
- /**
45
- * The base SDK to work with
46
- */
47
- private readonly aws;
48
- /**
49
- * Environment where the stack we're deploying is going
50
- */
51
- private readonly targetEnv;
52
- private sdkCache;
53
- constructor(
54
- /**
55
- * The base SDK to work with
56
- */
57
- aws: SdkProvider,
58
- /**
59
- * Environment where the stack we're deploying is going
60
- */
61
- targetEnv: cxapi.Environment);
62
- discoverPartition(): Promise<string>;
63
- discoverDefaultRegion(): Promise<string>;
64
- discoverCurrentAccount(): Promise<cdk_assets.Account>;
65
- discoverTargetAccount(options: cdk_assets.ClientOptions): Promise<cdk_assets.Account>;
66
- s3Client(options: cdk_assets.ClientOptions): Promise<AWS.S3>;
67
- ecrClient(options: cdk_assets.ClientOptions): Promise<AWS.ECR>;
68
- secretsManagerClient(options: cdk_assets.ClientOptions): Promise<AWS.SecretsManager>;
69
- /**
70
- * Get an SDK appropriate for the given client options
71
- */
72
- private sdk;
73
- }
74
- export declare const EVENT_TO_LOGGER: Record<cdk_assets.EventType, (x: string) => void>;
@@ -1,147 +0,0 @@
1
- import * as cxapi from "@aws-cdk/cx-api";
2
- import * as cdk_assets from "cdk-assets";
3
- // TODO: remove after PR is merged
4
- import { AssetPublishing } from "../cdk-assets/publishing.js";
5
- import { Mode } from "sst-aws-cdk/lib/api/aws-auth/credentials.js";
6
- import { debug, error, print } from "sst-aws-cdk/lib/logging.js";
7
- /**
8
- * Use cdk-assets to publish all assets in the given manifest.
9
- */
10
- export async function publishAssets(manifest, sdk, targetEnv, options = {}) {
11
- // This shouldn't really happen (it's a programming error), but we don't have
12
- // the types here to guide us. Do an runtime validation to be super super sure.
13
- if (targetEnv.account === undefined ||
14
- targetEnv.account === cxapi.UNKNOWN_ACCOUNT ||
15
- targetEnv.region === undefined ||
16
- targetEnv.account === cxapi.UNKNOWN_REGION) {
17
- throw new Error(`Asset publishing requires resolved account and region, got ${JSON.stringify(targetEnv)}`);
18
- }
19
- const publisher = new AssetPublishing(manifest, {
20
- aws: new PublishingAws(sdk, targetEnv),
21
- progressListener: new PublishingProgressListener(options.quiet ?? false),
22
- throwOnError: false,
23
- publishInParallel: options.parallel ?? true,
24
- buildAssets: options.buildAssets ?? true,
25
- publishAssets: true,
26
- // TODO: remove after PR is merged
27
- quiet: options.quiet,
28
- });
29
- await publisher.publish();
30
- if (publisher.hasFailures) {
31
- console.log(publisher.failures);
32
- throw new Error("Failed to publish one or more assets. See the error messages above for more information.");
33
- }
34
- }
35
- /**
36
- * Use cdk-assets to build all assets in the given manifest.
37
- */
38
- export async function buildAssets(manifest, sdk, targetEnv, options = {}) {
39
- // This shouldn't really happen (it's a programming error), but we don't have
40
- // the types here to guide us. Do an runtime validation to be super super sure.
41
- if (targetEnv.account === undefined ||
42
- targetEnv.account === cxapi.UNKNOWN_ACCOUNT ||
43
- targetEnv.region === undefined ||
44
- targetEnv.account === cxapi.UNKNOWN_REGION) {
45
- throw new Error(`Asset building requires resolved account and region, got ${JSON.stringify(targetEnv)}`);
46
- }
47
- const publisher = new cdk_assets.AssetPublishing(manifest, {
48
- aws: new PublishingAws(sdk, targetEnv),
49
- progressListener: new PublishingProgressListener(options.quiet ?? false),
50
- throwOnError: false,
51
- publishInParallel: options.parallel ?? true,
52
- buildAssets: true,
53
- publishAssets: false,
54
- });
55
- await publisher.publish();
56
- if (publisher.hasFailures) {
57
- throw new Error("Failed to build one or more assets. See the error messages above for more information.");
58
- }
59
- }
60
- export class PublishingAws {
61
- aws;
62
- targetEnv;
63
- sdkCache = new Map();
64
- constructor(
65
- /**
66
- * The base SDK to work with
67
- */
68
- aws,
69
- /**
70
- * Environment where the stack we're deploying is going
71
- */
72
- targetEnv) {
73
- this.aws = aws;
74
- this.targetEnv = targetEnv;
75
- }
76
- async discoverPartition() {
77
- return ((await this.aws.baseCredentialsPartition(this.targetEnv, Mode.ForWriting)) ?? "aws");
78
- }
79
- async discoverDefaultRegion() {
80
- return this.targetEnv.region;
81
- }
82
- async discoverCurrentAccount() {
83
- const account = await this.aws.defaultAccount();
84
- return (account ?? {
85
- accountId: "<unknown account>",
86
- partition: "aws",
87
- });
88
- }
89
- async discoverTargetAccount(options) {
90
- return (await this.sdk(options)).currentAccount();
91
- }
92
- async s3Client(options) {
93
- return (await this.sdk(options)).s3();
94
- }
95
- async ecrClient(options) {
96
- return (await this.sdk(options)).ecr();
97
- }
98
- async secretsManagerClient(options) {
99
- return (await this.sdk(options)).secretsManager();
100
- }
101
- /**
102
- * Get an SDK appropriate for the given client options
103
- */
104
- async sdk(options) {
105
- const env = {
106
- ...this.targetEnv,
107
- region: options.region ?? this.targetEnv.region, // Default: same region as the stack
108
- };
109
- const cacheKey = JSON.stringify({
110
- env,
111
- assumeRuleArn: options.assumeRoleArn,
112
- assumeRoleExternalId: options.assumeRoleExternalId,
113
- quiet: options.quiet,
114
- });
115
- const maybeSdk = this.sdkCache.get(cacheKey);
116
- if (maybeSdk) {
117
- return maybeSdk;
118
- }
119
- const sdk = (await this.aws.forEnvironment(env, Mode.ForWriting, {
120
- assumeRoleArn: options.assumeRoleArn,
121
- assumeRoleExternalId: options.assumeRoleExternalId,
122
- }, options.quiet)).sdk;
123
- this.sdkCache.set(cacheKey, sdk);
124
- return sdk;
125
- }
126
- }
127
- export const EVENT_TO_LOGGER = {
128
- build: debug,
129
- cached: debug,
130
- check: debug,
131
- debug,
132
- fail: error,
133
- found: debug,
134
- start: print,
135
- success: print,
136
- upload: debug,
137
- };
138
- class PublishingProgressListener {
139
- quiet;
140
- constructor(quiet) {
141
- this.quiet = quiet;
142
- }
143
- onPublishEvent(type, event) {
144
- const handler = this.quiet && type !== "fail" ? debug : EVENT_TO_LOGGER[type];
145
- handler(`[${event.percentComplete}%] ${type}: ${event.message}`);
146
- }
147
- }
@@ -1,29 +0,0 @@
1
- import { DockerFactory } from "./docker.js";
2
- import { IAws } from "cdk-assets/lib/aws.js";
3
- import { EventType } from "cdk-assets/lib/progress.js";
4
- /**
5
- * Handler for asset building and publishing.
6
- */
7
- export interface IAssetHandler {
8
- /**
9
- * Build the asset.
10
- */
11
- build(): Promise<void>;
12
- /**
13
- * Publish the asset.
14
- */
15
- publish(): Promise<void>;
16
- /**
17
- * Return whether the asset already exists
18
- */
19
- isPublished(): Promise<boolean>;
20
- }
21
- export interface IHandlerHost {
22
- readonly aws: IAws;
23
- readonly aborted: boolean;
24
- readonly dockerFactory: DockerFactory;
25
- emitMessage(type: EventType, m: string): void;
26
- }
27
- export interface IHandlerOptions {
28
- readonly quiet?: boolean;
29
- }
@@ -1 +0,0 @@
1
- export {};
@@ -1,94 +0,0 @@
1
- import { Logger } from "cdk-assets/lib/private/shell.js";
2
- interface BuildOptions {
3
- readonly directory: string;
4
- /**
5
- * Tag the image with a given repoName:tag combination
6
- */
7
- readonly tag: string;
8
- readonly target?: string;
9
- readonly file?: string;
10
- readonly buildArgs?: Record<string, string>;
11
- readonly buildSecrets?: Record<string, string>;
12
- readonly networkMode?: string;
13
- readonly platform?: string;
14
- readonly outputs?: string[];
15
- readonly cacheFrom?: DockerCacheOption[];
16
- readonly cacheTo?: DockerCacheOption;
17
- readonly quiet?: boolean;
18
- }
19
- interface PushOptions {
20
- readonly tag: string;
21
- readonly quiet?: boolean;
22
- }
23
- export interface DockerCredentialsConfig {
24
- readonly version: string;
25
- readonly domainCredentials: Record<string, DockerDomainCredentials>;
26
- }
27
- export interface DockerDomainCredentials {
28
- readonly secretsManagerSecretId?: string;
29
- readonly ecrRepository?: string;
30
- }
31
- export interface DockerCacheOption {
32
- readonly type: string;
33
- readonly params?: {
34
- [key: string]: string;
35
- };
36
- }
37
- export declare class Docker {
38
- private readonly logger?;
39
- private configDir;
40
- constructor(logger?: Logger | undefined);
41
- /**
42
- * Whether an image with the given tag exists
43
- */
44
- exists(tag: string): Promise<boolean>;
45
- build(options: BuildOptions): Promise<void>;
46
- /**
47
- * Get credentials from ECR and run docker login
48
- */
49
- login(ecr: AWS.ECR): Promise<void>;
50
- tag(sourceTag: string, targetTag: string): Promise<void>;
51
- push(options: PushOptions): Promise<void>;
52
- /**
53
- * If a CDK Docker Credentials file exists, creates a new Docker config directory.
54
- * Sets up `docker-credential-cdk-assets` to be the credential helper for each domain in the CDK config.
55
- * All future commands (e.g., `build`, `push`) will use this config.
56
- *
57
- * See https://docs.docker.com/engine/reference/commandline/login/#credential-helpers for more details on cred helpers.
58
- *
59
- * @returns true if CDK config was found and configured, false otherwise
60
- */
61
- configureCdkCredentials(): boolean;
62
- /**
63
- * Removes any configured Docker config directory.
64
- * All future commands (e.g., `build`, `push`) will use the default config.
65
- *
66
- * This is useful after calling `configureCdkCredentials` to reset to default credentials.
67
- */
68
- resetAuthPlugins(): void;
69
- private execute;
70
- private cacheOptionToFlag;
71
- }
72
- export interface DockerFactoryOptions {
73
- readonly repoUri: string;
74
- readonly ecr: AWS.ECR;
75
- readonly logger: (m: string) => void;
76
- }
77
- /**
78
- * Helps get appropriately configured Docker instances during the container
79
- * image publishing process.
80
- */
81
- export declare class DockerFactory {
82
- private enterLoggedInDestinationsCriticalSection;
83
- private loggedInDestinations;
84
- /**
85
- * Gets a Docker instance for building images.
86
- */
87
- forBuild(options: DockerFactoryOptions): Promise<Docker>;
88
- /**
89
- * Gets a Docker instance for pushing images to ECR.
90
- */
91
- forEcrPush(options: DockerFactoryOptions): Promise<Docker>;
92
- private loginOncePerDestination;
93
- }
94
- export {};
@@ -1,237 +0,0 @@
1
- import * as fs from "fs";
2
- import * as os from "os";
3
- import * as path from "path";
4
- import { cdkCredentialsConfig, obtainEcrCredentials, } from "cdk-assets/lib/private/docker-credentials.js";
5
- import { shell, } from "cdk-assets/lib/private/shell.js";
6
- import { createCriticalSection } from "cdk-assets/lib/private/util.js";
7
- var InspectImageErrorCode;
8
- (function (InspectImageErrorCode) {
9
- InspectImageErrorCode[InspectImageErrorCode["Docker"] = 1] = "Docker";
10
- InspectImageErrorCode[InspectImageErrorCode["Podman"] = 125] = "Podman";
11
- })(InspectImageErrorCode || (InspectImageErrorCode = {}));
12
- export class Docker {
13
- logger;
14
- configDir = undefined;
15
- constructor(logger) {
16
- this.logger = logger;
17
- }
18
- /**
19
- * Whether an image with the given tag exists
20
- */
21
- async exists(tag) {
22
- try {
23
- await this.execute(["inspect", tag], { quiet: true });
24
- return true;
25
- }
26
- catch (e) {
27
- const error = e;
28
- /**
29
- * The only error we expect to be thrown will have this property and value.
30
- * If it doesn't, it's unrecognized so re-throw it.
31
- */
32
- if (error.code !== "PROCESS_FAILED") {
33
- throw error;
34
- }
35
- /**
36
- * If we know the shell command above returned an error, check to see
37
- * if the exit code is one we know to actually mean that the image doesn't
38
- * exist.
39
- */
40
- switch (error.exitCode) {
41
- case InspectImageErrorCode.Docker:
42
- case InspectImageErrorCode.Podman:
43
- // Docker and Podman will return this exit code when an image doesn't exist, return false
44
- // context: https://github.com/aws/aws-cdk/issues/16209
45
- return false;
46
- default:
47
- // This is an error but it's not an exit code we recognize, throw.
48
- throw error;
49
- }
50
- }
51
- }
52
- async build(options) {
53
- const buildCommand = [
54
- "build",
55
- ...flatten(Object.entries(options.buildArgs || {}).map(([k, v]) => [
56
- "--build-arg",
57
- `${k}=${v}`,
58
- ])),
59
- ...flatten(Object.entries(options.buildSecrets || {}).map(([k, v]) => [
60
- "--secret",
61
- `id=${k},${v}`,
62
- ])),
63
- "--tag",
64
- options.tag,
65
- ...(options.target ? ["--target", options.target] : []),
66
- ...(options.file ? ["--file", options.file] : []),
67
- ...(options.networkMode ? ["--network", options.networkMode] : []),
68
- ...(options.platform ? ["--platform", options.platform] : []),
69
- ...(options.outputs
70
- ? options.outputs.map((output) => [`--output=${output}`])
71
- : []),
72
- ...(options.cacheFrom
73
- ? [
74
- ...options.cacheFrom
75
- .map((cacheFrom) => [
76
- "--cache-from",
77
- this.cacheOptionToFlag(cacheFrom),
78
- ])
79
- .flat(),
80
- ]
81
- : []),
82
- ...(options.cacheTo
83
- ? ["--cache-to", this.cacheOptionToFlag(options.cacheTo)]
84
- : []),
85
- ".",
86
- ];
87
- await this.execute(buildCommand, {
88
- cwd: options.directory,
89
- // TODO: remove after PR is merged
90
- quiet: options.quiet,
91
- });
92
- }
93
- /**
94
- * Get credentials from ECR and run docker login
95
- */
96
- async login(ecr) {
97
- const credentials = await obtainEcrCredentials(ecr);
98
- // Use --password-stdin otherwise docker will complain. Loudly.
99
- await this.execute([
100
- "login",
101
- "--username",
102
- credentials.username,
103
- "--password-stdin",
104
- credentials.endpoint,
105
- ], {
106
- input: credentials.password,
107
- // Need to quiet otherwise Docker will complain
108
- // 'WARNING! Your password will be stored unencrypted'
109
- // doesn't really matter since it's a token.
110
- quiet: true,
111
- });
112
- }
113
- async tag(sourceTag, targetTag) {
114
- await this.execute(["tag", sourceTag, targetTag]);
115
- }
116
- // TODO: remove after PR is merged
117
- async push(options) {
118
- await this.execute(["push", options.tag], { quiet: options.quiet });
119
- }
120
- /**
121
- * If a CDK Docker Credentials file exists, creates a new Docker config directory.
122
- * Sets up `docker-credential-cdk-assets` to be the credential helper for each domain in the CDK config.
123
- * All future commands (e.g., `build`, `push`) will use this config.
124
- *
125
- * See https://docs.docker.com/engine/reference/commandline/login/#credential-helpers for more details on cred helpers.
126
- *
127
- * @returns true if CDK config was found and configured, false otherwise
128
- */
129
- configureCdkCredentials() {
130
- const config = cdkCredentialsConfig();
131
- if (!config) {
132
- return false;
133
- }
134
- this.configDir = fs.mkdtempSync(path.join(os.tmpdir(), "cdkDockerConfig"));
135
- const domains = Object.keys(config.domainCredentials);
136
- const credHelpers = domains.reduce((map, domain) => {
137
- map[domain] = "cdk-assets"; // Use docker-credential-cdk-assets for this domain
138
- return map;
139
- }, {});
140
- fs.writeFileSync(path.join(this.configDir, "config.json"), JSON.stringify({ credHelpers }), { encoding: "utf-8" });
141
- return true;
142
- }
143
- /**
144
- * Removes any configured Docker config directory.
145
- * All future commands (e.g., `build`, `push`) will use the default config.
146
- *
147
- * This is useful after calling `configureCdkCredentials` to reset to default credentials.
148
- */
149
- resetAuthPlugins() {
150
- this.configDir = undefined;
151
- }
152
- async execute(args, options = {}) {
153
- const configArgs = this.configDir ? ["--config", this.configDir] : [];
154
- // TODO: remove after PR is merged
155
- //const pathToCdkAssets = path.resolve(__dirname, "..", "..", "bin");
156
- const pathToCdkAssets = "";
157
- try {
158
- await shell([getDockerCmd(), ...configArgs, ...args], {
159
- logger: this.logger,
160
- ...options,
161
- env: {
162
- ...process.env,
163
- ...options.env,
164
- PATH: `${pathToCdkAssets}${path.delimiter}${options.env?.PATH ?? process.env.PATH}`,
165
- },
166
- });
167
- }
168
- catch (e) {
169
- if (e.code === "ENOENT") {
170
- throw new Error("Unable to execute 'docker' in order to build a container asset. Please install 'docker' and try again.");
171
- }
172
- throw e;
173
- }
174
- }
175
- cacheOptionToFlag(option) {
176
- let flag = `type=${option.type}`;
177
- if (option.params) {
178
- flag +=
179
- "," +
180
- Object.entries(option.params)
181
- .map(([k, v]) => `${k}=${v}`)
182
- .join(",");
183
- }
184
- return flag;
185
- }
186
- }
187
- /**
188
- * Helps get appropriately configured Docker instances during the container
189
- * image publishing process.
190
- */
191
- export class DockerFactory {
192
- enterLoggedInDestinationsCriticalSection = createCriticalSection();
193
- loggedInDestinations = new Set();
194
- /**
195
- * Gets a Docker instance for building images.
196
- */
197
- async forBuild(options) {
198
- const docker = new Docker(options.logger);
199
- // Default behavior is to login before build so that the Dockerfile can reference images in the ECR repo
200
- // However, if we're in a pipelines environment (for example),
201
- // we may have alternative credentials to the default ones to use for the build itself.
202
- // If the special config file is present, delay the login to the default credentials until the push.
203
- // If the config file is present, we will configure and use those credentials for the build.
204
- let cdkDockerCredentialsConfigured = docker.configureCdkCredentials();
205
- if (!cdkDockerCredentialsConfigured) {
206
- await this.loginOncePerDestination(docker, options);
207
- }
208
- return docker;
209
- }
210
- /**
211
- * Gets a Docker instance for pushing images to ECR.
212
- */
213
- async forEcrPush(options) {
214
- const docker = new Docker(options.logger);
215
- await this.loginOncePerDestination(docker, options);
216
- return docker;
217
- }
218
- async loginOncePerDestination(docker, options) {
219
- // Changes: 012345678910.dkr.ecr.us-west-2.amazonaws.com/tagging-test
220
- // To this: 012345678910.dkr.ecr.us-west-2.amazonaws.com
221
- const repositoryDomain = options.repoUri.split("/")[0];
222
- // Ensure one-at-a-time access to loggedInDestinations.
223
- await this.enterLoggedInDestinationsCriticalSection(async () => {
224
- if (this.loggedInDestinations.has(repositoryDomain)) {
225
- return;
226
- }
227
- await docker.login(options.ecr);
228
- this.loggedInDestinations.add(repositoryDomain);
229
- });
230
- }
231
- }
232
- function getDockerCmd() {
233
- return process.env.CDK_DOCKER ?? "docker";
234
- }
235
- function flatten(x) {
236
- return Array.prototype.concat([], ...x);
237
- }
@@ -1,22 +0,0 @@
1
- import { DockerImageManifestEntry } from "cdk-assets/lib/asset-manifest.js";
2
- import { IAssetHandler, IHandlerHost, IHandlerOptions } from "../asset-handler.js";
3
- export declare class ContainerImageAssetHandler implements IAssetHandler {
4
- private readonly workDir;
5
- private readonly asset;
6
- private readonly host;
7
- private readonly options;
8
- private init?;
9
- constructor(workDir: string, asset: DockerImageManifestEntry, host: IHandlerHost, options: IHandlerOptions);
10
- build(): Promise<void>;
11
- isPublished(): Promise<boolean>;
12
- publish(): Promise<void>;
13
- private initOnce;
14
- /**
15
- * Check whether the image already exists in the ECR repo
16
- *
17
- * Use the fields from the destination to do the actual check. The imageUri
18
- * should correspond to that, but is only used to print Docker image location
19
- * for user benefit (the format is slightly different).
20
- */
21
- private destinationAlreadyExists;
22
- }
@@ -1,231 +0,0 @@
1
- import * as path from "path";
2
- import { EventType } from "cdk-assets/lib/progress.js";
3
- import { replaceAwsPlaceholders } from "cdk-assets/lib/private/placeholders.js";
4
- import { shell } from "cdk-assets/lib/private/shell.js";
5
- export class ContainerImageAssetHandler {
6
- workDir;
7
- asset;
8
- host;
9
- options;
10
- init;
11
- constructor(workDir, asset, host,
12
- // TODO: remove after PR is merged
13
- options) {
14
- this.workDir = workDir;
15
- this.asset = asset;
16
- this.host = host;
17
- this.options = options;
18
- }
19
- async build() {
20
- const initOnce = await this.initOnce();
21
- if (initOnce.destinationAlreadyExists) {
22
- return;
23
- }
24
- if (this.host.aborted) {
25
- return;
26
- }
27
- const dockerForBuilding = await this.host.dockerFactory.forBuild({
28
- repoUri: initOnce.repoUri,
29
- logger: (m) => this.host.emitMessage(EventType.DEBUG, m),
30
- ecr: initOnce.ecr,
31
- });
32
- const builder = new ContainerImageBuilder(dockerForBuilding, this.workDir, this.asset, this.host,
33
- // TODO: remove after PR is merged
34
- {
35
- quiet: this.options.quiet,
36
- });
37
- const localTagName = await builder.build();
38
- if (localTagName === undefined || this.host.aborted) {
39
- return;
40
- }
41
- if (this.host.aborted) {
42
- return;
43
- }
44
- await dockerForBuilding.tag(localTagName, initOnce.imageUri);
45
- }
46
- async isPublished() {
47
- try {
48
- const initOnce = await this.initOnce({ quiet: true });
49
- return initOnce.destinationAlreadyExists;
50
- }
51
- catch (e) {
52
- this.host.emitMessage(EventType.DEBUG, `${e.message}`);
53
- }
54
- return false;
55
- }
56
- async publish() {
57
- const initOnce = await this.initOnce();
58
- if (initOnce.destinationAlreadyExists) {
59
- return;
60
- }
61
- if (this.host.aborted) {
62
- return;
63
- }
64
- const dockerForPushing = await this.host.dockerFactory.forEcrPush({
65
- repoUri: initOnce.repoUri,
66
- logger: (m) => this.host.emitMessage(EventType.DEBUG, m),
67
- ecr: initOnce.ecr,
68
- });
69
- if (this.host.aborted) {
70
- return;
71
- }
72
- this.host.emitMessage(EventType.UPLOAD, `Push ${initOnce.imageUri}`);
73
- // TODO: remove after PR is merged
74
- await dockerForPushing.push({
75
- tag: initOnce.imageUri,
76
- quiet: this.options.quiet,
77
- });
78
- }
79
- async initOnce(options = {}) {
80
- if (this.init) {
81
- return this.init;
82
- }
83
- const destination = await replaceAwsPlaceholders(this.asset.destination, this.host.aws);
84
- const ecr = await this.host.aws.ecrClient({
85
- ...destination,
86
- quiet: options.quiet,
87
- });
88
- const account = async () => (await this.host.aws.discoverCurrentAccount())?.accountId;
89
- const repoUri = await repositoryUri(ecr, destination.repositoryName);
90
- if (!repoUri) {
91
- throw new Error(`No ECR repository named '${destination.repositoryName}' in account ${await account()}. Is this account bootstrapped?`);
92
- }
93
- const imageUri = `${repoUri}:${destination.imageTag}`;
94
- this.init = {
95
- imageUri,
96
- ecr,
97
- repoUri,
98
- destinationAlreadyExists: await this.destinationAlreadyExists(ecr, destination, imageUri),
99
- };
100
- return this.init;
101
- }
102
- /**
103
- * Check whether the image already exists in the ECR repo
104
- *
105
- * Use the fields from the destination to do the actual check. The imageUri
106
- * should correspond to that, but is only used to print Docker image location
107
- * for user benefit (the format is slightly different).
108
- */
109
- async destinationAlreadyExists(ecr, destination, imageUri) {
110
- this.host.emitMessage(EventType.CHECK, `Check ${imageUri}`);
111
- if (await imageExists(ecr, destination.repositoryName, destination.imageTag)) {
112
- this.host.emitMessage(EventType.FOUND, `Found ${imageUri}`);
113
- return true;
114
- }
115
- return false;
116
- }
117
- }
118
- class ContainerImageBuilder {
119
- docker;
120
- workDir;
121
- asset;
122
- host;
123
- options;
124
- constructor(docker, workDir, asset, host,
125
- // TODO: remove after PR is merged
126
- options) {
127
- this.docker = docker;
128
- this.workDir = workDir;
129
- this.asset = asset;
130
- this.host = host;
131
- this.options = options;
132
- }
133
- async build() {
134
- return this.asset.source.executable
135
- ? this.buildExternalAsset(this.asset.source.executable)
136
- : this.buildDirectoryAsset();
137
- }
138
- /**
139
- * Build a (local) Docker asset from a directory with a Dockerfile
140
- *
141
- * Tags under a deterministic, unique, local identifier wich will skip
142
- * the build if it already exists.
143
- */
144
- async buildDirectoryAsset() {
145
- const localTagName = `cdkasset-${this.asset.id.assetId.toLowerCase()}`;
146
- if (!(await this.isImageCached(localTagName))) {
147
- if (this.host.aborted) {
148
- return undefined;
149
- }
150
- await this.buildImage(localTagName);
151
- }
152
- return localTagName;
153
- }
154
- /**
155
- * Build a (local) Docker asset by running an external command
156
- *
157
- * External command is responsible for deduplicating the build if possible,
158
- * and is expected to return the generated image identifier on stdout.
159
- */
160
- async buildExternalAsset(executable, cwd) {
161
- const assetPath = cwd ?? this.workDir;
162
- this.host.emitMessage(EventType.BUILD, `Building Docker image using command '${executable}'`);
163
- if (this.host.aborted) {
164
- return undefined;
165
- }
166
- return (await shell(executable, { cwd: assetPath, quiet: true })).trim();
167
- }
168
- async buildImage(localTagName) {
169
- const source = this.asset.source;
170
- if (!source.directory) {
171
- throw new Error(`'directory' is expected in the DockerImage asset source, got: ${JSON.stringify(source)}`);
172
- }
173
- const fullPath = path.resolve(this.workDir, source.directory);
174
- this.host.emitMessage(EventType.BUILD, `Building Docker image at ${fullPath}`);
175
- await this.docker.build({
176
- directory: fullPath,
177
- tag: localTagName,
178
- buildArgs: source.dockerBuildArgs,
179
- buildSecrets: source.dockerBuildSecrets,
180
- target: source.dockerBuildTarget,
181
- file: source.dockerFile,
182
- networkMode: source.networkMode,
183
- platform: source.platform,
184
- outputs: source.dockerOutputs,
185
- cacheFrom: source.cacheFrom,
186
- cacheTo: source.cacheTo,
187
- // TODO: remove after PR is merged
188
- quiet: this.options.quiet,
189
- });
190
- }
191
- async isImageCached(localTagName) {
192
- if (await this.docker.exists(localTagName)) {
193
- this.host.emitMessage(EventType.CACHED, `Cached ${localTagName}`);
194
- return true;
195
- }
196
- return false;
197
- }
198
- }
199
- async function imageExists(ecr, repositoryName, imageTag) {
200
- try {
201
- await ecr
202
- .describeImages({ repositoryName, imageIds: [{ imageTag }] })
203
- .promise();
204
- return true;
205
- }
206
- catch (e) {
207
- if (e.code !== "ImageNotFoundException") {
208
- throw e;
209
- }
210
- return false;
211
- }
212
- }
213
- /**
214
- * Return the URI for the repository with the given name
215
- *
216
- * Returns undefined if the repository does not exist.
217
- */
218
- async function repositoryUri(ecr, repositoryName) {
219
- try {
220
- const response = await ecr
221
- .describeRepositories({ repositoryNames: [repositoryName] })
222
- .promise();
223
- return (response.repositories || [])[0]?.repositoryUri;
224
- }
225
- catch (e) {
226
- if (e.code !== "RepositoryNotFoundException") {
227
- throw e;
228
- }
229
- return undefined;
230
- }
231
- }
@@ -1,3 +0,0 @@
1
- import { AssetManifest, IManifestEntry } from "cdk-assets/lib/asset-manifest.js";
2
- import { IAssetHandler, IHandlerHost, IHandlerOptions } from "../asset-handler.js";
3
- export declare function makeAssetHandler(manifest: AssetManifest, asset: IManifestEntry, host: IHandlerHost, options: IHandlerOptions): IAssetHandler;
@@ -1,18 +0,0 @@
1
- import { ContainerImageAssetHandler } from "./container-images.js";
2
- import { FileAssetHandler } from "cdk-assets/lib/private/handlers/files.js";
3
- import { DockerImageManifestEntry, FileManifestEntry, } from "cdk-assets/lib/asset-manifest.js";
4
- export function makeAssetHandler(manifest, asset, host,
5
- // TODO: remove after PR is merged
6
- options) {
7
- if (asset instanceof FileManifestEntry) {
8
- // TODO: remove after PR is merged
9
- // @ts-ignore
10
- return new FileAssetHandler(manifest.directory, asset, host);
11
- }
12
- if (asset instanceof DockerImageManifestEntry) {
13
- return new ContainerImageAssetHandler(manifest.directory, asset, host,
14
- // TODO: remove after PR is merged
15
- options);
16
- }
17
- throw new Error(`Unrecognized asset type: '${asset}'`);
18
- }
@@ -1,113 +0,0 @@
1
- import { AssetManifest, IManifestEntry } from "cdk-assets/lib/asset-manifest.js";
2
- import { IAws } from "cdk-assets/lib/aws.js";
3
- import { IPublishProgress, IPublishProgressListener } from "cdk-assets/lib/progress.js";
4
- export interface AssetPublishingOptions {
5
- /**
6
- * Entry point for AWS client
7
- */
8
- readonly aws: IAws;
9
- /**
10
- * Listener for progress events
11
- *
12
- * @default No listener
13
- */
14
- readonly progressListener?: IPublishProgressListener;
15
- /**
16
- * Whether to throw at the end if there were errors
17
- *
18
- * @default true
19
- */
20
- readonly throwOnError?: boolean;
21
- /**
22
- * Whether to publish in parallel, when 'publish()' is called
23
- *
24
- * @default false
25
- */
26
- readonly publishInParallel?: boolean;
27
- /**
28
- * Whether to build assets, when 'publish()' is called
29
- *
30
- * @default true
31
- */
32
- readonly buildAssets?: boolean;
33
- /**
34
- * Whether to publish assets, when 'publish()' is called
35
- *
36
- * @default true
37
- */
38
- readonly publishAssets?: boolean;
39
- /**
40
- * Whether to print publishing logs
41
- *
42
- * @default true
43
- */
44
- readonly quiet?: boolean;
45
- }
46
- /**
47
- * A failure to publish an asset
48
- */
49
- export interface FailedAsset {
50
- /**
51
- * The asset that failed to publish
52
- */
53
- readonly asset: IManifestEntry;
54
- /**
55
- * The failure that occurred
56
- */
57
- readonly error: Error;
58
- }
59
- export declare class AssetPublishing implements IPublishProgress {
60
- private readonly manifest;
61
- private readonly options;
62
- /**
63
- * The message for the IPublishProgress interface
64
- */
65
- message: string;
66
- /**
67
- * The current asset for the IPublishProgress interface
68
- */
69
- currentAsset?: IManifestEntry;
70
- readonly failures: FailedAsset[];
71
- private readonly assets;
72
- private readonly totalOperations;
73
- private completedOperations;
74
- private aborted;
75
- private readonly handlerHost;
76
- private readonly publishInParallel;
77
- private readonly buildAssets;
78
- private readonly publishAssets;
79
- private readonly handlerCache;
80
- constructor(manifest: AssetManifest, options: AssetPublishingOptions);
81
- /**
82
- * Publish all assets from the manifest
83
- */
84
- publish(): Promise<void>;
85
- /**
86
- * Build a single asset from the manifest
87
- */
88
- buildEntry(asset: IManifestEntry): Promise<boolean>;
89
- /**
90
- * Publish a single asset from the manifest
91
- */
92
- publishEntry(asset: IManifestEntry): Promise<boolean>;
93
- /**
94
- * Return whether a single asset is published
95
- */
96
- isEntryPublished(asset: IManifestEntry): Promise<boolean>;
97
- /**
98
- * publish an asset (used by 'publish()')
99
- * @param asset The asset to publish
100
- * @returns false when publishing should stop
101
- */
102
- private publishAsset;
103
- get percentComplete(): number;
104
- abort(): void;
105
- get hasFailures(): boolean;
106
- /**
107
- * Publish a progress event to the listener, if present.
108
- *
109
- * Returns whether an abort is requested. Helper to get rid of repetitive code in publish().
110
- */
111
- private progressEvent;
112
- private assetHandler;
113
- }
@@ -1,194 +0,0 @@
1
- import { DockerFactory } from "./private/docker.js";
2
- import { makeAssetHandler } from "./private/handlers/index.js";
3
- import { EventType, } from "cdk-assets/lib/progress.js";
4
- export class AssetPublishing {
5
- manifest;
6
- options;
7
- /**
8
- * The message for the IPublishProgress interface
9
- */
10
- message = "Starting";
11
- /**
12
- * The current asset for the IPublishProgress interface
13
- */
14
- currentAsset;
15
- failures = new Array();
16
- assets;
17
- totalOperations;
18
- completedOperations = 0;
19
- aborted = false;
20
- handlerHost;
21
- publishInParallel;
22
- buildAssets;
23
- publishAssets;
24
- handlerCache = new Map();
25
- constructor(manifest, options) {
26
- this.manifest = manifest;
27
- this.options = options;
28
- this.assets = manifest.entries;
29
- this.totalOperations = this.assets.length;
30
- this.publishInParallel = options.publishInParallel ?? false;
31
- this.buildAssets = options.buildAssets ?? true;
32
- this.publishAssets = options.publishAssets ?? true;
33
- const self = this;
34
- this.handlerHost = {
35
- aws: this.options.aws,
36
- get aborted() {
37
- return self.aborted;
38
- },
39
- emitMessage(t, m) {
40
- self.progressEvent(t, m);
41
- },
42
- dockerFactory: new DockerFactory(),
43
- };
44
- }
45
- /**
46
- * Publish all assets from the manifest
47
- */
48
- async publish() {
49
- if (this.publishInParallel) {
50
- await Promise.all(this.assets.map(async (asset) => this.publishAsset(asset)));
51
- }
52
- else {
53
- for (const asset of this.assets) {
54
- if (!(await this.publishAsset(asset))) {
55
- break;
56
- }
57
- }
58
- }
59
- if ((this.options.throwOnError ?? true) && this.failures.length > 0) {
60
- throw new Error(`Error publishing: ${this.failures.map((e) => e.error.message)}`);
61
- }
62
- }
63
- /**
64
- * Build a single asset from the manifest
65
- */
66
- async buildEntry(asset) {
67
- try {
68
- if (this.progressEvent(EventType.START, `Building ${asset.id}`)) {
69
- return false;
70
- }
71
- const handler = this.assetHandler(asset);
72
- await handler.build();
73
- if (this.aborted) {
74
- throw new Error("Aborted");
75
- }
76
- this.completedOperations++;
77
- if (this.progressEvent(EventType.SUCCESS, `Built ${asset.id}`)) {
78
- return false;
79
- }
80
- }
81
- catch (e) {
82
- this.failures.push({ asset, error: e });
83
- this.completedOperations++;
84
- if (this.progressEvent(EventType.FAIL, e.message)) {
85
- return false;
86
- }
87
- }
88
- return true;
89
- }
90
- /**
91
- * Publish a single asset from the manifest
92
- */
93
- async publishEntry(asset) {
94
- try {
95
- if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) {
96
- return false;
97
- }
98
- const handler = this.assetHandler(asset);
99
- await handler.publish();
100
- if (this.aborted) {
101
- throw new Error("Aborted");
102
- }
103
- this.completedOperations++;
104
- if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) {
105
- return false;
106
- }
107
- }
108
- catch (e) {
109
- this.failures.push({ asset, error: e });
110
- this.completedOperations++;
111
- if (this.progressEvent(EventType.FAIL, e.message)) {
112
- return false;
113
- }
114
- }
115
- return true;
116
- }
117
- /**
118
- * Return whether a single asset is published
119
- */
120
- isEntryPublished(asset) {
121
- const handler = this.assetHandler(asset);
122
- return handler.isPublished();
123
- }
124
- /**
125
- * publish an asset (used by 'publish()')
126
- * @param asset The asset to publish
127
- * @returns false when publishing should stop
128
- */
129
- async publishAsset(asset) {
130
- try {
131
- if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) {
132
- return false;
133
- }
134
- const handler = this.assetHandler(asset);
135
- if (this.buildAssets) {
136
- await handler.build();
137
- }
138
- if (this.publishAssets) {
139
- await handler.publish();
140
- }
141
- if (this.aborted) {
142
- throw new Error("Aborted");
143
- }
144
- this.completedOperations++;
145
- if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) {
146
- return false;
147
- }
148
- }
149
- catch (e) {
150
- this.failures.push({ asset, error: e });
151
- this.completedOperations++;
152
- if (this.progressEvent(EventType.FAIL, e.message)) {
153
- return false;
154
- }
155
- }
156
- return true;
157
- }
158
- get percentComplete() {
159
- if (this.totalOperations === 0) {
160
- return 100;
161
- }
162
- return Math.floor((this.completedOperations / this.totalOperations) * 100);
163
- }
164
- abort() {
165
- this.aborted = true;
166
- }
167
- get hasFailures() {
168
- return this.failures.length > 0;
169
- }
170
- /**
171
- * Publish a progress event to the listener, if present.
172
- *
173
- * Returns whether an abort is requested. Helper to get rid of repetitive code in publish().
174
- */
175
- progressEvent(event, message) {
176
- this.message = message;
177
- if (this.options.progressListener) {
178
- this.options.progressListener.onPublishEvent(event, this);
179
- }
180
- return this.aborted;
181
- }
182
- assetHandler(asset) {
183
- const existing = this.handlerCache.get(asset);
184
- if (existing) {
185
- return existing;
186
- }
187
- const ret = makeAssetHandler(this.manifest, asset, this.handlerHost, {
188
- // TODO: remove after PR is merged
189
- quiet: this.options.quiet,
190
- });
191
- this.handlerCache.set(asset, ret);
192
- return ret;
193
- }
194
- }