-
Notifications
You must be signed in to change notification settings - Fork 313
/
Copy pathdeploy.ts
149 lines (120 loc) · 4.49 KB
/
deploy.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
import * as fs from 'fs';
import * as path from 'path';
import * as lodash from 'lodash';
import * as tar from 'tar';
import * as minimatch from 'minimatch';
import * as S3 from 'aws-sdk/clients/s3';
import * as CloudFront from 'aws-sdk/clients/cloudfront';
import { Command, CommandLineInputs, CommandLineOptions, CommandMetadata } from '@ionic/cli-framework';
import { getCommandHeader, getDirectories, log, readStarterManifest } from '../utils';
import { BUILD_DIRECTORY, STARTERS_LIST_PATH } from '../lib/build';
import { bold, green } from 'colorette';
const s3 = new S3({ apiVersion: '2006-03-01' });
const cloudfront = new CloudFront({ apiVersion: '2017-03-25' });
const keys: string[] = [];
export class DeployCommand extends Command {
async getMetadata(): Promise<CommandMetadata> {
return {
name: 'deploy',
summary: 'Deploys the built starter templates to the CDN',
options: [
{
name: 'tag',
summary: `Deploy to a tag, such as 'next' ('latest' is production)`,
default: 'testing',
},
{
name: 'dry',
summary: 'Perform a dry run and do not upload anything',
type: Boolean,
},
],
};
}
async run(inputs: CommandLineInputs, options: CommandLineOptions) {
const tag = options['tag'] ? String(options['tag']) : 'testing';
const dry = options['dry'] ? true : false;
console.log(getCommandHeader('DEPLOY'));
console.log(`tag: ${bold(tag)}`);
const contents = await getDirectories(BUILD_DIRECTORY);
await Promise.all(
contents.map(async (dir) => {
const id = path.basename(dir);
const templateFileName = `${id}.tar.gz`;
const templateKey = `${tag === 'latest' ? '' : `${tag}/`}${templateFileName}`;
const manifest = await readStarterManifest(dir);
const tarignore = manifest && manifest.tarignore ? manifest.tarignore : undefined;
const archive = tar.create(
{
gzip: true,
cwd: dir,
portable: true,
filter: (p, _stat) => {
const filePath = path.relative(dir, path.resolve(dir, p));
if (!tarignore) {
return true;
}
return !lodash.some(tarignore.map((rule) => minimatch(filePath, rule)));
},
},
['.']
);
const archivePath = path.resolve(BUILD_DIRECTORY, templateFileName);
await writeStarter(archive, archivePath);
if (dry) {
log(id, green(`${bold('--dry')}: upload to ${bold(templateKey)}`));
} else {
log(id, `Archiving and uploading`);
// s3 needs a content length, and it's safe to know content length from a file
await upload(fs.createReadStream(archivePath), templateKey);
keys.push(templateKey);
log(id, green(`Uploaded to ${bold(templateKey)}`));
}
})
);
const startersJsonKey = `${tag === 'latest' ? '' : `${tag}/`}starters.json`;
if (dry) {
console.log(bold('starters.json'), green(`${bold('--dry')}: upload to ${bold(startersJsonKey)}`));
} else {
await upload(fs.createReadStream(STARTERS_LIST_PATH), startersJsonKey, { ContentType: 'application/json' });
keys.push(startersJsonKey);
console.log(bold('starters.json'), green(`Uploaded to ${bold(startersJsonKey)}`));
console.log(`Invalidating cache for keys:\n${keys.map((k) => ` - ${bold(k)}`).join('\n')}`);
const result = await cloudfront
.createInvalidation({
DistributionId: 'E1XZ2T0DZXJ521',
InvalidationBatch: {
CallerReference: String(new Date().getTime()),
Paths: {
Quantity: keys.length,
Items: keys.map((k) => `/${k}`),
},
},
})
.promise();
if (!result.Invalidation) {
throw new Error('No result from invalidation batch.');
}
console.log(`Invalidation ID: ${bold(result.Invalidation.Id)}`);
}
}
}
async function writeStarter(rs: NodeJS.ReadableStream, dest: string) {
return new Promise<void>((resolve, reject) => {
const ws = fs
.createWriteStream(dest)
.on('finish', () => resolve())
.on('error', (err) => reject(err));
rs.pipe(ws);
});
}
async function upload(rs: NodeJS.ReadableStream, key: string, params?: Partial<S3.PutObjectRequest>) {
await s3
.upload({
Bucket: 'ionic-starters',
Key: key,
Body: rs,
...params,
})
.promise();
}