aws-delivlib 13.5.19 → 13.6.2

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.
@@ -0,0 +1,238 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ Object.defineProperty(o, k2, { enumerable: true, get: function() { return m[k]; } });
5
+ }) : (function(o, m, k, k2) {
6
+ if (k2 === undefined) k2 = k;
7
+ o[k2] = m[k];
8
+ }));
9
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
10
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
11
+ }) : function(o, v) {
12
+ o["default"] = v;
13
+ });
14
+ var __importStar = (this && this.__importStar) || function (mod) {
15
+ if (mod && mod.__esModule) return mod;
16
+ var result = {};
17
+ if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
18
+ __setModuleDefault(result, mod);
19
+ return result;
20
+ };
21
+ var __importDefault = (this && this.__importDefault) || function (mod) {
22
+ return (mod && mod.__esModule) ? mod : { "default": mod };
23
+ };
24
+ Object.defineProperty(exports, "__esModule", { value: true });
25
+ exports.get = exports.download = exports.jsonGet = exports.PyPIArtifactIntegrity = exports.NpmArtifactIntegrity = exports.RepositoryIntegrity = exports.ArtifactIntegrity = void 0;
26
+ const child_process_1 = require("child_process");
27
+ const https = __importStar(require("https"));
28
+ const os = __importStar(require("os"));
29
+ const path = __importStar(require("path"));
30
+ // eslint-disable-next-line import/no-extraneous-dependencies
31
+ const adm_zip_1 = __importDefault(require("adm-zip"));
32
+ // eslint-disable-next-line import/no-extraneous-dependencies
33
+ const fs = __importStar(require("fs-extra"));
34
+ // eslint-disable-next-line import/no-extraneous-dependencies
35
+ const jstream = __importStar(require("JSONStream"));
36
+ // eslint-disable-next-line import/no-extraneous-dependencies
37
+ const tar = __importStar(require("tar"));
38
+ /**
39
+ * Integrity class for validating a local artifact against its published counterpart.
40
+ *
41
+ * Implementations differ based on the package manager in question.
42
+ */
43
+ class ArtifactIntegrity {
44
+ /**
45
+ * Validate a local artifact against its published counterpart.
46
+ *
47
+ * @param localArtifactDir The directory of the local artifact. Must contain exactly one file with the appropriate extenstion.
48
+ */
49
+ async validate(localArtifactDir) {
50
+ const artifactPath = this.findOne(localArtifactDir);
51
+ const name = this.constructor.name;
52
+ this.log(`Validating ${artifactPath}`);
53
+ const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'integrity-check'));
54
+ try {
55
+ const downloaded = path.join(workdir, `${name}.downloaded`);
56
+ const remote = path.join(workdir, `${name}.remote`);
57
+ const local = path.join(workdir, `${name}.local`);
58
+ // parse the artifact name into a package.
59
+ const pkg = this.parseArtifactName(path.basename(artifactPath));
60
+ fs.mkdirSync(remote);
61
+ fs.mkdirSync(local);
62
+ // download the package
63
+ this.log(`Downloading ${pkg.name}@${pkg.version} to ${downloaded}`);
64
+ await this.download(pkg, downloaded);
65
+ // extract the downlaoded package
66
+ this.log(`Extracting remote artifact from ${downloaded} to ${remote}`);
67
+ await this.extract(downloaded, remote);
68
+ // extract the local artfiact
69
+ this.log(`Extracting local artifact from ${artifactPath} to ${local}`);
70
+ await this.extract(artifactPath, local);
71
+ this.log(`Comparing ${local} <> ${remote}`);
72
+ try {
73
+ (0, child_process_1.execSync)(`diff ${local} ${remote}`, { stdio: ['ignore', 'inherit', 'inherit'] });
74
+ }
75
+ catch (error) {
76
+ throw new Error(`${name} validation failed`);
77
+ }
78
+ this.log('Success');
79
+ }
80
+ finally {
81
+ fs.removeSync(workdir);
82
+ }
83
+ }
84
+ log(message) {
85
+ console.log(`${this.constructor.name} | ${message} `);
86
+ }
87
+ findOne(dir) {
88
+ const files = fs.readdirSync(dir).filter(f => f.endsWith(this.ext));
89
+ if (files.length === 0) {
90
+ throw new Error(`No files found in ${dir} with extension ${this.ext}`);
91
+ }
92
+ const [first, ...rest] = files;
93
+ if (rest.length > 0) {
94
+ throw new Error(`Multiple files found in ${dir} with extension ${this.ext}: ${first}, ${rest.join(', ')}`);
95
+ }
96
+ return path.join(dir, first);
97
+ }
98
+ }
99
+ exports.ArtifactIntegrity = ArtifactIntegrity;
100
+ /**
101
+ * Integrity class for validating the artifacts produced by this repository against their published counterparts.
102
+ */
103
+ class RepositoryIntegrity {
104
+ constructor(props) {
105
+ this.props = props;
106
+ }
107
+ /**
108
+ * Validate the artifacts of this repo against its published counterpart.
109
+ */
110
+ async validate() {
111
+ var _a;
112
+ // note that run 'release' by default to preserve the version number.
113
+ // this won't do a bump since the commit we are on is already tagged.
114
+ const artifacts = this.props.repository.pack((_a = this.props.packCommand) !== null && _a !== void 0 ? _a : 'npx projen release');
115
+ let integrity = undefined;
116
+ for (const artifact of artifacts) {
117
+ switch (artifact.lang) {
118
+ case 'js':
119
+ integrity = new NpmArtifactIntegrity();
120
+ break;
121
+ case 'python':
122
+ integrity = new PyPIArtifactIntegrity();
123
+ break;
124
+ case 'java':
125
+ case 'dotnet':
126
+ case 'go':
127
+ // we don't have integrity checks for these
128
+ // artifacts yet.
129
+ break;
130
+ default:
131
+ throw new Error(`Unsupported artifact language: ${artifact.lang}`);
132
+ }
133
+ if (integrity) {
134
+ await integrity.validate(artifact.directory);
135
+ }
136
+ }
137
+ console.log('Validation done');
138
+ }
139
+ }
140
+ exports.RepositoryIntegrity = RepositoryIntegrity;
141
+ /**
142
+ * NpmIntegiry is able to perform integiry checks against packages stored on npmjs.com
143
+ */
144
+ class NpmArtifactIntegrity extends ArtifactIntegrity {
145
+ constructor() {
146
+ super(...arguments);
147
+ this.ext = 'tgz';
148
+ }
149
+ async download(pkg, target) {
150
+ const tarballUrl = await jsonGet(`https://registry.npmjs.org/${encodeURIComponent(pkg.name)}/${encodeURIComponent(pkg.version)}`, ['dist', 'tarball']);
151
+ await download(tarballUrl, target);
152
+ }
153
+ async extract(file, targetDir) {
154
+ return tar.x({ cwd: targetDir, file: file, strip: 1 });
155
+ }
156
+ parseArtifactName(artifactName) {
157
+ // cdk8s@1.0.0-beta.59.jsii.tgz
158
+ const jsiiArtifact = /(.*)@(.*)\.jsii./;
159
+ // npm artifact: cdk8s-cli-1.0.0-beta59.tgz
160
+ // yarn artifact: cdk8s-cli-v1.0.0-beta59.tgz (add a 'v' before the version)
161
+ const npmOrYarnArtifact = /(.*)-v?(\d.*).(tgz|tar.gz)/;
162
+ const regex = artifactName.includes('.jsii.') ? jsiiArtifact : npmOrYarnArtifact;
163
+ const match = artifactName.match(regex);
164
+ if (!match) {
165
+ throw new Error(`Unable to parse artifact: ${artifactName}`);
166
+ }
167
+ return { name: match[1], version: match[2] };
168
+ }
169
+ }
170
+ exports.NpmArtifactIntegrity = NpmArtifactIntegrity;
171
+ /**
172
+ * PyPIIntegiry is able to perform integiry checks against packages stored on pypi.org
173
+ */
174
+ class PyPIArtifactIntegrity extends ArtifactIntegrity {
175
+ constructor() {
176
+ super(...arguments);
177
+ this.ext = 'whl';
178
+ }
179
+ async download(pkg, target) {
180
+ const files = await jsonGet(`https://pypi.org/pypi/${encodeURIComponent(pkg.name)}/json`, ['releases', pkg.version]);
181
+ const wheels = files.filter((f) => f.url.endsWith('whl')).map((f) => f.url);
182
+ if (wheels.length === 0) {
183
+ throw new Error(`No wheels found for package ${pkg.name}-${pkg.version}`);
184
+ }
185
+ if (wheels.length > 1) {
186
+ throw new Error(`Multiple wheels found for package ${pkg.name}-${pkg.version}: ${wheels.join(',')}`);
187
+ }
188
+ await download(wheels[0], target);
189
+ }
190
+ async extract(artifact, target) {
191
+ const zip = new adm_zip_1.default(artifact);
192
+ return zip.extractAllTo(target);
193
+ }
194
+ parseArtifactName(artifactName) {
195
+ // cdk8s-1.0.0b63-py3-none-any.whl
196
+ const regex = /(.*)-v?(\d.*)-py.*.whl/;
197
+ const match = artifactName.match(regex);
198
+ if (!match) {
199
+ throw new Error(`Unable to parse artifact: ${artifactName}`);
200
+ }
201
+ return { name: match[1], version: match[2] };
202
+ }
203
+ }
204
+ exports.PyPIArtifactIntegrity = PyPIArtifactIntegrity;
205
+ function jsonGet(url, jsonPath) {
206
+ return get(url, (res, ok, ko) => {
207
+ const json = jstream.parse(jsonPath);
208
+ json.once('data', ok);
209
+ json.once('error', ko);
210
+ res.pipe(json, { end: true });
211
+ }, { headers: { 'Accept': 'application/json', 'Accept-Encoding': 'identity' } });
212
+ }
213
+ exports.jsonGet = jsonGet;
214
+ async function download(url, targetFile) {
215
+ return get(url, (res, ok, ko) => {
216
+ const file = fs.createWriteStream(targetFile);
217
+ file.on('finish', ok);
218
+ file.on('error', ko);
219
+ res.pipe(file, { end: true });
220
+ });
221
+ }
222
+ exports.download = download;
223
+ async function get(url, handler, options = {}) {
224
+ return new Promise((ok, ko) => {
225
+ const request = https.get(url, options, (res) => {
226
+ if (res.statusCode !== 200) {
227
+ const error = new Error(`GET ${url} - HTTP ${res.statusCode} (${res.statusMessage})`);
228
+ Error.captureStackTrace(error);
229
+ return ko(error);
230
+ }
231
+ res.once('error', ko);
232
+ handler(res, ok, ko);
233
+ });
234
+ request.on('error', ko);
235
+ });
236
+ }
237
+ exports.get = get;
238
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZWdyaXR5LmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiaW50ZWdyaXR5LnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiI7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7Ozs7QUFBQSxpREFBeUM7QUFFekMsNkNBQStCO0FBQy9CLHVDQUF5QjtBQUN6QiwyQ0FBNkI7QUFDN0IsNkRBQTZEO0FBQzdELHNEQUE2QjtBQUM3Qiw2REFBNkQ7QUFDN0QsNkNBQStCO0FBQy9CLDZEQUE2RDtBQUM3RCxvREFBc0M7QUFDdEMsNkRBQTZEO0FBQzdELHlDQUEyQjtBQW9CM0I7Ozs7R0FJRztBQUNILE1BQXNCLGlCQUFpQjtJQStCckM7Ozs7T0FJRztJQUNJLEtBQUssQ0FBQyxRQUFRLENBQUMsZ0JBQXdCO1FBRTVDLE1BQU0sWUFBWSxHQUFHLElBQUksQ0FBQyxPQUFPLENBQUMsZ0JBQWdCLENBQUMsQ0FBQztRQUNwRCxNQUFNLElBQUksR0FBRyxJQUFJLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQztRQUVuQyxJQUFJLENBQUMsR0FBRyxDQUFDLGNBQWMsWUFBWSxFQUFFLENBQUMsQ0FBQztRQUN2QyxNQUFNLE9BQU8sR0FBRyxFQUFFLENBQUMsV0FBVyxDQUFDLElBQUksQ0FBQyxJQUFJLENBQUMsRUFBRSxDQUFDLE1BQU0sRUFBRSxFQUFFLGlCQUFpQixDQUFDLENBQUMsQ0FBQztRQUUxRSxJQUFJO1lBQ0YsTUFBTSxVQUFVLEdBQUcsSUFBSSxDQUFDLElBQUksQ0FBQyxPQUFPLEVBQUUsR0FBRyxJQUFJLGFBQWEsQ0FBQyxDQUFDO1lBQzVELE1BQU0sTUFBTSxHQUFHLElBQUksQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEdBQUcsSUFBSSxTQUFTLENBQUMsQ0FBQztZQUNwRCxNQUFNLEtBQUssR0FBRyxJQUFJLENBQUMsSUFBSSxDQUFDLE9BQU8sRUFBRSxHQUFHLElBQUksUUFBUSxDQUFDLENBQUM7WUFFbEQsMENBQTBDO1lBQzFDLE1BQU0sR0FBRyxHQUFH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QztRQUN0QixJQUFJLENBQUMsRUFBRSxDQUFDLE9BQU8sRUFBRSxFQUFFLENBQUMsQ0FBQztRQUNyQixHQUFHLENBQUMsSUFBSSxDQUFDLElBQUksRUFBRSxFQUFFLEdBQUcsRUFBRSxJQUFJLEVBQUUsQ0FBQyxDQUFDO0lBQ2hDLENBQUMsQ0FBQyxDQUFDO0FBQ0wsQ0FBQztBQVBELDRCQU9DO0FBRU0sS0FBSyxVQUFVLEdBQUcsQ0FDdkIsR0FBVyxFQUNYLE9BQStGLEVBQy9GLFVBQTBCLEVBQUU7SUFFNUIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxDQUFDLEVBQUUsRUFBRSxFQUFFLEVBQUUsRUFBRTtRQUM1QixNQUFNLE9BQU8sR0FBRyxLQUFLLENBQUMsR0FBRyxDQUFDLEdBQUcsRUFBRSxPQUFPLEVBQUUsQ0FBQyxHQUFvQixFQUFFLEVBQUU7WUFDL0QsSUFBSSxHQUFHLENBQUMsVUFBVSxLQUFLLEdBQUcsRUFBRTtnQkFDMUIsTUFBTSxLQUFLLEdBQUcsSUFBSSxLQUFLLENBQUMsT0FBTyxHQUFHLFdBQVcsR0FBRyxDQUFDLFVBQVUsS0FBSyxHQUFHLENBQUMsYUFBYSxHQUFHLENBQUMsQ0FBQztnQkFDdEYsS0FBSyxDQUFDLGlCQUFpQixDQUFDLEtBQUssQ0FBQyxDQUFDO2dCQUMvQixPQUFPLEVBQUUsQ0FBQyxLQUFLLENBQUMsQ0FBQzthQUNsQjtZQUNELEdBQUcsQ0FBQyxJQUFJLENBQUMsT0FBTyxFQUFFLEVBQUUsQ0FBQyxDQUFDO1lBQ3RCLE9BQU8sQ0FBQyxHQUFHLEVBQUUsRUFBRSxFQUFFLEVBQUUsQ0FBQyxDQUFDO1FBQ3ZCLENBQUMsQ0FBQyxDQUFDO1FBQ0gsT0FBTyxDQUFDLEVBQUUsQ0FBQyxPQUFPLEVBQUUsRUFBRSxDQUFDLENBQUM7SUFDMUIsQ0FBQyxDQUFDLENBQUM7QUFFTCxDQUFDO0FBbEJELGtCQWtCQyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IGV4ZWNTeW5jIH0gZnJvbSAnY2hpbGRfcHJvY2Vzcyc7XG5pbXBvcnQgdHlwZSB7IFJlcXVlc3RPcHRpb25zLCBJbmNvbWluZ01lc3NhZ2UgfSBmcm9tICdodHRwJztcbmltcG9ydCAqIGFzIGh0dHBzIGZyb20gJ2h0dHBzJztcbmltcG9ydCAqIGFzIG9zIGZyb20gJ29zJztcbmltcG9ydCAqIGFzIHBhdGggZnJvbSAncGF0aCc7XG4vLyBlc2xpbnQtZGlzYWJsZS1uZXh0LWxpbmUgaW1wb3J0L25vLWV4dHJhbmVvdXMtZGVwZW5kZW5jaWVzXG5pbXBvcnQgQWRtWmlwIGZyb20gJ2FkbS16aXAnO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuaW1wb3J0ICogYXMgZnMgZnJvbSAnZnMtZXh0cmEnO1xuLy8gZXNsaW50LWRpc2FibGUtbmV4dC1saW5lIGltcG9ydC9uby1leHRyYW5lb3VzLWRlcGVuZGVuY2llc1xuaW1wb3J0ICogYXMganN0cmVhbSBmcm9tICdKU09OU3RyZWFtJztcbi8vIGVzbGludC1kaXNhYmxlLW5leHQtbGluZSBpbXBvcnQvbm8tZXh0cmFuZW91cy1kZXBlbmRlbmNpZXNcbmltcG9ydCAqIGFzIHRhciBmcm9tICd0YXInO1xuaW1wb3J0IHsgUmVwb3NpdG9yeSB9IGZyb20gJy4vcmVwb3NpdG9yeSc7XG5cblxuLyoqXG4gKiBQdWJsaXNoZWQgcGFja2FnZS5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBQdWJsaXNoZWRQYWNrYWdlIHtcblxuICAvKipcbiAgICogTmFtZSBvZiB0aGUgcGFja2FnZSBhcyBzdG9yZWQgaW4gdGhlIHBhY2thZ2UgbWFuYWdlci5cbiAgICovXG4gIHJlYWRvbmx5IG5hbWU6IHN0cmluZztcblxuICAvKipcbiAgICogVmVyc2lvbiBvZiB0aGUgcGFja2FnZSBhcyBzdG9yZWQgaW4gdGhlIHBhY2thZ2UgbWFuYWdlci5cbiAgICovXG4gIHJlYWRvbmx5IHZlcnNpb246IHN0cmluZztcbn1cblxuLyoqXG4gKiBJbnRlZ3JpdHkgY2xhc3MgZm9yIHZhbGlkYXRpbmcgYSBsb2NhbCBhcnRpZmFjdCBhZ2FpbnN0IGl0cyBwdWJsaXNoZWQgY291bnRlcnBhcnQuXG4gKlxuICogSW1wbGVtZW50YXRpb25zIGRpZmZlciBiYXNlZCBvbiB0aGUgcGFja2FnZSBtYW5hZ2VyIGluIHF1ZXN0aW9uLlxuICovXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgQXJ0aWZhY3RJbnRlZ3JpdHkge1xuXG4gIC8qKlxuICAgKiBUaGUgZmlsZSBleHRlbnN0aW9uIG9mIGFydGlmYWN0cyBwcm9kdWNlZCBmb3IgdGhpcyBjaGVjay4gKGUuZyAnd2hsJylcbiAgICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCByZWFkb25seSBleHQ6IHN0cmluZztcblxuICAvKipcbiAgICogRG93bmxvYWQgYSBwYWNrYWdlIHRvIHRoZSB0YXJnZXQgZmlsZS5cbiAgICpcbiAgICogQHBhcmFtIHBrZyBUaGUgcGFja2FnZSB0byBkb3dubG9hZC5cbiAgICogQHBhcmFtIHRhcmdldEZpbGUgVGhlIGZpbGUgcGF0aCB0byBkb3dubG9hZCB0aGUgcGFja2FnZSB0by5cbiAgICovXG4gIHByb3RlY3RlZCBhYnN0cmFjdCBkb3dubG9hZChwa2c6IFB1Ymxpc2hlZFBhY2thZ2UsIHRhcmdldEZpbGU6IHN0cmluZyk6IFByb21pc2U8dm9pZD47XG5cbiAgLyoqXG4gICAqIEV4dHJhY3QgdGhlIGFydGlmYWN0IGludG8gdGhlIHRhcmdldCBkaXJlY3RvcnkuXG4gICAqXG4gICAqIEBwYXJhbSBhcnRpZmFjdCBQYXRoIHRvIGFuIGFydGlmYWN0IGZpbGUuXG4gICAqIEBwYXJhbSB0YXJnZXREaXIgVGhlIGRpcmVjdG9yeSB0byBleHRyYWN0IHRvLiBJdCB3aWxsIGV4aXN0IGJ5IHRoZSB0aW1lIHRoaXMgbWV0aG9kIGlzIGludm9rZWQuXG4gICAqL1xuICBwcm90ZWN0ZWQgYWJzdHJhY3QgZXh0cmFjdChhcnRpZmFjdDogc3RyaW5nLCB0YXJnZXREaXI6IHN0cmluZyk6IFByb21pc2U8dm9pZD47XG5cbiAgLyoqXG4gICAqIFBhcnNlIGEgbG9jYWwgYXJ0aWZhY3QgZmlsZSBuYW1lIGludG8gYSBzdHJ1Y3R1cmVkIHBhY2thZ2UuXG4gICAqXG4gICAqIEBwYXJhbSBhcnRpZmFjdE5hbWUgQmFzZSBuYW1lIG9mIHRoZSBsb2NhbCBhcnRpZmFjdCBmaWxlLlxuICAgKiBAcmV0dXJucyBUaGUgcGFja2FnZSB0aGlzIGFydGlmYWN0IGNvcnJlbGF0ZXMgdG8uXG4gICAqL1xuICBwcm90ZWN0ZWQgYWJzdHJhY3QgcGFyc2VBcnRpZmFjdE5hbWUoYXJ0aWZhY3ROYW1lOiBzdHJpbmcpOiBQdWJsaXNoZWRQYWNrYWdlO1xuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSBhIGxvY2FsIGFydGlmYWN0IGFnYWluc3QgaXRzIHB1Ymxpc2hlZCBjb3VudGVycGFydC5cbiAgICpcbiAgICogQHBhcmFtIGxvY2FsQXJ0aWZhY3REaXIgVGhlIGRpcmVjdG9yeSBvZiB0aGUgbG9jYWwgYXJ0aWZhY3QuIE11c3QgY29udGFpbiBleGFjdGx5IG9uZSBmaWxlIHdpdGggdGhlIGFwcHJvcHJpYXRlIGV4dGVuc3Rpb24uXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdmFsaWRhdGUobG9jYWxBcnRpZmFjdERpcjogc3RyaW5nKSB7XG5cbiAgICBjb25zdCBhcnRpZmFjdFBhdGggPSB0aGlzLmZpbmRPbmUobG9jYWxBcnRpZmFjdERpcik7XG4gICAgY29uc3QgbmFtZSA9IHRoaXMuY29uc3RydWN0b3IubmFtZTtcblxuICAgIHRoaXMubG9nKGBWYWxpZGF0aW5nICR7YXJ0aWZhY3RQYXRofWApO1xuICAgIGNvbnN0IHdvcmtkaXIgPSBmcy5ta2R0ZW1wU3luYyhwYXRoLmpvaW4ob3MudG1wZGlyKCksICdpbnRlZ3JpdHktY2hlY2snKSk7XG5cbiAgICB0cnkge1xuICAgICAgY29uc3QgZG93bmxvYWRlZCA9IHBhdGguam9pbih3b3JrZGlyLCBgJHtuYW1lfS5kb3dubG9hZGVkYCk7XG4gICAgICBjb25zdCByZW1vdGUgPSBwYXRoLmpvaW4od29ya2RpciwgYCR7bmFtZX0ucmVtb3RlYCk7XG4gICAgICBjb25zdCBsb2NhbCA9IHBhdGguam9pbih3b3JrZGlyLCBgJHtuYW1lfS5sb2NhbGApO1xuXG4gICAgICAvLyBwYXJzZSB0aGUgYXJ0aWZhY3QgbmFtZSBpbnRvIGEgcGFja2FnZS5cbiAgICAgIGNvbnN0IHBrZyA9IHRoaXMucGFyc2VBcnRpZmFjdE5hbWUocGF0aC5iYXNlbmFtZShhcnRpZmFjdFBhdGgpKTtcblxuICAgICAgZnMubWtkaXJTeW5jKHJlbW90ZSk7XG4gICAgICBmcy5ta2RpclN5bmMobG9jYWwpO1xuXG4gICAgICAvLyBkb3dubG9hZCB0aGUgcGFja2FnZVxuICAgICAgdGhpcy5sb2coYERvd25sb2FkaW5nICR7cGtnLm5hbWV9QCR7cGtnLnZlcnNpb259IHRvICR7ZG93bmxvYWRlZH1gKTtcbiAgICAgIGF3YWl0IHRoaXMuZG93bmxvYWQocGtnLCBkb3dubG9hZGVkKTtcblxuICAgICAgLy8gZXh0cmFjdCB0aGUgZG93bmxhb2RlZCBwYWNrYWdlXG4gICAgICB0aGlzLmxvZyhgRXh0cmFjdGluZyByZW1vdGUgYXJ0aWZhY3QgZnJvbSAke2Rvd25sb2FkZWR9IHRvICR7cmVtb3RlfWApO1xuICAgICAgYXdhaXQgdGhpcy5leHRyYWN0KGRvd25sb2FkZWQsIHJlbW90ZSk7XG5cbiAgICAgIC8vIGV4dHJhY3QgdGhlIGxvY2FsIGFydGZpYWN0XG4gICAgICB0aGlzLmxvZyhgRXh0cmFjdGluZyBsb2NhbCBhcnRpZmFjdCBmcm9tICR7YXJ0aWZhY3RQYXRofSB0byAke2xvY2FsfWApO1xuICAgICAgYXdhaXQgdGhpcy5leHRyYWN0KGFydGlmYWN0UGF0aCwgbG9jYWwpO1xuXG4gICAgICB0aGlzLmxvZyhgQ29tcGFyaW5nICR7bG9jYWx9IDw+ICR7cmVtb3RlfWApO1xuICAgICAgdHJ5IHtcbiAgICAgICAgZXhlY1N5bmMoYGRpZmYgJHtsb2NhbH0gJHtyZW1vdGV9YCwgeyBzdGRpbzogWydpZ25vcmUnLCAnaW5oZXJpdCcsICdpbmhlcml0J10gfSk7XG4gICAgICB9IGNhdGNoIChlcnJvcikge1xuICAgICAgICB0aHJvdyBuZXcgRXJyb3IoYCR7bmFtZX0gdmFsaWRhdGlvbiBmYWlsZWRgKTtcbiAgICAgIH1cbiAgICAgIHRoaXMubG9nKCdTdWNjZXNzJyk7XG5cbiAgICB9IGZpbmFsbHkge1xuICAgICAgZnMucmVtb3ZlU3luYyh3b3JrZGlyKTtcbiAgICB9XG5cbiAgfVxuXG4gIHByb3RlY3RlZCBsb2cobWVzc2FnZTogc3RyaW5nKSB7XG4gICAgY29uc29sZS5sb2coYCR7dGhpcy5jb25zdHJ1Y3Rvci5uYW1lfSB8ICR7bWVzc2FnZX0gYCk7XG4gIH1cblxuICBwcml2YXRlIGZpbmRPbmUoZGlyOiBzdHJpbmcpOiBzdHJpbmcge1xuICAgIGNvbnN0IGZpbGVzID0gZnMucmVhZGRpclN5bmMoZGlyKS5maWx0ZXIoZiA9PiBmLmVuZHNXaXRoKHRoaXMuZXh0KSk7XG4gICAgaWYgKGZpbGVzLmxlbmd0aCA9PT0gMCkge1xuICAgICAgdGhyb3cgbmV3IEVycm9yKGBObyBmaWxlcyBmb3VuZCBpbiAke2Rpcn0gd2l0aCBleHRlbnNpb24gJHt0aGlzLmV4dH1gKTtcbiAgICB9XG4gICAgY29uc3QgW2ZpcnN0LCAuLi5yZXN0XSA9IGZpbGVzO1xuICAgIGlmIChyZXN0Lmxlbmd0aCA+IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTXVsdGlwbGUgZmlsZXMgZm91bmQgaW4gJHtkaXJ9IHdpdGggZXh0ZW5zaW9uICR7dGhpcy5leHR9OiAke2ZpcnN0fSwgJHtyZXN0LmpvaW4oJywgJyl9YCk7XG4gICAgfVxuICAgIHJldHVybiBwYXRoLmpvaW4oZGlyLCBmaXJzdCk7XG4gIH1cblxufVxuXG4vKipcbiAqIFByb3BlcnRpZXMgZm9yIGBSZXBvc2l0b3J5SW50ZWdyaXR5YC5cbiAqL1xuZXhwb3J0IGludGVyZmFjZSBSZXBvc2l0b3J5SW50ZWdyaXR5UHJvcHMge1xuICAvKipcbiAgICogUmVwb3NpdG9yeSB0byB2YWxpZGF0ZS5cbiAgICovXG4gIHJlYWRvbmx5IHJlcG9zaXRvcnk6IFJlcG9zaXRvcnk7XG5cbiAgLyoqXG4gICAqIFRoZSBjb21tYW5kIHRoYXQgcHJvZHVjZXMgdGhlIGxvY2FsIGFydGlmYWN0cy5cbiAgICpcbiAgICogQGRlZmF1bHQgJ25weCBwcm9qZW4gcmVsZWFzZSdcbiAgICovXG4gIHJlYWRvbmx5IHBhY2tDb21tYW5kPzogc3RyaW5nO1xufVxuXG4vKipcbiAqIEludGVncml0eSBjbGFzcyBmb3IgdmFsaWRhdGluZyB0aGUgYXJ0aWZhY3RzIHByb2R1Y2VkIGJ5IHRoaXMgcmVwb3NpdG9yeSBhZ2FpbnN0IHRoZWlyIHB1Ymxpc2hlZCBjb3VudGVycGFydHMuXG4gKi9cbmV4cG9ydCBjbGFzcyBSZXBvc2l0b3J5SW50ZWdyaXR5IHtcblxuICBwdWJsaWMgY29uc3RydWN0b3IocHJpdmF0ZSByZWFkb25seSBwcm9wczogUmVwb3NpdG9yeUludGVncml0eVByb3BzKSB7fVxuXG4gIC8qKlxuICAgKiBWYWxpZGF0ZSB0aGUgYXJ0aWZhY3RzIG9mIHRoaXMgcmVwbyBhZ2FpbnN0IGl0cyBwdWJsaXNoZWQgY291bnRlcnBhcnQuXG4gICAqL1xuICBwdWJsaWMgYXN5bmMgdmFsaWRhdGUoKSB7XG5cbiAgICAvLyBub3RlIHRoYXQgcnVuICdyZWxlYXNlJyBieSBkZWZhdWx0IHRvIHByZXNlcnZlIHRoZSB2ZXJzaW9uIG51bWJlci5cbiAgICAvLyB0aGlzIHdvbid0IGRvIGEgYnVtcCBzaW5jZSB0aGUgY29tbWl0IHdlIGFyZSBvbiBpcyBhbHJlYWR5IHRhZ2dlZC5cbiAgICBjb25zdCBhcnRpZmFjdHMgPSB0aGlzLnByb3BzLnJlcG9zaXRvcnkucGFjayh0aGlzLnByb3BzLnBhY2tDb21tYW5kID8/ICducHggcHJvamVuIHJlbGVhc2UnKTtcblxuICAgIGxldCBpbnRlZ3JpdHkgPSB1bmRlZmluZWQ7XG4gICAgZm9yIChjb25zdCBhcnRpZmFjdCBvZiBhcnRpZmFjdHMpIHtcbiAgICAgIHN3aXRjaCAoYXJ0aWZhY3QubGFuZykge1xuICAgICAgICBjYXNlICdqcyc6XG4gICAgICAgICAgaW50ZWdyaXR5ID0gbmV3IE5wbUFydGlmYWN0SW50ZWdyaXR5KCk7XG4gICAgICAgICAgYnJlYWs7XG4gICAgICAgIGNhc2UgJ3B5dGhvbic6XG4gICAgICAgICAgaW50ZWdyaXR5ID0gbmV3IFB5UElBcnRpZmFjdEludGVncml0eSgpO1xuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBjYXNlICdqYXZhJzpcbiAgICAgICAgY2FzZSAnZG90bmV0JzpcbiAgICAgICAgY2FzZSAnZ28nOlxuICAgICAgICAgIC8vIHdlIGRvbid0IGhhdmUgaW50ZWdyaXR5IGNoZWNrcyBmb3IgdGhlc2VcbiAgICAgICAgICAvLyBhcnRpZmFjdHMgeWV0LlxuICAgICAgICAgIGJyZWFrO1xuICAgICAgICBkZWZhdWx0OlxuICAgICAgICAgIHRocm93IG5ldyBFcnJvcihgVW5zdXBwb3J0ZWQgYXJ0aWZhY3QgbGFuZ3VhZ2U6ICR7YXJ0aWZhY3QubGFuZ31gKTtcbiAgICAgIH1cbiAgICAgIGlmIChpbnRlZ3JpdHkpIHtcbiAgICAgICAgYXdhaXQgaW50ZWdyaXR5LnZhbGlkYXRlKGFydGlmYWN0LmRpcmVjdG9yeSk7XG4gICAgICB9XG4gICAgfVxuICAgIGNvbnNvbGUubG9nKCdWYWxpZGF0aW9uIGRvbmUnKTtcbiAgfVxuXG59XG5cbi8qKlxuICogTnBtSW50ZWdpcnkgaXMgYWJsZSB0byBwZXJmb3JtIGludGVnaXJ5IGNoZWNrcyBhZ2FpbnN0IHBhY2thZ2VzIHN0b3JlZCBvbiBucG1qcy5jb21cbiAqL1xuZXhwb3J0IGNsYXNzIE5wbUFydGlmYWN0SW50ZWdyaXR5IGV4dGVuZHMgQXJ0aWZhY3RJbnRlZ3JpdHkge1xuXG4gIHByb3RlY3RlZCByZWFkb25seSBleHQgPSAndGd6JztcblxuICBwcm90ZWN0ZWQgYXN5bmMgZG93bmxvYWQocGtnOiBQdWJsaXNoZWRQYWNrYWdlLCB0YXJnZXQ6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIGNvbnN0IHRhcmJhbGxVcmwgPSBhd2FpdCBqc29uR2V0KGBodHRwczovL3JlZ2lzdHJ5Lm5wbWpzLm9yZy8ke2VuY29kZVVSSUNvbXBvbmVudChwa2cubmFtZSl9LyR7ZW5jb2RlVVJJQ29tcG9uZW50KHBrZy52ZXJzaW9uKX1gLCBbJ2Rpc3QnLCAndGFyYmFsbCddKTtcbiAgICBhd2FpdCBkb3dubG9hZCh0YXJiYWxsVXJsLCB0YXJnZXQpO1xuICB9XG5cbiAgcHVibGljIGFzeW5jIGV4dHJhY3QoZmlsZTogc3RyaW5nLCB0YXJnZXREaXI6IHN0cmluZyk6IFByb21pc2U8dm9pZD4ge1xuICAgIHJldHVybiB0YXIueCh7IGN3ZDogdGFyZ2V0RGlyLCBmaWxlOiBmaWxlLCBzdHJpcDogMSB9KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBwYXJzZUFydGlmYWN0TmFtZShhcnRpZmFjdE5hbWU6IHN0cmluZyk6IFB1Ymxpc2hlZFBhY2thZ2Uge1xuXG4gICAgLy8gY2RrOHNAMS4wLjAtYmV0YS41OS5qc2lpLnRnelxuICAgIGNvbnN0IGpzaWlBcnRpZmFjdCA9IC8oLiopQCguKilcXC5qc2lpLi87XG5cbiAgICAvLyBucG0gYXJ0aWZhY3Q6IGNkazhzLWNsaS0xLjAuMC1iZXRhNTkudGd6XG4gICAgLy8geWFybiBhcnRpZmFjdDogY2RrOHMtY2xpLXYxLjAuMC1iZXRhNTkudGd6IChhZGQgYSAndicgYmVmb3JlIHRoZSB2ZXJzaW9uKVxuICAgIGNvbnN0IG5wbU9yWWFybkFydGlmYWN0ID0gLyguKiktdj8oXFxkLiopLih0Z3p8dGFyLmd6KS87XG5cbiAgICBjb25zdCByZWdleCA9IGFydGlmYWN0TmFtZS5pbmNsdWRlcygnLmpzaWkuJykgPyBqc2lpQXJ0aWZhY3QgOiBucG1Pcllhcm5BcnRpZmFjdDtcblxuICAgIGNvbnN0IG1hdGNoID0gYXJ0aWZhY3ROYW1lLm1hdGNoKHJlZ2V4KTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFVuYWJsZSB0byBwYXJzZSBhcnRpZmFjdDogJHthcnRpZmFjdE5hbWV9YCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgbmFtZTogbWF0Y2hbMV0sIHZlcnNpb246IG1hdGNoWzJdIH07XG5cbiAgfVxuXG59XG5cbi8qKlxuICogUHlQSUludGVnaXJ5IGlzIGFibGUgdG8gcGVyZm9ybSBpbnRlZ2lyeSBjaGVja3MgYWdhaW5zdCBwYWNrYWdlcyBzdG9yZWQgb24gcHlwaS5vcmdcbiAqL1xuZXhwb3J0IGNsYXNzIFB5UElBcnRpZmFjdEludGVncml0eSBleHRlbmRzIEFydGlmYWN0SW50ZWdyaXR5IHtcblxuICBwcm90ZWN0ZWQgcmVhZG9ubHkgZXh0ID0gJ3dobCc7XG5cbiAgcHJvdGVjdGVkIGFzeW5jIGRvd25sb2FkKHBrZzogUHVibGlzaGVkUGFja2FnZSwgdGFyZ2V0OiBzdHJpbmcpOiBQcm9taXNlPHZvaWQ+IHtcblxuICAgIGNvbnN0IGZpbGVzID0gYXdhaXQganNvbkdldChgaHR0cHM6Ly9weXBpLm9yZy9weXBpLyR7ZW5jb2RlVVJJQ29tcG9uZW50KHBrZy5uYW1lKX0vanNvbmAsIFsncmVsZWFzZXMnLCBwa2cudmVyc2lvbl0pO1xuICAgIGNvbnN0IHdoZWVsczogc3RyaW5nW10gPSBmaWxlcy5maWx0ZXIoKGY6IGFueSkgPT4gZi51cmwuZW5kc1dpdGgoJ3dobCcpKS5tYXAoKGY6IGFueSkgPT4gZi51cmwpO1xuXG4gICAgaWYgKHdoZWVscy5sZW5ndGggPT09IDApIHtcbiAgICAgIHRocm93IG5ldyBFcnJvcihgTm8gd2hlZWxzIGZvdW5kIGZvciBwYWNrYWdlICR7cGtnLm5hbWV9LSR7cGtnLnZlcnNpb259YCk7XG4gICAgfVxuXG4gICAgaWYgKHdoZWVscy5sZW5ndGggPiAxKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYE11bHRpcGxlIHdoZWVscyBmb3VuZCBmb3IgcGFja2FnZSAke3BrZy5uYW1lfS0ke3BrZy52ZXJzaW9ufTogJHt3aGVlbHMuam9pbignLCcpfWApO1xuICAgIH1cblxuICAgIGF3YWl0IGRvd25sb2FkKHdoZWVsc1swXSwgdGFyZ2V0KTtcbiAgfVxuXG4gIHB1YmxpYyBhc3luYyBleHRyYWN0KGFydGlmYWN0OiBzdHJpbmcsIHRhcmdldDogc3RyaW5nKTogUHJvbWlzZTx2b2lkPiB7XG4gICAgY29uc3QgemlwID0gbmV3IEFkbVppcChhcnRpZmFjdCk7XG4gICAgcmV0dXJuIHppcC5leHRyYWN0QWxsVG8odGFyZ2V0KTtcbiAgfVxuXG4gIHByb3RlY3RlZCBwYXJzZUFydGlmYWN0TmFtZShhcnRpZmFjdE5hbWU6IHN0cmluZyk6IFB1Ymxpc2hlZFBhY2thZ2Uge1xuXG4gICAgLy8gY2RrOHMtMS4wLjBiNjMtcHkzLW5vbmUtYW55LndobFxuICAgIGNvbnN0IHJlZ2V4ID0gLyguKiktdj8oXFxkLiopLXB5Lioud2hsLztcblxuICAgIGNvbnN0IG1hdGNoID0gYXJ0aWZhY3ROYW1lLm1hdGNoKHJlZ2V4KTtcbiAgICBpZiAoIW1hdGNoKSB7XG4gICAgICB0aHJvdyBuZXcgRXJyb3IoYFVuYWJsZSB0byBwYXJzZSBhcnRpZmFjdDogJHthcnRpZmFjdE5hbWV9YCk7XG4gICAgfVxuXG4gICAgcmV0dXJuIHsgbmFtZTogbWF0Y2hbMV0sIHZlcnNpb246IG1hdGNoWzJdIH07XG5cbiAgfVxuXG59XG5cbmV4cG9ydCBmdW5jdGlvbiBqc29uR2V0KHVybDogc3RyaW5nLCBqc29uUGF0aD86IHN0cmluZ1tdKTogUHJvbWlzZTxhbnk+IHtcbiAgcmV0dXJuIGdldCh1cmwsIChyZXMsIG9rLCBrbykgPT4ge1xuICAgIGNvbnN0IGpzb24gPSBqc3RyZWFtLnBhcnNlKGpzb25QYXRoKTtcbiAgICBqc29uLm9uY2UoJ2RhdGEnLCBvayk7XG4gICAganNvbi5vbmNlKCdlcnJvcicsIGtvKTtcblxuICAgIHJlcy5waXBlKGpzb24sIHsgZW5kOiB0cnVlIH0pO1xuICB9LCB7IGhlYWRlcnM6IHsgJ0FjY2VwdCc6ICdhcHBsaWNhdGlvbi9qc29uJywgJ0FjY2VwdC1FbmNvZGluZyc6ICdpZGVudGl0eScgfSB9KTtcbn1cblxuZXhwb3J0IGFzeW5jIGZ1bmN0aW9uIGRvd25sb2FkKHVybDogc3RyaW5nLCB0YXJnZXRGaWxlOiBzdHJpbmcpOiBQcm9taXNlPGFueT4ge1xuICByZXR1cm4gZ2V0KHVybCwgKHJlcywgb2ssIGtvKSA9PiB7XG4gICAgY29uc3QgZmlsZSA9IGZzLmNyZWF0ZVdyaXRlU3RyZWFtKHRhcmdldEZpbGUpO1xuICAgIGZpbGUub24oJ2ZpbmlzaCcsIG9rKTtcbiAgICBmaWxlLm9uKCdlcnJvcicsIGtvKTtcbiAgICByZXMucGlwZShmaWxlLCB7IGVuZDogdHJ1ZSB9KTtcbiAgfSk7XG59XG5cbmV4cG9ydCBhc3luYyBmdW5jdGlvbiBnZXQoXG4gIHVybDogc3RyaW5nLFxuICBoYW5kbGVyOiAocmVzOiBJbmNvbWluZ01lc3NhZ2UsIG9rOiAodmFsdWU6IHVua25vd24pID0+IHZvaWQsIGtvOiAoZXJyOiBFcnJvcikgPT4gdm9pZCkgPT4gdm9pZCxcbiAgb3B0aW9uczogUmVxdWVzdE9wdGlvbnMgPSB7fSkge1xuXG4gIHJldHVybiBuZXcgUHJvbWlzZSgob2ssIGtvKSA9PiB7XG4gICAgY29uc3QgcmVxdWVzdCA9IGh0dHBzLmdldCh1cmwsIG9wdGlvbnMsIChyZXM6IEluY29taW5nTWVzc2FnZSkgPT4ge1xuICAgICAgaWYgKHJlcy5zdGF0dXNDb2RlICE9PSAyMDApIHtcbiAgICAgICAgY29uc3QgZXJyb3IgPSBuZXcgRXJyb3IoYEdFVCAke3VybH0gLSBIVFRQICR7cmVzLnN0YXR1c0NvZGV9ICgke3Jlcy5zdGF0dXNNZXNzYWdlfSlgKTtcbiAgICAgICAgRXJyb3IuY2FwdHVyZVN0YWNrVHJhY2UoZXJyb3IpO1xuICAgICAgICByZXR1cm4ga28oZXJyb3IpO1xuICAgICAgfVxuICAgICAgcmVzLm9uY2UoJ2Vycm9yJywga28pO1xuICAgICAgaGFuZGxlcihyZXMsIG9rLCBrbyk7XG4gICAgfSk7XG4gICAgcmVxdWVzdC5vbignZXJyb3InLCBrbyk7XG4gIH0pO1xuXG59XG4iXX0=
@@ -0,0 +1,316 @@
1
+ import { execSync } from 'child_process';
2
+ import type { RequestOptions, IncomingMessage } from 'http';
3
+ import * as https from 'https';
4
+ import * as os from 'os';
5
+ import * as path from 'path';
6
+ // eslint-disable-next-line import/no-extraneous-dependencies
7
+ import AdmZip from 'adm-zip';
8
+ // eslint-disable-next-line import/no-extraneous-dependencies
9
+ import * as fs from 'fs-extra';
10
+ // eslint-disable-next-line import/no-extraneous-dependencies
11
+ import * as jstream from 'JSONStream';
12
+ // eslint-disable-next-line import/no-extraneous-dependencies
13
+ import * as tar from 'tar';
14
+ import { Repository } from './repository';
15
+
16
+
17
+ /**
18
+ * Published package.
19
+ */
20
+ export interface PublishedPackage {
21
+
22
+ /**
23
+ * Name of the package as stored in the package manager.
24
+ */
25
+ readonly name: string;
26
+
27
+ /**
28
+ * Version of the package as stored in the package manager.
29
+ */
30
+ readonly version: string;
31
+ }
32
+
33
+ /**
34
+ * Integrity class for validating a local artifact against its published counterpart.
35
+ *
36
+ * Implementations differ based on the package manager in question.
37
+ */
38
+ export abstract class ArtifactIntegrity {
39
+
40
+ /**
41
+ * The file extenstion of artifacts produced for this check. (e.g 'whl')
42
+ */
43
+ protected abstract readonly ext: string;
44
+
45
+ /**
46
+ * Download a package to the target file.
47
+ *
48
+ * @param pkg The package to download.
49
+ * @param targetFile The file path to download the package to.
50
+ */
51
+ protected abstract download(pkg: PublishedPackage, targetFile: string): Promise<void>;
52
+
53
+ /**
54
+ * Extract the artifact into the target directory.
55
+ *
56
+ * @param artifact Path to an artifact file.
57
+ * @param targetDir The directory to extract to. It will exist by the time this method is invoked.
58
+ */
59
+ protected abstract extract(artifact: string, targetDir: string): Promise<void>;
60
+
61
+ /**
62
+ * Parse a local artifact file name into a structured package.
63
+ *
64
+ * @param artifactName Base name of the local artifact file.
65
+ * @returns The package this artifact correlates to.
66
+ */
67
+ protected abstract parseArtifactName(artifactName: string): PublishedPackage;
68
+
69
+ /**
70
+ * Validate a local artifact against its published counterpart.
71
+ *
72
+ * @param localArtifactDir The directory of the local artifact. Must contain exactly one file with the appropriate extenstion.
73
+ */
74
+ public async validate(localArtifactDir: string) {
75
+
76
+ const artifactPath = this.findOne(localArtifactDir);
77
+ const name = this.constructor.name;
78
+
79
+ this.log(`Validating ${artifactPath}`);
80
+ const workdir = fs.mkdtempSync(path.join(os.tmpdir(), 'integrity-check'));
81
+
82
+ try {
83
+ const downloaded = path.join(workdir, `${name}.downloaded`);
84
+ const remote = path.join(workdir, `${name}.remote`);
85
+ const local = path.join(workdir, `${name}.local`);
86
+
87
+ // parse the artifact name into a package.
88
+ const pkg = this.parseArtifactName(path.basename(artifactPath));
89
+
90
+ fs.mkdirSync(remote);
91
+ fs.mkdirSync(local);
92
+
93
+ // download the package
94
+ this.log(`Downloading ${pkg.name}@${pkg.version} to ${downloaded}`);
95
+ await this.download(pkg, downloaded);
96
+
97
+ // extract the downlaoded package
98
+ this.log(`Extracting remote artifact from ${downloaded} to ${remote}`);
99
+ await this.extract(downloaded, remote);
100
+
101
+ // extract the local artfiact
102
+ this.log(`Extracting local artifact from ${artifactPath} to ${local}`);
103
+ await this.extract(artifactPath, local);
104
+
105
+ this.log(`Comparing ${local} <> ${remote}`);
106
+ try {
107
+ execSync(`diff ${local} ${remote}`, { stdio: ['ignore', 'inherit', 'inherit'] });
108
+ } catch (error) {
109
+ throw new Error(`${name} validation failed`);
110
+ }
111
+ this.log('Success');
112
+
113
+ } finally {
114
+ fs.removeSync(workdir);
115
+ }
116
+
117
+ }
118
+
119
+ protected log(message: string) {
120
+ console.log(`${this.constructor.name} | ${message} `);
121
+ }
122
+
123
+ private findOne(dir: string): string {
124
+ const files = fs.readdirSync(dir).filter(f => f.endsWith(this.ext));
125
+ if (files.length === 0) {
126
+ throw new Error(`No files found in ${dir} with extension ${this.ext}`);
127
+ }
128
+ const [first, ...rest] = files;
129
+ if (rest.length > 0) {
130
+ throw new Error(`Multiple files found in ${dir} with extension ${this.ext}: ${first}, ${rest.join(', ')}`);
131
+ }
132
+ return path.join(dir, first);
133
+ }
134
+
135
+ }
136
+
137
+ /**
138
+ * Properties for `RepositoryIntegrity`.
139
+ */
140
+ export interface RepositoryIntegrityProps {
141
+ /**
142
+ * Repository to validate.
143
+ */
144
+ readonly repository: Repository;
145
+
146
+ /**
147
+ * The command that produces the local artifacts.
148
+ *
149
+ * @default 'npx projen release'
150
+ */
151
+ readonly packCommand?: string;
152
+ }
153
+
154
+ /**
155
+ * Integrity class for validating the artifacts produced by this repository against their published counterparts.
156
+ */
157
+ export class RepositoryIntegrity {
158
+
159
+ public constructor(private readonly props: RepositoryIntegrityProps) {}
160
+
161
+ /**
162
+ * Validate the artifacts of this repo against its published counterpart.
163
+ */
164
+ public async validate() {
165
+
166
+ // note that run 'release' by default to preserve the version number.
167
+ // this won't do a bump since the commit we are on is already tagged.
168
+ const artifacts = this.props.repository.pack(this.props.packCommand ?? 'npx projen release');
169
+
170
+ let integrity = undefined;
171
+ for (const artifact of artifacts) {
172
+ switch (artifact.lang) {
173
+ case 'js':
174
+ integrity = new NpmArtifactIntegrity();
175
+ break;
176
+ case 'python':
177
+ integrity = new PyPIArtifactIntegrity();
178
+ break;
179
+ case 'java':
180
+ case 'dotnet':
181
+ case 'go':
182
+ // we don't have integrity checks for these
183
+ // artifacts yet.
184
+ break;
185
+ default:
186
+ throw new Error(`Unsupported artifact language: ${artifact.lang}`);
187
+ }
188
+ if (integrity) {
189
+ await integrity.validate(artifact.directory);
190
+ }
191
+ }
192
+ console.log('Validation done');
193
+ }
194
+
195
+ }
196
+
197
+ /**
198
+ * NpmIntegiry is able to perform integiry checks against packages stored on npmjs.com
199
+ */
200
+ export class NpmArtifactIntegrity extends ArtifactIntegrity {
201
+
202
+ protected readonly ext = 'tgz';
203
+
204
+ protected async download(pkg: PublishedPackage, target: string): Promise<void> {
205
+ const tarballUrl = await jsonGet(`https://registry.npmjs.org/${encodeURIComponent(pkg.name)}/${encodeURIComponent(pkg.version)}`, ['dist', 'tarball']);
206
+ await download(tarballUrl, target);
207
+ }
208
+
209
+ public async extract(file: string, targetDir: string): Promise<void> {
210
+ return tar.x({ cwd: targetDir, file: file, strip: 1 });
211
+ }
212
+
213
+ protected parseArtifactName(artifactName: string): PublishedPackage {
214
+
215
+ // cdk8s@1.0.0-beta.59.jsii.tgz
216
+ const jsiiArtifact = /(.*)@(.*)\.jsii./;
217
+
218
+ // npm artifact: cdk8s-cli-1.0.0-beta59.tgz
219
+ // yarn artifact: cdk8s-cli-v1.0.0-beta59.tgz (add a 'v' before the version)
220
+ const npmOrYarnArtifact = /(.*)-v?(\d.*).(tgz|tar.gz)/;
221
+
222
+ const regex = artifactName.includes('.jsii.') ? jsiiArtifact : npmOrYarnArtifact;
223
+
224
+ const match = artifactName.match(regex);
225
+ if (!match) {
226
+ throw new Error(`Unable to parse artifact: ${artifactName}`);
227
+ }
228
+
229
+ return { name: match[1], version: match[2] };
230
+
231
+ }
232
+
233
+ }
234
+
235
+ /**
236
+ * PyPIIntegiry is able to perform integiry checks against packages stored on pypi.org
237
+ */
238
+ export class PyPIArtifactIntegrity extends ArtifactIntegrity {
239
+
240
+ protected readonly ext = 'whl';
241
+
242
+ protected async download(pkg: PublishedPackage, target: string): Promise<void> {
243
+
244
+ const files = await jsonGet(`https://pypi.org/pypi/${encodeURIComponent(pkg.name)}/json`, ['releases', pkg.version]);
245
+ const wheels: string[] = files.filter((f: any) => f.url.endsWith('whl')).map((f: any) => f.url);
246
+
247
+ if (wheels.length === 0) {
248
+ throw new Error(`No wheels found for package ${pkg.name}-${pkg.version}`);
249
+ }
250
+
251
+ if (wheels.length > 1) {
252
+ throw new Error(`Multiple wheels found for package ${pkg.name}-${pkg.version}: ${wheels.join(',')}`);
253
+ }
254
+
255
+ await download(wheels[0], target);
256
+ }
257
+
258
+ public async extract(artifact: string, target: string): Promise<void> {
259
+ const zip = new AdmZip(artifact);
260
+ return zip.extractAllTo(target);
261
+ }
262
+
263
+ protected parseArtifactName(artifactName: string): PublishedPackage {
264
+
265
+ // cdk8s-1.0.0b63-py3-none-any.whl
266
+ const regex = /(.*)-v?(\d.*)-py.*.whl/;
267
+
268
+ const match = artifactName.match(regex);
269
+ if (!match) {
270
+ throw new Error(`Unable to parse artifact: ${artifactName}`);
271
+ }
272
+
273
+ return { name: match[1], version: match[2] };
274
+
275
+ }
276
+
277
+ }
278
+
279
+ export function jsonGet(url: string, jsonPath?: string[]): Promise<any> {
280
+ return get(url, (res, ok, ko) => {
281
+ const json = jstream.parse(jsonPath);
282
+ json.once('data', ok);
283
+ json.once('error', ko);
284
+
285
+ res.pipe(json, { end: true });
286
+ }, { headers: { 'Accept': 'application/json', 'Accept-Encoding': 'identity' } });
287
+ }
288
+
289
+ export async function download(url: string, targetFile: string): Promise<any> {
290
+ return get(url, (res, ok, ko) => {
291
+ const file = fs.createWriteStream(targetFile);
292
+ file.on('finish', ok);
293
+ file.on('error', ko);
294
+ res.pipe(file, { end: true });
295
+ });
296
+ }
297
+
298
+ export async function get(
299
+ url: string,
300
+ handler: (res: IncomingMessage, ok: (value: unknown) => void, ko: (err: Error) => void) => void,
301
+ options: RequestOptions = {}) {
302
+
303
+ return new Promise((ok, ko) => {
304
+ const request = https.get(url, options, (res: IncomingMessage) => {
305
+ if (res.statusCode !== 200) {
306
+ const error = new Error(`GET ${url} - HTTP ${res.statusCode} (${res.statusMessage})`);
307
+ Error.captureStackTrace(error);
308
+ return ko(error);
309
+ }
310
+ res.once('error', ko);
311
+ handler(res, ok, ko);
312
+ });
313
+ request.on('error', ko);
314
+ });
315
+
316
+ }
@@ -0,0 +1,81 @@
1
+ /**
2
+ * Properties for `Repository`.
3
+ */
4
+ export interface RepositoryProps {
5
+ /**
6
+ * Local directory where the repository was cloned to.
7
+ */
8
+ readonly repoDir: string;
9
+ }
10
+ /**
11
+ * Artifact produced by this repository.
12
+ */
13
+ export interface Artifact {
14
+ /**
15
+ * Language of the artifact.
16
+ */
17
+ readonly lang: string;
18
+ /**
19
+ * Directory containing the artifact.
20
+ */
21
+ readonly directory: string;
22
+ }
23
+ /**
24
+ * Options for `Repository.fromGitHub`
25
+ */
26
+ export interface RepositoryFromGitHubOptions {
27
+ /**
28
+ * Repository slug (e.g cdk8s-team/cdk8s-core)
29
+ */
30
+ readonly slug: string;
31
+ /**
32
+ * Repository tag.
33
+ *
34
+ * @default - latest tag based on creation date.
35
+ */
36
+ readonly tag?: string;
37
+ /**
38
+ * Prefix for detecting the latest tag of the repo. Only applies if `tag` isn't specified.
39
+ * This is useful for repositories that produce multiple packages, and hence multiple tags
40
+ * for example: https://github.com/cdk8s-team/cdk8s-plus/tags.
41
+ */
42
+ readonly tagPrefix?: string;
43
+ /**
44
+ * ARN of an AWS secrets manager secret containing a GitHub token.
45
+ * Required for private repositories. Recommended for public ones, to avoid throtlling issues.
46
+ *
47
+ * @default - the repository is cloned without credentials.
48
+ */
49
+ readonly githubTokenSecretArn?: string;
50
+ }
51
+ /**
52
+ * Options for `Repository.fromDir`
53
+ */
54
+ export interface RepositoryFromDirOptions {
55
+ /**
56
+ * The directory of the repo.
57
+ */
58
+ readonly repoDir: string;
59
+ }
60
+ /**
61
+ * Repository containing a node project.
62
+ */
63
+ export declare class Repository {
64
+ readonly repoDir: string;
65
+ /**
66
+ * Create a repository from a local directory.
67
+ */
68
+ static fromDir(options: RepositoryFromDirOptions): Promise<Repository>;
69
+ /**
70
+ * Create a repository from a GitHub repository.
71
+ */
72
+ static fromGitHub(options: RepositoryFromGitHubOptions): Promise<Repository>;
73
+ private readonly isJsii;
74
+ private readonly manifest;
75
+ private constructor();
76
+ /**
77
+ * Pack the repository to produce the artifacts.
78
+ */
79
+ pack(command: string): Artifact[];
80
+ private _shell;
81
+ }