Skip to content

Commit 7c65899

Browse files
rix0rrrgithub-actions
and
github-actions
authored
feat: assets can have a display name (#175)
Add a new field to the asset schema: `displayName`. If supplied, it will be used instead of the asset ID for display purposes in `cdk-assets` and in the CLI. This adds support for display names to the contract and to the CLI. A future PR in `aws-cdk-lib` will make it possible to configure display names on Asset objects, and add the same support to CDK Pipelines. --- By submitting this pull request, I confirm that my contribution is made under the terms of the Apache-2.0 license --------- Signed-off-by: github-actions <github-actions@github.com> Co-authored-by: github-actions <github-actions@github.com>
1 parent 478113d commit 7c65899

File tree

10 files changed

+134
-23
lines changed

10 files changed

+134
-23
lines changed

packages/@aws-cdk/cloud-assembly-schema/lib/assets/docker-image-asset.ts

+9-2
Original file line numberDiff line numberDiff line change
@@ -5,12 +5,19 @@ import { AwsDestination } from './aws-destination';
55
*/
66
export interface DockerImageAsset {
77
/**
8-
* Source description for file assets
8+
* A display name for this asset
9+
*
10+
* @default - The identifier will be used as the display name
11+
*/
12+
readonly displayName?: string;
13+
14+
/**
15+
* Source description for container assets
916
*/
1017
readonly source: DockerImageSource;
1118

1219
/**
13-
* Destinations for this file asset
20+
* Destinations for this container asset
1421
*/
1522
readonly destinations: { [id: string]: DockerImageDestination };
1623
}

packages/@aws-cdk/cloud-assembly-schema/lib/assets/file-asset.ts

+7
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,13 @@ import { AwsDestination } from './aws-destination';
44
* A file asset
55
*/
66
export interface FileAsset {
7+
/**
8+
* A display name for this asset
9+
*
10+
* @default - The identifier will be used as the display name
11+
*/
12+
readonly displayName?: string;
13+
714
/**
815
* Source description for file assets
916
*/

packages/@aws-cdk/cloud-assembly-schema/schema/assets.schema.json

+10-2
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,10 @@
3232
"description": "A file asset",
3333
"type": "object",
3434
"properties": {
35+
"displayName": {
36+
"description": "A display name for this asset (Default - The identifier will be used as the display name)",
37+
"type": "string"
38+
},
3539
"source": {
3640
"$ref": "#/definitions/FileSource",
3741
"description": "Source description for file assets"
@@ -113,12 +117,16 @@
113117
"description": "A file asset",
114118
"type": "object",
115119
"properties": {
120+
"displayName": {
121+
"description": "A display name for this asset (Default - The identifier will be used as the display name)",
122+
"type": "string"
123+
},
116124
"source": {
117125
"$ref": "#/definitions/DockerImageSource",
118-
"description": "Source description for file assets"
126+
"description": "Source description for container assets"
119127
},
120128
"destinations": {
121-
"description": "Destinations for this file asset",
129+
"description": "Destinations for this container asset",
122130
"type": "object",
123131
"additionalProperties": {
124132
"$ref": "#/definitions/DockerImageDestination"
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
{
2-
"schemaHash": "4244f1ed6fcece9abcfb319c637fd2eb863a5deef9cc36f05f7d52377ce60012",
3-
"revision": 40
2+
"schemaHash": "ba7d47a7a023c39293e99a374af293384eaf1ccd207e515dbdc59dfb5cae4ed6",
3+
"revision": 41
44
}

packages/aws-cdk/lib/api/deployments/deployments.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -634,7 +634,7 @@ export class Deployments {
634634
const publisher = this.cachedPublisher(assetManifest, resolvedEnvironment, options.stackName);
635635
await publisher.buildEntry(asset);
636636
if (publisher.hasFailures) {
637-
throw new ToolkitError(`Failed to build asset ${asset.id}`);
637+
throw new ToolkitError(`Failed to build asset ${asset.displayName(false)}`);
638638
}
639639
}
640640

@@ -652,7 +652,7 @@ export class Deployments {
652652
const publisher = this.cachedPublisher(assetManifest, stackEnv, options.stackName);
653653
await publisher.publishEntry(asset, { allowCrossAccount: await this.allowCrossAccountAssetPublishingForEnv(options.stack) });
654654
if (publisher.hasFailures) {
655-
throw new ToolkitError(`Failed to publish asset ${asset.id}`);
655+
throw new ToolkitError(`Failed to publish asset ${asset.displayName(true)}`);
656656
}
657657
}
658658

packages/aws-cdk/lib/api/work-graph/work-graph-builder.ts

+2-2
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ export class WorkGraphBuilder {
6262
const node: AssetBuildNode = {
6363
type: 'asset-build',
6464
id: buildId,
65-
note: assetId,
65+
note: asset.displayName(false),
6666
dependencies: new Set([
6767
...this.stackArtifactIds(assetManifestArtifact.dependencies),
6868
// If we disable prebuild, then assets inherit (stack) dependencies from their parent stack
@@ -83,7 +83,7 @@ export class WorkGraphBuilder {
8383
this.graph.addNodes({
8484
type: 'asset-publish',
8585
id: publishId,
86-
note: `${asset.id}`,
86+
note: asset.displayName(true),
8787
dependencies: new Set([
8888
buildId,
8989
]),

packages/aws-cdk/test/cli/cdk-toolkit.test.ts

+34-2
Original file line numberDiff line numberDiff line change
@@ -656,6 +656,32 @@ describe('deploy', () => {
656656
});
657657
});
658658

659+
test('uses display names to reference assets', async () => {
660+
// GIVEN
661+
cloudExecutable = new MockCloudExecutable({
662+
stacks: [MockStack.MOCK_STACK_WITH_ASSET],
663+
});
664+
const toolkit = new CdkToolkit({
665+
cloudExecutable,
666+
configuration: cloudExecutable.configuration,
667+
sdkProvider: cloudExecutable.sdkProvider,
668+
deployments: new FakeCloudFormation({}),
669+
});
670+
stderrMock.mockImplementation((...x) => {
671+
console.error(...x);
672+
});
673+
674+
// WHEN
675+
await toolkit.deploy({
676+
selector: { patterns: [MockStack.MOCK_STACK_WITH_ASSET.stackName] },
677+
hotswap: HotswapMode.FULL_DEPLOYMENT,
678+
});
679+
680+
// THEN
681+
expect(stderrMock).toHaveBeenCalledWith(expect.stringContaining('Building Asset Display Name'));
682+
expect(stderrMock).toHaveBeenCalledWith(expect.stringContaining('Publishing Asset Display Name (desto)'));
683+
});
684+
659685
test('with stacks all stacks specified as wildcard', async () => {
660686
// GIVEN
661687
const toolkit = defaultToolkitSetup();
@@ -1640,10 +1666,16 @@ class MockStack {
16401666
version: Manifest.version(),
16411667
files: {
16421668
xyz: {
1669+
displayName: 'Asset Display Name',
16431670
source: {
1644-
path: path.resolve(__dirname, '..', 'LICENSE'),
1671+
path: path.resolve(__dirname, '..', '..', 'LICENSE'),
1672+
},
1673+
destinations: {
1674+
desto: {
1675+
bucketName: 'some-bucket',
1676+
objectKey: 'some-key',
1677+
},
16451678
},
1646-
destinations: {},
16471679
},
16481680
},
16491681
},

packages/cdk-assets/lib/asset-manifest.ts

+44-5
Original file line numberDiff line numberDiff line change
@@ -144,13 +144,13 @@ export class AssetManifest {
144144
}
145145

146146
function makeEntries<A, B, C>(
147-
assets: Record<string, { source: A; destinations: Record<string, B> }>,
148-
ctor: new (id: DestinationIdentifier, source: A, destination: B) => C,
147+
assets: Record<string, { source: A; displayName?: string; destinations: Record<string, B> }>,
148+
ctor: new (id: DestinationIdentifier, displayName: string | undefined, source: A, destination: B) => C,
149149
): C[] {
150150
const ret = new Array<C>();
151151
for (const [assetId, asset] of Object.entries(assets)) {
152152
for (const [destId, destination] of Object.entries(asset.destinations)) {
153-
ret.push(new ctor(new DestinationIdentifier(assetId, destId), asset.source, destination));
153+
ret.push(new ctor(new DestinationIdentifier(assetId, destId), asset.displayName, asset.source, destination));
154154
}
155155
}
156156
return ret;
@@ -183,6 +183,20 @@ export interface IManifestEntry {
183183
* Type-dependent destination data
184184
*/
185185
readonly genericDestination: unknown;
186+
187+
/**
188+
* Return a display name for this asset
189+
*
190+
* The `includeDestination` parameter controls whether or not to include the
191+
* destination ID in the display name.
192+
*
193+
* - Pass `false` if you are displaying notifications about building the
194+
* asset, or if you are describing the work of building the asset and publishing
195+
* to all destinations at the same time.
196+
* - Pass `true` if you are displaying notifications about publishing to a
197+
* specific destination.
198+
*/
199+
displayName(includeDestination: boolean): string;
186200
}
187201

188202
/**
@@ -196,6 +210,7 @@ export class FileManifestEntry implements IManifestEntry {
196210
constructor(
197211
/** Identifier for this asset */
198212
public readonly id: DestinationIdentifier,
213+
private readonly _displayName: string | undefined,
199214
/** Source of the file asset */
200215
public readonly source: FileSource,
201216
/** Destination for the file asset */
@@ -204,6 +219,14 @@ export class FileManifestEntry implements IManifestEntry {
204219
this.genericSource = source;
205220
this.genericDestination = destination;
206221
}
222+
223+
public displayName(includeDestination: boolean): string {
224+
if (includeDestination) {
225+
return this._displayName ? `${this._displayName} (${this.id.destinationId})` : `${this.id}`;
226+
} else {
227+
return this._displayName ? this._displayName : this.id.assetId;
228+
}
229+
}
207230
}
208231

209232
/**
@@ -217,6 +240,7 @@ export class DockerImageManifestEntry implements IManifestEntry {
217240
constructor(
218241
/** Identifier for this asset */
219242
public readonly id: DestinationIdentifier,
243+
private readonly _displayName: string | undefined,
220244
/** Source of the file asset */
221245
public readonly source: DockerImageSource,
222246
/** Destination for the file asset */
@@ -225,13 +249,28 @@ export class DockerImageManifestEntry implements IManifestEntry {
225249
this.genericSource = source;
226250
this.genericDestination = destination;
227251
}
252+
253+
public displayName(includeDestination: boolean): string {
254+
if (includeDestination) {
255+
return this._displayName ? `${this._displayName} (${this.id.destinationId})` : `${this.id}`;
256+
} else {
257+
return this._displayName ? this._displayName : this.id.assetId;
258+
}
259+
}
228260
}
229261

230262
/**
231263
* Identify an asset destination in an asset manifest
232264
*
233-
* When stringified, this will be a combination of the source
234-
* and destination IDs.
265+
* This class is used to identify both an asset to be built as well as a
266+
* destination where an asset will be published. However, when reasoning about
267+
* building assets the destination part can be ignored, because the same asset
268+
* being sent to multiple destinations will only need to be built once and their
269+
* assetIds are all the same.
270+
*
271+
* When stringified, this will be a combination of the source and destination
272+
* IDs; if a string representation of the source is necessary, use `id.assetId`
273+
* instead.
235274
*/
236275
export class DestinationIdentifier {
237276
/**

packages/cdk-assets/lib/publishing.ts

+6-6
Original file line numberDiff line numberDiff line change
@@ -153,7 +153,7 @@ export class AssetPublishing implements IPublishProgress {
153153
*/
154154
public async buildEntry(asset: IManifestEntry) {
155155
try {
156-
if (this.progressEvent(EventType.START, `Building ${asset.id}`)) {
156+
if (this.progressEvent(EventType.START, `Building ${asset.displayName(false)}`)) {
157157
return false;
158158
}
159159

@@ -165,7 +165,7 @@ export class AssetPublishing implements IPublishProgress {
165165
}
166166

167167
this.completedOperations++;
168-
if (this.progressEvent(EventType.SUCCESS, `Built ${asset.id}`)) {
168+
if (this.progressEvent(EventType.SUCCESS, `Built ${asset.displayName(false)}`)) {
169169
return false;
170170
}
171171
} catch (e: any) {
@@ -184,7 +184,7 @@ export class AssetPublishing implements IPublishProgress {
184184
*/
185185
public async publishEntry(asset: IManifestEntry, options: PublishOptions = {}) {
186186
try {
187-
if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) {
187+
if (this.progressEvent(EventType.START, `Publishing ${asset.displayName(true)}`)) {
188188
return false;
189189
}
190190

@@ -196,7 +196,7 @@ export class AssetPublishing implements IPublishProgress {
196196
}
197197

198198
this.completedOperations++;
199-
if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) {
199+
if (this.progressEvent(EventType.SUCCESS, `Published ${asset.displayName(true)}`)) {
200200
return false;
201201
}
202202
} catch (e: any) {
@@ -225,7 +225,7 @@ export class AssetPublishing implements IPublishProgress {
225225
*/
226226
private async publishAsset(asset: IManifestEntry, options: PublishOptions = {}) {
227227
try {
228-
if (this.progressEvent(EventType.START, `Publishing ${asset.id}`)) {
228+
if (this.progressEvent(EventType.START, `Publishing ${asset.displayName(true)}`)) {
229229
return false;
230230
}
231231

@@ -244,7 +244,7 @@ export class AssetPublishing implements IPublishProgress {
244244
}
245245

246246
this.completedOperations++;
247-
if (this.progressEvent(EventType.SUCCESS, `Published ${asset.id}`)) {
247+
if (this.progressEvent(EventType.SUCCESS, `Published ${asset.displayName(true)}`)) {
248248
return false;
249249
}
250250
} catch (e: any) {

packages/cdk-assets/test/manifest.test.ts

+18
Original file line numberDiff line numberDiff line change
@@ -60,21 +60,25 @@ test('.entries() iterates over all destinations', () => {
6060
expect(manifest.entries).toEqual([
6161
new FileManifestEntry(
6262
new DestinationIdentifier('asset1', 'dest1'),
63+
undefined,
6364
{ path: 'S1' },
6465
{ bucketName: 'D1', objectKey: 'X' },
6566
),
6667
new FileManifestEntry(
6768
new DestinationIdentifier('asset1', 'dest2'),
69+
undefined,
6870
{ path: 'S1' },
6971
{ bucketName: 'D2', objectKey: 'X' },
7072
),
7173
new DockerImageManifestEntry(
7274
new DestinationIdentifier('asset2', 'dest1'),
75+
undefined,
7376
{ directory: 'S2' },
7477
{ repositoryName: 'D3', imageTag: 'X' },
7578
),
7679
new DockerImageManifestEntry(
7780
new DestinationIdentifier('asset2', 'dest2'),
81+
undefined,
7882
{ directory: 'S2' },
7983
{ repositoryName: 'D4', imageTag: 'X' },
8084
),
@@ -136,6 +140,20 @@ test('parse *:DEST the same as :DEST', () => {
136140
expect(DestinationPattern.parse('*:a')).toEqual(DestinationPattern.parse(':a'));
137141
});
138142

143+
test.each([
144+
['Display Name', false, 'Display Name'],
145+
['Display Name', true, 'Display Name (dest2)'],
146+
[undefined, false, 'asset1'],
147+
[undefined, true, 'asset1:dest2'],
148+
])('with displayName %p and including destination %p => %p', (displayName, includeDestination, expected) => {
149+
expect(new FileManifestEntry(
150+
new DestinationIdentifier('asset1', 'dest2'),
151+
displayName,
152+
{ path: 'S1' },
153+
{ bucketName: 'D2', objectKey: 'X' },
154+
).displayName(includeDestination)).toEqual(expected);
155+
});
156+
139157
function f(obj: unknown, ...keys: string[]): any {
140158
for (const k of keys) {
141159
if (typeof obj === 'object' && obj !== null && k in obj) {

0 commit comments

Comments
 (0)