-
Notifications
You must be signed in to change notification settings - Fork 4.1k
/
Copy pathpublish.ts
151 lines (129 loc) · 4.43 KB
/
publish.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
import * as os from 'os';
import {
AssetManifest, AssetPublishing, ClientOptions, DestinationPattern, EventType, IAws,
IPublishProgress, IPublishProgressListener,
} from '../lib';
import { Account } from '../lib/aws';
import { log, LogLevel, VERSION } from './logging';
export async function publish(args: {
path: string;
assets?: string[];
profile?: string;
}) {
let manifest = AssetManifest.fromPath(args.path);
log('verbose', `Loaded manifest from ${args.path}: ${manifest.entries.length} assets found`);
if (args.assets && args.assets.length > 0) {
const selection = args.assets.map(a => DestinationPattern.parse(a));
manifest = manifest.select(selection);
log('verbose', `Applied selection: ${manifest.entries.length} assets selected.`);
}
const pub = new AssetPublishing(manifest, {
aws: new DefaultAwsClient(args.profile),
progressListener: new ConsoleProgress(),
throwOnError: false,
});
await pub.publish();
if (pub.hasFailures) {
for (const failure of pub.failures) {
// eslint-disable-next-line no-console
console.error('Failure:', failure.error.stack);
}
process.exitCode = 1;
}
}
const EVENT_TO_LEVEL: Record<EventType, LogLevel> = {
build: 'verbose',
cached: 'verbose',
check: 'verbose',
debug: 'verbose',
fail: 'error',
found: 'verbose',
start: 'info',
success: 'info',
upload: 'verbose',
};
class ConsoleProgress implements IPublishProgressListener {
public onPublishEvent(type: EventType, event: IPublishProgress): void {
log(EVENT_TO_LEVEL[type], `[${event.percentComplete}%] ${type}: ${event.message}`);
}
}
/**
* AWS client using the AWS SDK for JS with no special configuration
*/
class DefaultAwsClient implements IAws {
private readonly AWS: typeof import('aws-sdk');
private account?: Account;
constructor(profile?: string) {
// Force AWS SDK to look in ~/.aws/credentials and potentially use the configured profile.
process.env.AWS_SDK_LOAD_CONFIG = '1';
process.env.AWS_STS_REGIONAL_ENDPOINTS = 'regional';
process.env.AWS_NODEJS_CONNECTION_REUSE_ENABLED = '1';
if (profile) {
process.env.AWS_PROFILE = profile;
}
// We need to set the environment before we load this library for the first time.
// eslint-disable-next-line @typescript-eslint/no-require-imports
this.AWS = require('aws-sdk');
}
public async s3Client(options: ClientOptions) {
return new this.AWS.S3(await this.awsOptions(options));
}
public async ecrClient(options: ClientOptions) {
return new this.AWS.ECR(await this.awsOptions(options));
}
public async discoverDefaultRegion(): Promise<string> {
return this.AWS.config.region || 'us-east-1';
}
public async discoverCurrentAccount(): Promise<Account> {
if (this.account === undefined) {
const sts = new this.AWS.STS();
const response = await sts.getCallerIdentity().promise();
if (!response.Account || !response.Arn) {
log('error', `Unrecognized reponse from STS: '${JSON.stringify(response)}'`);
throw new Error('Unrecognized reponse from STS');
}
this.account = {
accountId: response.Account!,
partition: response.Arn!.split(':')[1],
};
}
return this.account;
}
private async awsOptions(options: ClientOptions) {
let credentials;
if (options.assumeRoleArn) {
credentials = await this.assumeRole(options.region, options.assumeRoleArn, options.assumeRoleExternalId);
}
return {
region: options.region,
customUserAgent: `cdk-assets/${VERSION}`,
credentials,
};
}
/**
* Explicit manual AssumeRole call
*
* Necessary since I can't seem to get the built-in support for ChainableTemporaryCredentials to work.
*
* It needs an explicit configuration of `masterCredentials`, we need to put
* a `DefaultCredentialProverChain()` in there but that is not possible.
*/
private async assumeRole(region: string | undefined, roleArn: string, externalId?: string): Promise<AWS.Credentials> {
const msg = [
`Assume ${roleArn}`,
...externalId ? [`(ExternalId ${externalId})`] : [],
];
log('verbose', msg.join(' '));
return new this.AWS.ChainableTemporaryCredentials({
params: {
RoleArn: roleArn,
ExternalId: externalId,
RoleSessionName: `cdk-assets-${os.userInfo().username}`,
},
stsConfig: {
region,
customUserAgent: `cdk-assets/${VERSION}`,
},
});
}
}