build-strap 6.0.1 → 6.1.0-main.159
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.
- package/README.md +23 -1
- package/package.json +6 -6
- package/src/cert.d.ts +1 -1
- package/src/cert.js +1 -1
- package/src/copy.d.ts +6 -1
- package/src/copy.js +29 -8
- package/src/cp.js +1 -2
- package/src/docker.container.js +46 -35
- package/src/docker.js +9 -13
- package/src/docker.network.d.ts +1 -1
- package/src/docker.network.js +2 -3
- package/src/docker.volume.js +4 -1
- package/src/dockerCleanup.js +5 -1
- package/src/dockerCompose.d.ts +2 -2
- package/src/dockerCompose.js +2 -4
- package/src/env.js +0 -2
- package/src/fs.js +1 -1
- package/src/git.d.ts +2 -2
- package/src/npm.d.ts +4 -4
- package/src/orbstack.js +4 -8
- package/src/pkg.js +3 -3
- package/src/run.d.ts +26 -1
- package/src/run.js +55 -4
- package/src/tgz.d.ts +4 -4
- package/src/tgz.js +16 -6
- package/src/version.d.ts +2 -2
- package/src/version.js +6 -5
package/README.md
CHANGED
|
@@ -120,13 +120,35 @@ publish(
|
|
|
120
120
|
```
|
|
121
121
|
|
|
122
122
|
### buildLog
|
|
123
|
-
Write out to the console in a timestamp prefixed format consistent with the rest of the build output.
|
|
123
|
+
Write out to the console in a timestamp prefixed format consistent with the rest of the build output. Output goes to `stderr` by default, so that `stdout` can be reserved for machine readable output.
|
|
124
124
|
```
|
|
125
125
|
import { buildLog } from 'build-strap';
|
|
126
126
|
|
|
127
127
|
buildLog('Hello world');
|
|
128
128
|
```
|
|
129
129
|
|
|
130
|
+
Pass a `Date` (as before) or an options object to control the timestamp and destination:
|
|
131
|
+
```
|
|
132
|
+
buildLog('Hello world', new Date());
|
|
133
|
+
buildLog('to stdout instead', { stream: 'stdout' });
|
|
134
|
+
buildLog('to any writable stream', { stream: myWriteStream });
|
|
135
|
+
buildLog('or handle it yourself', { stream: (msg) => myLogger.info(msg) });
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
The default destination can be changed globally (affects `run`/`runCli` logging too):
|
|
139
|
+
```
|
|
140
|
+
import { getBuildLogStream, setBuildLogStream } from 'build-strap';
|
|
141
|
+
|
|
142
|
+
setBuildLogStream('stdout');
|
|
143
|
+
setBuildLogStream(process.stderr);
|
|
144
|
+
setBuildLogStream((msg) => myLogger.info(msg));
|
|
145
|
+
getBuildLogStream(); // current default
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
CLI flags:
|
|
149
|
+
- `--buildLog-stdout` — default to `stdout` instead of `stderr`
|
|
150
|
+
- `--silence-buildLog` — suppress all `buildLog` output
|
|
151
|
+
|
|
130
152
|
## NPM Credentials
|
|
131
153
|
In order to publish to NPM, proper credentials must be provided to the script. By default, these are read from the `NPM_CREDS` environment variable, but it is also possible to pass them as an argument to most functions. This is expected to be a JSON encoded string in the following format:
|
|
132
154
|
```
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "build-strap",
|
|
3
|
-
"version": "6.0.
|
|
3
|
+
"version": "6.1.0-main.159",
|
|
4
4
|
"description": "Node scripts for building things",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"author": "Justin Murray <justin@murrayju.com>",
|
|
@@ -16,17 +16,17 @@
|
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
18
|
"bytes": "^3.1.1",
|
|
19
|
-
"chokidar": "^
|
|
19
|
+
"chokidar": "^5.0.0",
|
|
20
20
|
"cross-spawn": "^7.0.3",
|
|
21
21
|
"fs-extra": "^11.2.0",
|
|
22
22
|
"get-port": "^7.1.0",
|
|
23
|
-
"glob": "^
|
|
24
|
-
"is-reachable": "^
|
|
25
|
-
"js-yaml": "^4.
|
|
23
|
+
"glob": "^13.0.6",
|
|
24
|
+
"is-reachable": "^6.1.0",
|
|
25
|
+
"js-yaml": "^5.4.0",
|
|
26
26
|
"lodash-es": "^4.17.21",
|
|
27
27
|
"moment": "^2.30.1",
|
|
28
28
|
"node-fetch": "^3.3.2",
|
|
29
|
-
"rimraf": "^
|
|
29
|
+
"rimraf": "^6.1.3",
|
|
30
30
|
"stream-counter": "1",
|
|
31
31
|
"tar": "^7.1.0"
|
|
32
32
|
}
|
package/src/cert.d.ts
CHANGED
|
@@ -3,8 +3,8 @@ export interface CertConfig {
|
|
|
3
3
|
countryName?: string;
|
|
4
4
|
emailAddress?: string;
|
|
5
5
|
localityName?: string;
|
|
6
|
-
organizationName?: string;
|
|
7
6
|
organizationalUnitName?: string;
|
|
7
|
+
organizationName?: string;
|
|
8
8
|
stateOrProvinceName?: string;
|
|
9
9
|
}
|
|
10
10
|
export declare const getCertConfig: () => CertConfig;
|
package/src/cert.js
CHANGED
|
@@ -21,7 +21,7 @@ export async function generateCert(targetDir, force = process.argv.includes('--f
|
|
|
21
21
|
return;
|
|
22
22
|
}
|
|
23
23
|
buildLog('Generating self-signed certificate...');
|
|
24
|
-
const { commonName = 'app.company.com', countryName = 'US', emailAddress = 'contact@company.com', localityName = 'SomeCity',
|
|
24
|
+
const { commonName = 'app.company.com', countryName = 'US', emailAddress = 'contact@company.com', localityName = 'SomeCity', organizationalUnitName = 'Product', organizationName = 'Company', stateOrProvinceName = 'Pennsylvania', } = getCertConfig();
|
|
25
25
|
const hostname = os.hostname();
|
|
26
26
|
buildLog(`hostname: ${hostname}`);
|
|
27
27
|
const ipAddresses = getIpAddresses();
|
package/src/copy.d.ts
CHANGED
|
@@ -1,5 +1,10 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Callback invoked after a copy completes. May be sync or async; a returned
|
|
3
|
+
* promise is awaited.
|
|
4
|
+
*/
|
|
5
|
+
export type CopySrcCallback = () => Promise<void> | void;
|
|
1
6
|
export interface CopySrcOptions {
|
|
2
|
-
cbFn?:
|
|
7
|
+
cbFn?: CopySrcCallback;
|
|
3
8
|
from: string;
|
|
4
9
|
to: string;
|
|
5
10
|
watch?: boolean;
|
package/src/copy.js
CHANGED
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { watch as chokidarWatch } from 'chokidar';
|
|
2
2
|
import fs from 'fs-extra';
|
|
3
3
|
import path from 'path';
|
|
4
4
|
import { onKillSignal } from './cp.js';
|
|
5
5
|
import { cleanDir, copyDir } from './fs.js';
|
|
6
|
-
import { buildLog } from './run.js';
|
|
6
|
+
import { buildLog, errorMessage } from './run.js';
|
|
7
7
|
let timer = null;
|
|
8
|
-
|
|
8
|
+
function throttledCallback(cbFn) {
|
|
9
9
|
if (!cbFn || typeof cbFn !== 'function') {
|
|
10
10
|
return;
|
|
11
11
|
}
|
|
@@ -15,7 +15,16 @@ async function throttledCallback(cbFn) {
|
|
|
15
15
|
}
|
|
16
16
|
timer = setTimeout(() => {
|
|
17
17
|
timer = null;
|
|
18
|
-
|
|
18
|
+
// the callback may be async, so surface a rejection instead of leaving it
|
|
19
|
+
// floating on the timer
|
|
20
|
+
void (async () => {
|
|
21
|
+
try {
|
|
22
|
+
await cbFn();
|
|
23
|
+
}
|
|
24
|
+
catch (e) {
|
|
25
|
+
buildLog(`Error in copySrc callback: ${errorMessage(e)}`);
|
|
26
|
+
}
|
|
27
|
+
})();
|
|
19
28
|
}, 200);
|
|
20
29
|
}
|
|
21
30
|
export async function copySrc({ cbFn, from, to, watch = process.argv.includes('--watch'), }) {
|
|
@@ -23,10 +32,13 @@ export async function copySrc({ cbFn, from, to, watch = process.argv.includes('-
|
|
|
23
32
|
if (cbFn)
|
|
24
33
|
await cbFn();
|
|
25
34
|
if (watch) {
|
|
26
|
-
|
|
35
|
+
// chokidar v4 removed glob support; watch the directory recursively instead
|
|
36
|
+
const watcher = chokidarWatch(from, {
|
|
27
37
|
ignoreInitial: true,
|
|
28
38
|
});
|
|
29
|
-
|
|
39
|
+
// chokidar's typings expect a void-returning listener, so the async work
|
|
40
|
+
// is wrapped and its rejection handled rather than left floating
|
|
41
|
+
const handleEvent = async (event, filePath) => {
|
|
30
42
|
const start = new Date();
|
|
31
43
|
const src = path.relative(from, filePath);
|
|
32
44
|
const dest = path.join(to, src);
|
|
@@ -38,7 +50,9 @@ export async function copySrc({ cbFn, from, to, watch = process.argv.includes('-
|
|
|
38
50
|
break;
|
|
39
51
|
case 'unlink':
|
|
40
52
|
case 'unlinkDir':
|
|
41
|
-
|
|
53
|
+
// must be awaited: otherwise the log below (and the throttled
|
|
54
|
+
// callback) can fire before the delete actually completes
|
|
55
|
+
await cleanDir(dest, { dot: true });
|
|
42
56
|
break;
|
|
43
57
|
default:
|
|
44
58
|
return;
|
|
@@ -47,7 +61,14 @@ export async function copySrc({ cbFn, from, to, watch = process.argv.includes('-
|
|
|
47
61
|
const time = end.getTime() - start.getTime();
|
|
48
62
|
buildLog(`${event} '${dest}' after ${time} ms`, end);
|
|
49
63
|
throttledCallback(cbFn);
|
|
64
|
+
};
|
|
65
|
+
watcher.on('all', (event, filePath) => {
|
|
66
|
+
handleEvent(event, filePath).catch((e) => {
|
|
67
|
+
buildLog(`Error handling ${event} for ${filePath}: ${errorMessage(e)}`);
|
|
68
|
+
});
|
|
69
|
+
});
|
|
70
|
+
onKillSignal(() => {
|
|
71
|
+
void watcher.close();
|
|
50
72
|
});
|
|
51
|
-
onKillSignal(() => watcher.close());
|
|
52
73
|
}
|
|
53
74
|
}
|
package/src/cp.js
CHANGED
|
@@ -63,7 +63,6 @@ export async function spawn(command, args, opts) {
|
|
|
63
63
|
p.on('close', handleExit);
|
|
64
64
|
p.on('exit', handleExit);
|
|
65
65
|
// only fires if we failed to spawn
|
|
66
|
-
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
67
66
|
p.on('error', (err) => {
|
|
68
67
|
// Here we have a workaround that reverses a "feature" of cross-spawn on Windows. See:
|
|
69
68
|
// https://github.com/moxystudio/node-cross-spawn/blob/master/lib/enoent.js#L23
|
|
@@ -79,7 +78,7 @@ export async function spawn(command, args, opts) {
|
|
|
79
78
|
});
|
|
80
79
|
}
|
|
81
80
|
catch (err) {
|
|
82
|
-
reject(err);
|
|
81
|
+
reject(err instanceof Error ? err : new Error(String(err)));
|
|
83
82
|
}
|
|
84
83
|
});
|
|
85
84
|
}
|
package/src/docker.container.js
CHANGED
|
@@ -2,7 +2,7 @@ import bytes from 'bytes';
|
|
|
2
2
|
import { throttle } from 'lodash-es';
|
|
3
3
|
import { spawn } from './cp.js';
|
|
4
4
|
import { parseDockerDate } from './docker.js';
|
|
5
|
-
import { buildLog } from './run.js';
|
|
5
|
+
import { buildLog, errorMessage } from './run.js';
|
|
6
6
|
// calling this function in rapid succession can lead to errors.
|
|
7
7
|
// prefer throttled version
|
|
8
8
|
export async function unthrottledDockerContainerLs({ all, filter, } = {}) {
|
|
@@ -31,7 +31,7 @@ export async function unthrottledDockerContainerLs({ all, filter, } = {}) {
|
|
|
31
31
|
return false;
|
|
32
32
|
}
|
|
33
33
|
},
|
|
34
|
-
exited:
|
|
34
|
+
exited: status.startsWith('Exited'),
|
|
35
35
|
id,
|
|
36
36
|
image,
|
|
37
37
|
inspect: async () => (await dockerContainerInspect(id))?.[0],
|
|
@@ -50,7 +50,7 @@ export async function unthrottledDockerContainerLs({ all, filter, } = {}) {
|
|
|
50
50
|
names,
|
|
51
51
|
networks: Networks.split(',').map((n) => n.trim()),
|
|
52
52
|
ports: Ports.split(',').map((p) => {
|
|
53
|
-
const [, iface, src, dest] =
|
|
53
|
+
const [, iface, src, dest] = /^([^:]+):(\d+)-\\u003e(\d+)/i.exec(p) || [];
|
|
54
54
|
return {
|
|
55
55
|
dest: parseInt(dest, 10),
|
|
56
56
|
iface,
|
|
@@ -73,14 +73,14 @@ export async function unthrottledDockerContainerLs({ all, filter, } = {}) {
|
|
|
73
73
|
log(`<${this.name}> container stopped.`);
|
|
74
74
|
}
|
|
75
75
|
catch (e1) {
|
|
76
|
-
log(`Failed to stop <${this.name}> container: ${e1
|
|
76
|
+
log(`Failed to stop <${this.name}> container: ${errorMessage(e1)}`);
|
|
77
77
|
try {
|
|
78
78
|
log(`Killing container: <${this.name}>...`);
|
|
79
79
|
await this.kill(false);
|
|
80
80
|
log(`<${this.name}> container killed.`);
|
|
81
81
|
}
|
|
82
82
|
catch (e2) {
|
|
83
|
-
log(`Failed to kill <${this.name}> container: ${e2
|
|
83
|
+
log(`Failed to kill <${this.name}> container: ${errorMessage(e2)}`);
|
|
84
84
|
}
|
|
85
85
|
}
|
|
86
86
|
}
|
|
@@ -113,7 +113,9 @@ export async function dockerContainerStop(id, ignoreErrors = true) {
|
|
|
113
113
|
}
|
|
114
114
|
catch (e) {
|
|
115
115
|
if (!ignoreErrors) {
|
|
116
|
-
throw new Error(`Failed to stop container(s): ${e
|
|
116
|
+
throw new Error(`Failed to stop container(s): ${errorMessage(e)}`, {
|
|
117
|
+
cause: e,
|
|
118
|
+
});
|
|
117
119
|
}
|
|
118
120
|
}
|
|
119
121
|
}
|
|
@@ -124,7 +126,9 @@ export async function dockerContainerKill(id, ignoreErrors = true) {
|
|
|
124
126
|
}
|
|
125
127
|
catch (e) {
|
|
126
128
|
if (!ignoreErrors) {
|
|
127
|
-
throw new Error(`Failed to kill container(s): ${e
|
|
129
|
+
throw new Error(`Failed to kill container(s): ${errorMessage(e)}`, {
|
|
130
|
+
cause: e,
|
|
131
|
+
});
|
|
128
132
|
}
|
|
129
133
|
}
|
|
130
134
|
}
|
|
@@ -134,7 +138,7 @@ export async function dockerTryStopContainer(id, name = '') {
|
|
|
134
138
|
await dockerContainerStop(id, false);
|
|
135
139
|
}
|
|
136
140
|
catch (e) {
|
|
137
|
-
buildLog(`Failed to stop ${name} container: ${e
|
|
141
|
+
buildLog(`Failed to stop ${name} container: ${errorMessage(e)}`);
|
|
138
142
|
}
|
|
139
143
|
}
|
|
140
144
|
}
|
|
@@ -145,10 +149,12 @@ export async function dockerContainerRm(id, ignoreErrors = true) {
|
|
|
145
149
|
}
|
|
146
150
|
catch (e) {
|
|
147
151
|
if (ignoreErrors) {
|
|
148
|
-
buildLog(`Warning (ignored Error): Failed to remove container(s): ${e
|
|
152
|
+
buildLog(`Warning (ignored Error): Failed to remove container(s): ${errorMessage(e)}`);
|
|
149
153
|
}
|
|
150
154
|
else {
|
|
151
|
-
throw new Error(`Failed to remove container(s): ${e
|
|
155
|
+
throw new Error(`Failed to remove container(s): ${errorMessage(e)}`, {
|
|
156
|
+
cause: e,
|
|
157
|
+
});
|
|
152
158
|
}
|
|
153
159
|
}
|
|
154
160
|
}
|
|
@@ -191,7 +197,6 @@ export async function dockerContainerRunDaemon({ cmd = [], image, runArgs = [],
|
|
|
191
197
|
let container = await dockerContainerFind(id);
|
|
192
198
|
let attempts = 0;
|
|
193
199
|
// containers don't always show up in the list right away
|
|
194
|
-
/* eslint-disable no-await-in-loop */
|
|
195
200
|
while (!container && attempts < waitAttempts) {
|
|
196
201
|
await new Promise((resolve) => {
|
|
197
202
|
setTimeout(resolve, waitDuration);
|
|
@@ -199,7 +204,6 @@ export async function dockerContainerRunDaemon({ cmd = [], image, runArgs = [],
|
|
|
199
204
|
container = await dockerContainerFind(id);
|
|
200
205
|
attempts += 1;
|
|
201
206
|
}
|
|
202
|
-
/* eslint-enable no-await-in-loop */
|
|
203
207
|
if (!container) {
|
|
204
208
|
throw new Error(`Failed to find newly created container: ${id}`);
|
|
205
209
|
}
|
|
@@ -221,27 +225,34 @@ export const dockerContainerWaitForStart = async (container, testFn, timeoutMs =
|
|
|
221
225
|
curIndicator = (curIndicator + 1) % indicators.length;
|
|
222
226
|
const { name } = container;
|
|
223
227
|
buildLog(`Waiting for ${name} to fully start... ${indicator}`);
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
setTimeout(
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
228
|
+
for (let tries = 0; tries <= maxAttempts; tries += 1) {
|
|
229
|
+
process.stdout.write(indicator);
|
|
230
|
+
await new Promise((resolve) => {
|
|
231
|
+
setTimeout(resolve, timeoutMs);
|
|
232
|
+
});
|
|
233
|
+
// A container that has definitively gone away will never become ready, so
|
|
234
|
+
// that case is terminal. An error from either check just means the state is
|
|
235
|
+
// currently unknown, so keep polling until maxAttempts is exhausted.
|
|
236
|
+
let gone = false;
|
|
237
|
+
let ready = false;
|
|
238
|
+
try {
|
|
239
|
+
if (await container.isRunning()) {
|
|
240
|
+
ready = await testFn(container);
|
|
241
|
+
}
|
|
242
|
+
else {
|
|
243
|
+
gone = true;
|
|
244
|
+
}
|
|
245
|
+
}
|
|
246
|
+
catch {
|
|
247
|
+
// state unknown; keep polling
|
|
248
|
+
}
|
|
249
|
+
if (gone) {
|
|
250
|
+
throw new Error(`The '${name}' container is no longer running.`);
|
|
251
|
+
}
|
|
252
|
+
if (ready) {
|
|
253
|
+
process.stdout.write('\n');
|
|
254
|
+
return;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
throw new Error(`Timeout waiting for '${name}' container to start.`);
|
|
247
258
|
};
|
package/src/docker.js
CHANGED
|
@@ -11,7 +11,7 @@ import { cmdExists, isArm, isMac } from './env.js';
|
|
|
11
11
|
import { downloadFile } from './fetch.js';
|
|
12
12
|
import { mountDmg, unmountDmg } from './macos.js';
|
|
13
13
|
import { getCfg, getPkgName, getPkgScope } from './pkg.js';
|
|
14
|
-
import { buildLog } from './run.js';
|
|
14
|
+
import { buildLog, errorMessage } from './run.js';
|
|
15
15
|
import { getMainBranch, getVersion } from './version.js';
|
|
16
16
|
export function getDockerConfig() {
|
|
17
17
|
return getCfg().docker || {};
|
|
@@ -51,12 +51,7 @@ export async function dockerTagVersion(imageId, repo = getDockerRepo()) {
|
|
|
51
51
|
if (isRelease) {
|
|
52
52
|
await dockerTag(imageId, prerelease
|
|
53
53
|
? [npm, `latest-${prerelease.split('.')[0]}`]
|
|
54
|
-
: [
|
|
55
|
-
'latest',
|
|
56
|
-
`${major}`,
|
|
57
|
-
`${major}.${minor}`,
|
|
58
|
-
`${major}.${minor}.${patch}`,
|
|
59
|
-
], repo);
|
|
54
|
+
: ['latest', major, `${major}.${minor}`, `${major}.${minor}.${patch}`], repo);
|
|
60
55
|
}
|
|
61
56
|
else if (branch === getMainBranch()) {
|
|
62
57
|
await dockerTag(imageId, [`latest-${branch}`], repo);
|
|
@@ -77,8 +72,7 @@ export async function dockerImages(repo = getDockerRepo(), filter = null) {
|
|
|
77
72
|
tag,
|
|
78
73
|
};
|
|
79
74
|
})
|
|
80
|
-
.filter((m) =>
|
|
81
|
-
(typeof filter === 'function' ? filter(m) : true));
|
|
75
|
+
.filter((m) => m?.id != null && (typeof filter === 'function' ? filter(m) : true));
|
|
82
76
|
}
|
|
83
77
|
export async function getDockerId(tag = 'latest', repo = null) {
|
|
84
78
|
return (await dockerImages(repo, (m) => m.tag === tag)).map((m) => m.id)[0];
|
|
@@ -106,7 +100,7 @@ export async function dockerPush(tags = ['latest'], repo = getDockerRepo()) {
|
|
|
106
100
|
}
|
|
107
101
|
export async function dockerPull({ image, offline = process.argv.includes('--offline'), testUrl, }) {
|
|
108
102
|
if (offline || !(await isReachable(testUrl || 'https://hub.docker.com'))) {
|
|
109
|
-
if (offline
|
|
103
|
+
if (!offline) {
|
|
110
104
|
throw new Error('Offline, cannot docker pull');
|
|
111
105
|
}
|
|
112
106
|
buildLog('It looks like you are offline, skipping docker pull');
|
|
@@ -122,7 +116,7 @@ export async function dockerRmi(images = [], ignoreErrors = true) {
|
|
|
122
116
|
await prev;
|
|
123
117
|
await spawn('docker', ['rmi', image], { stdio: 'inherit' }).catch((err) => {
|
|
124
118
|
if (ignoreErrors) {
|
|
125
|
-
buildLog(`Warning (ignored Error): Failed to rm image(s): ${err
|
|
119
|
+
buildLog(`Warning (ignored Error): Failed to rm image(s): ${errorMessage(err)}`);
|
|
126
120
|
}
|
|
127
121
|
else {
|
|
128
122
|
throw err;
|
|
@@ -169,7 +163,9 @@ export async function copyFilesFromDockerImage({ filePaths, ignoreErrors, imageI
|
|
|
169
163
|
};
|
|
170
164
|
const copyErrors = [];
|
|
171
165
|
try {
|
|
172
|
-
|
|
166
|
+
// must be awaited: with forEach the copies were never waited on, so the
|
|
167
|
+
// container was killed mid-copy and copyErrors was always empty below
|
|
168
|
+
await Promise.all(filePaths.map(async (fp) => {
|
|
173
169
|
// Try to copy from the Debug Folder
|
|
174
170
|
try {
|
|
175
171
|
await spawn('docker', ['cp', `${container.id}:${fp.from}`, fp.to], spawnOptions);
|
|
@@ -179,7 +175,7 @@ export async function copyFilesFromDockerImage({ filePaths, ignoreErrors, imageI
|
|
|
179
175
|
copyErrors.push(error);
|
|
180
176
|
buildLog(`Error copying file ${fp.from}`);
|
|
181
177
|
}
|
|
182
|
-
});
|
|
178
|
+
}));
|
|
183
179
|
}
|
|
184
180
|
finally {
|
|
185
181
|
try {
|
package/src/docker.network.d.ts
CHANGED
package/src/docker.network.js
CHANGED
|
@@ -12,11 +12,11 @@ export async function unthrottledDockerNetworkLs({ filter, } = {}) {
|
|
|
12
12
|
.map((line) => {
|
|
13
13
|
const { CreatedAt, Driver: driver, ID: id, IPv6, Labels, Name: name, Scope: scope, } = JSON.parse(line.trim());
|
|
14
14
|
return {
|
|
15
|
-
IPv6: IPv6 === 'true',
|
|
16
15
|
created: parseDockerDate(CreatedAt),
|
|
17
16
|
driver,
|
|
18
17
|
exists: async () => !!(await dockerNetworkFind(id)),
|
|
19
18
|
id,
|
|
19
|
+
IPv6: IPv6 === 'true',
|
|
20
20
|
labels: Labels.split(',').map((l) => l.trim()),
|
|
21
21
|
name,
|
|
22
22
|
rm: async () => dockerNetworkRm(id),
|
|
@@ -65,9 +65,8 @@ export async function dockerNetworkRm(id) {
|
|
|
65
65
|
*/
|
|
66
66
|
export async function dockerNetworkDelete(networkName) {
|
|
67
67
|
let existing;
|
|
68
|
-
//
|
|
68
|
+
// extra parens satisfy no-cond-assign's `except-parens` option
|
|
69
69
|
while ((existing = await dockerNetworkFind(networkName))) {
|
|
70
|
-
// eslint-disable-next-line no-await-in-loop
|
|
71
70
|
await existing.rm();
|
|
72
71
|
}
|
|
73
72
|
}
|
package/src/docker.volume.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { throttle } from 'lodash-es';
|
|
2
2
|
import { spawn } from './cp.js';
|
|
3
|
+
import { errorMessage } from './run.js';
|
|
3
4
|
// calling this function in rapid succession can lead to errors.
|
|
4
5
|
// prefer throttled version
|
|
5
6
|
export async function unthrottledDockerVolumeLs({ filter, } = {}) {
|
|
@@ -49,7 +50,9 @@ export async function dockerVolumeRm(id, ignoreErrors = true) {
|
|
|
49
50
|
}
|
|
50
51
|
catch (e) {
|
|
51
52
|
if (!ignoreErrors) {
|
|
52
|
-
throw new Error(`Failed to remove volume(s): ${e
|
|
53
|
+
throw new Error(`Failed to remove volume(s): ${errorMessage(e)}`, {
|
|
54
|
+
cause: e,
|
|
55
|
+
});
|
|
53
56
|
}
|
|
54
57
|
}
|
|
55
58
|
}
|
package/src/dockerCleanup.js
CHANGED
|
@@ -5,6 +5,10 @@ import { dockerImages, dockerRmi, getDockerRepo, getDockerTags, getUntaggedDocke
|
|
|
5
5
|
import { buildLog } from './run.js';
|
|
6
6
|
// flattens arrays of promises and/or promises for arrays
|
|
7
7
|
// into a single array of (awaited) values
|
|
8
|
+
// The recursive shape here defeats both the type checker and the type-aware
|
|
9
|
+
// await rules: `input` may itself be an array of promises, so the awaits are
|
|
10
|
+
// load-bearing even where the static type looks non-thenable.
|
|
11
|
+
/* eslint-disable @typescript-eslint/await-thenable */
|
|
8
12
|
async function flatten(input) {
|
|
9
13
|
const result = await input;
|
|
10
14
|
return Array.isArray(result)
|
|
@@ -40,7 +44,7 @@ export const dockerContainerCleanup = async ({ dryRun = process.argv.includes('-
|
|
|
40
44
|
*/
|
|
41
45
|
export const dockerImageCleanup = async ({ buildImageIds = [], dryRun = process.argv.includes('--dry-run'), images = [], purgeAll = process.argv.includes('--purge-all'), purgeOld = !process.argv.includes('--keep-old'), repos = [getDockerRepo()], } = {}) => {
|
|
42
46
|
// only match images without a :latest tag
|
|
43
|
-
const filterLatest = (m) => purgeAll ||
|
|
47
|
+
const filterLatest = (m) => purgeAll || !/^latest(-\w+)?$/.exec(m.tag);
|
|
44
48
|
// only match images more than an hour old, AND without a :latest* tag
|
|
45
49
|
const filterOld = (m) => purgeAll ||
|
|
46
50
|
(filterLatest(m) && moment().subtract(1, 'h').isAfter(m.created));
|
package/src/dockerCompose.d.ts
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
import { DockerContainer } from './docker.container.js';
|
|
2
2
|
import { DockerNetwork, DockerNetworkCreateOptions } from './docker.network.js';
|
|
3
3
|
import { DockerVolume, DockerVolumeCreateOptions } from './docker.volume.js';
|
|
4
|
-
export
|
|
4
|
+
export interface DockerComposeResourceTracker {
|
|
5
5
|
cleaning: null | Promise<void>;
|
|
6
6
|
containers: Map<string, DockerContainer>;
|
|
7
7
|
networks: Map<string, DockerNetwork>;
|
|
8
8
|
services: Map<string, DockerComposeService>;
|
|
9
9
|
volumes: Map<string, DockerVolume>;
|
|
10
|
-
}
|
|
10
|
+
}
|
|
11
11
|
export declare const dockerComposeCreateResourceTracker: () => DockerComposeResourceTracker;
|
|
12
12
|
type DockerComposeEnvDef = string[] | Record<string, string>;
|
|
13
13
|
type DockerComposeULimitDef = number | {
|
package/src/dockerCompose.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import fs from 'fs-extra';
|
|
2
2
|
import getPort from 'get-port';
|
|
3
|
-
import
|
|
3
|
+
import { load as yamlLoad } from 'js-yaml';
|
|
4
4
|
import path from 'path';
|
|
5
5
|
import { dockerContainerFind, dockerContainerRunDaemon, } from './docker.container.js';
|
|
6
6
|
import { dockerPull } from './docker.js';
|
|
@@ -21,7 +21,7 @@ const defaultTracker = dockerComposeCreateResourceTracker();
|
|
|
21
21
|
* Parse a docker-compose.yml file into a pojo representation
|
|
22
22
|
* @param {string} filePath Path to the docker-compose.yml file
|
|
23
23
|
*/
|
|
24
|
-
export const dockerComposeParse = async (filePath = './docker-compose.yml') =>
|
|
24
|
+
export const dockerComposeParse = async (filePath = './docker-compose.yml') => yamlLoad(await fs.readFile(filePath, 'utf8'));
|
|
25
25
|
let dc = null;
|
|
26
26
|
/**
|
|
27
27
|
* Get the main docker-compose.yml from the root of the repo
|
|
@@ -153,7 +153,6 @@ export class DockerComposeService {
|
|
|
153
153
|
* @param {number} max maximum number of names to try
|
|
154
154
|
*/
|
|
155
155
|
async getNames(avoidConflicts = false, max = 100) {
|
|
156
|
-
/* eslint-disable no-await-in-loop */
|
|
157
156
|
for (let i = 0; i < max; i += 1) {
|
|
158
157
|
const suffix = i ? `-${i}` : '';
|
|
159
158
|
const testName = `${this.defaultContainerName}${suffix}`;
|
|
@@ -166,7 +165,6 @@ export class DockerComposeService {
|
|
|
166
165
|
};
|
|
167
166
|
}
|
|
168
167
|
}
|
|
169
|
-
/* eslint-enable no-await-in-loop */
|
|
170
168
|
throw new Error(`Failed to find available container name for ${this.defaultContainerName}`);
|
|
171
169
|
}
|
|
172
170
|
// Get (available) port mappings
|
package/src/env.js
CHANGED
|
@@ -8,7 +8,6 @@ const cacheBool = (key, fn) => {
|
|
|
8
8
|
if (!boolCache.has(key)) {
|
|
9
9
|
boolCache.set(key, fn());
|
|
10
10
|
}
|
|
11
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
12
11
|
return boolCache.get(key);
|
|
13
12
|
};
|
|
14
13
|
export const isMac = () => cacheBool('isMac', () => os.platform() === 'darwin');
|
|
@@ -22,7 +21,6 @@ const cacheString = (key, fn) => {
|
|
|
22
21
|
if (!stringCache.has(key)) {
|
|
23
22
|
stringCache.set(key, fn());
|
|
24
23
|
}
|
|
25
|
-
// eslint-disable-next-line @typescript-eslint/no-non-null-assertion
|
|
26
24
|
return stringCache.get(key);
|
|
27
25
|
};
|
|
28
26
|
export const shellType = () => cacheString('shellType', () => os.userInfo().shell?.split('/').pop() || null);
|
package/src/fs.js
CHANGED
|
@@ -35,7 +35,7 @@ export const cleanDir = async (pattern, globOptions) => rimraf(pattern, { glob:
|
|
|
35
35
|
export const producedPathExists = async (producer) => {
|
|
36
36
|
try {
|
|
37
37
|
const result = (await producer()).trim();
|
|
38
|
-
return !!result && fs.pathExists(result);
|
|
38
|
+
return !!result && (await fs.pathExists(result));
|
|
39
39
|
}
|
|
40
40
|
catch {
|
|
41
41
|
return false;
|
package/src/git.d.ts
CHANGED
|
@@ -21,10 +21,10 @@ export declare function gitTagsAtCommit(commitish?: string): Promise<string[]>;
|
|
|
21
21
|
* Useful for verifying that a release tag lives on the main branch.
|
|
22
22
|
*/
|
|
23
23
|
export declare function gitIsAncestor(ancestorCommitish: string, descendantCommitish: string): Promise<boolean>;
|
|
24
|
-
|
|
24
|
+
interface GitInfo {
|
|
25
25
|
branch: null | string;
|
|
26
26
|
revision: string;
|
|
27
|
-
}
|
|
27
|
+
}
|
|
28
28
|
export declare function gitInfo(noCache?: boolean): Promise<GitInfo>;
|
|
29
29
|
export declare const ensureGitLfsInstalled: () => Promise<void>;
|
|
30
30
|
export declare const gitConfigRead: (key: string, global?: boolean) => Promise<string>;
|
package/src/npm.d.ts
CHANGED
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { SpawnOptions, SpawnResult } from './cp.js';
|
|
2
|
-
export
|
|
2
|
+
export interface NpmCreds {
|
|
3
3
|
email?: string;
|
|
4
4
|
password?: string;
|
|
5
5
|
username?: string;
|
|
6
|
-
}
|
|
7
|
-
export
|
|
6
|
+
}
|
|
7
|
+
export interface NpmConfig {
|
|
8
8
|
access?: 'public' | 'restricted';
|
|
9
9
|
dryRun?: boolean;
|
|
10
10
|
name?: string;
|
|
11
11
|
publish?: boolean;
|
|
12
12
|
registry?: string;
|
|
13
|
-
}
|
|
13
|
+
}
|
|
14
14
|
export declare function getNpmConfig(): NpmConfig;
|
|
15
15
|
export declare const envNpmCreds: null | NpmCreds;
|
|
16
16
|
export interface NpmOptions {
|
package/src/orbstack.js
CHANGED
|
@@ -67,16 +67,12 @@ export const ensureOrbStackConfigured = async (desiredConfig) => {
|
|
|
67
67
|
};
|
|
68
68
|
if (!Object.entries(desiredConfig).every(([key, value]) => effectiveConfig[key] === value)) {
|
|
69
69
|
buildLog('Adjusting OrbStack config...');
|
|
70
|
-
const
|
|
70
|
+
const defaults = defaultConfig;
|
|
71
|
+
// Don't write values that already match the defaults
|
|
72
|
+
const newConfig = Object.fromEntries(Object.entries({
|
|
71
73
|
...existingConfig,
|
|
72
74
|
...desiredConfig,
|
|
73
|
-
};
|
|
74
|
-
for (const [key, value] of Object.entries(defaultConfig)) {
|
|
75
|
-
if (newConfig[key] === value) {
|
|
76
|
-
// Don't write default values
|
|
77
|
-
delete newConfig[key];
|
|
78
|
-
}
|
|
79
|
-
}
|
|
75
|
+
}).filter(([key, value]) => defaults[key] !== value));
|
|
80
76
|
await fs.writeJson(configPath, newConfig, { spaces: 2 });
|
|
81
77
|
changed = true;
|
|
82
78
|
}
|
package/src/pkg.js
CHANGED
|
@@ -21,7 +21,7 @@ export function getPkgName(includeScope = false) {
|
|
|
21
21
|
if (includeScope) {
|
|
22
22
|
return pkg.name;
|
|
23
23
|
}
|
|
24
|
-
const [, , shortName] = pkg.name
|
|
24
|
+
const [, , shortName] = pkgNameRegex.exec(pkg.name) || [];
|
|
25
25
|
if (!shortName) {
|
|
26
26
|
throw new Error('Package name in package.json has invalid format.');
|
|
27
27
|
}
|
|
@@ -29,11 +29,11 @@ export function getPkgName(includeScope = false) {
|
|
|
29
29
|
}
|
|
30
30
|
export function getPkgScope() {
|
|
31
31
|
const pkgName = getPkgName(true);
|
|
32
|
-
return
|
|
32
|
+
return pkgNameRegex.exec(pkgName)?.[1] || null;
|
|
33
33
|
}
|
|
34
34
|
export function getPkgSafeName() {
|
|
35
35
|
const pkgName = getPkgName(true);
|
|
36
|
-
const match =
|
|
36
|
+
const match = pkgNameRegex.exec(pkgName);
|
|
37
37
|
if (!match) {
|
|
38
38
|
return null;
|
|
39
39
|
}
|
package/src/run.d.ts
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
export declare function format(time?: Date): string;
|
|
2
|
-
|
|
2
|
+
/**
|
|
3
|
+
* Extract a human readable message from an unknown caught value.
|
|
4
|
+
*
|
|
5
|
+
* `catch` and promise rejections are typed `unknown`, and anything can be
|
|
6
|
+
* thrown. Interpolating such a value directly is unsafe: the common
|
|
7
|
+
* `${e instanceof Error && e.message}` shorthand renders the string "false"
|
|
8
|
+
* whenever the thrown value is not an Error, hiding the real cause.
|
|
9
|
+
*/
|
|
10
|
+
export declare function errorMessage(err: unknown): string;
|
|
11
|
+
/**
|
|
12
|
+
* Where `buildLog` output should be written. Either the name of one of the
|
|
13
|
+
* standard streams, any writable stream, or a function that receives the
|
|
14
|
+
* fully formatted message.
|
|
15
|
+
*/
|
|
16
|
+
export type BuildLogStream = 'stderr' | 'stdout' | NodeJS.WritableStream | ((msg: string) => void);
|
|
17
|
+
export interface BuildLogOptions {
|
|
18
|
+
/** Override the destination for this message only. */
|
|
19
|
+
stream?: BuildLogStream;
|
|
20
|
+
/** Timestamp to prefix the message with (defaults to now). */
|
|
21
|
+
time?: Date;
|
|
22
|
+
}
|
|
23
|
+
/** Change the destination used by `buildLog` when none is specified. */
|
|
24
|
+
export declare function setBuildLogStream(stream: BuildLogStream): void;
|
|
25
|
+
/** The destination currently used by `buildLog` when none is specified. */
|
|
26
|
+
export declare function getBuildLogStream(): BuildLogStream;
|
|
27
|
+
export declare function buildLog(msg: string, timeOrOptions?: BuildLogOptions | Date): void;
|
|
3
28
|
export type RunnableModule<Args extends string[], Result> = ((...args: Args) => Promise<Result>) | {
|
|
4
29
|
default: (...args: Args) => Promise<Result>;
|
|
5
30
|
};
|
package/src/run.js
CHANGED
|
@@ -1,16 +1,67 @@
|
|
|
1
1
|
export function format(time = new Date()) {
|
|
2
2
|
return time.toTimeString().replace(/.*(\d{2}:\d{2}:\d{2}).*/, '$1');
|
|
3
3
|
}
|
|
4
|
+
/**
|
|
5
|
+
* Extract a human readable message from an unknown caught value.
|
|
6
|
+
*
|
|
7
|
+
* `catch` and promise rejections are typed `unknown`, and anything can be
|
|
8
|
+
* thrown. Interpolating such a value directly is unsafe: the common
|
|
9
|
+
* `${e instanceof Error && e.message}` shorthand renders the string "false"
|
|
10
|
+
* whenever the thrown value is not an Error, hiding the real cause.
|
|
11
|
+
*/
|
|
12
|
+
export function errorMessage(err) {
|
|
13
|
+
if (err instanceof Error) {
|
|
14
|
+
return err.message;
|
|
15
|
+
}
|
|
16
|
+
if (typeof err === 'string') {
|
|
17
|
+
return err;
|
|
18
|
+
}
|
|
19
|
+
try {
|
|
20
|
+
return JSON.stringify(err) ?? String(err);
|
|
21
|
+
}
|
|
22
|
+
catch {
|
|
23
|
+
return String(err);
|
|
24
|
+
}
|
|
25
|
+
}
|
|
4
26
|
const silenceLogs = process.argv.includes('--silence-buildLog');
|
|
5
|
-
|
|
27
|
+
// stderr by default, so that stdout can be used for machine readable output.
|
|
28
|
+
let defaultStream = process.argv.includes('--buildLog-stdout')
|
|
29
|
+
? 'stdout'
|
|
30
|
+
: 'stderr';
|
|
31
|
+
/** Change the destination used by `buildLog` when none is specified. */
|
|
32
|
+
export function setBuildLogStream(stream) {
|
|
33
|
+
defaultStream = stream;
|
|
34
|
+
}
|
|
35
|
+
/** The destination currently used by `buildLog` when none is specified. */
|
|
36
|
+
export function getBuildLogStream() {
|
|
37
|
+
return defaultStream;
|
|
38
|
+
}
|
|
39
|
+
function writeLog(msg, stream) {
|
|
40
|
+
if (typeof stream === 'function') {
|
|
41
|
+
stream(msg);
|
|
42
|
+
}
|
|
43
|
+
else if (stream === 'stdout') {
|
|
44
|
+
process.stdout.write(`${msg}\n`);
|
|
45
|
+
}
|
|
46
|
+
else if (stream === 'stderr') {
|
|
47
|
+
process.stderr.write(`${msg}\n`);
|
|
48
|
+
}
|
|
49
|
+
else {
|
|
50
|
+
stream.write(`${msg}\n`);
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
export function buildLog(msg, timeOrOptions) {
|
|
6
54
|
if (silenceLogs) {
|
|
7
55
|
return;
|
|
8
56
|
}
|
|
9
|
-
|
|
57
|
+
const { stream = defaultStream, time } = timeOrOptions instanceof Date
|
|
58
|
+
? { time: timeOrOptions }
|
|
59
|
+
: (timeOrOptions ?? {});
|
|
60
|
+
writeLog(`[${format(time || new Date())}] ${msg}`, stream);
|
|
10
61
|
}
|
|
11
62
|
export async function run(fn, ...options) {
|
|
12
63
|
if (fn == null) {
|
|
13
|
-
throw new Error(`Invalid argument passed to run(${fn})`);
|
|
64
|
+
throw new Error(`Invalid argument passed to run(${String(fn)})`);
|
|
14
65
|
}
|
|
15
66
|
const task = typeof fn === 'function' ? fn : fn.default;
|
|
16
67
|
if (typeof task !== 'function') {
|
|
@@ -38,7 +89,7 @@ export async function runCli({ argv = process.argv, defaultAction = 'build', pas
|
|
|
38
89
|
? argv.slice(3)
|
|
39
90
|
: [];
|
|
40
91
|
return run(module, ...args).catch((err) => {
|
|
41
|
-
console.error(
|
|
92
|
+
console.error(err instanceof Error ? err.stack : err);
|
|
42
93
|
process.exit(1);
|
|
43
94
|
});
|
|
44
95
|
}
|
package/src/tgz.d.ts
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
import { Readable } from 'stream';
|
|
2
|
-
import {
|
|
3
|
-
export
|
|
2
|
+
import { type TarOptionsWithAliasesAsyncNoFile } from 'tar';
|
|
3
|
+
export interface ArtifactInfo {
|
|
4
4
|
contentType?: string;
|
|
5
5
|
md5: string;
|
|
6
6
|
sha1: string;
|
|
7
7
|
sha512: string;
|
|
8
8
|
size: number;
|
|
9
|
-
}
|
|
9
|
+
}
|
|
10
10
|
export declare const generateFileHash: (filePath: string, type: "md5" | "sha1" | "sha512") => Promise<string>;
|
|
11
11
|
export declare function getArtifactInfo(artifact: string | Pick<Readable, 'pipe'>): Promise<ArtifactInfo>;
|
|
12
|
-
export declare function tgzDir(srcDir: string, outPath: string, options?:
|
|
12
|
+
export declare function tgzDir(srcDir: string, outPath: string, options?: TarOptionsWithAliasesAsyncNoFile): Promise<ArtifactInfo>;
|
package/src/tgz.js
CHANGED
|
@@ -13,16 +13,24 @@ const generateHash = async (stream, type) => new Promise((resolve, reject) => {
|
|
|
13
13
|
.on('end', function onEnd() {
|
|
14
14
|
resolve(this.read());
|
|
15
15
|
})
|
|
16
|
-
.on('error', (err) =>
|
|
16
|
+
.on('error', (err) => {
|
|
17
|
+
reject(err);
|
|
18
|
+
});
|
|
17
19
|
});
|
|
18
20
|
export const generateFileHash = async (filePath, type) => generateHash(fs.createReadStream(filePath), type);
|
|
19
21
|
const countBytes = async (stream) => new Promise((resolve, reject) => {
|
|
20
22
|
const counter = new StreamCounter();
|
|
21
23
|
stream
|
|
22
24
|
.pipe(counter)
|
|
23
|
-
.on('finish', () =>
|
|
24
|
-
|
|
25
|
-
|
|
25
|
+
.on('finish', () => {
|
|
26
|
+
resolve(counter.bytes);
|
|
27
|
+
})
|
|
28
|
+
.on('end', () => {
|
|
29
|
+
resolve(counter.bytes);
|
|
30
|
+
})
|
|
31
|
+
.on('error', (err) => {
|
|
32
|
+
reject(err);
|
|
33
|
+
});
|
|
26
34
|
});
|
|
27
35
|
const writeStreamToFile = async (stream, filePath) => new Promise((resolve, reject) => {
|
|
28
36
|
stream
|
|
@@ -31,7 +39,9 @@ const writeStreamToFile = async (stream, filePath) => new Promise((resolve, reje
|
|
|
31
39
|
buildLog(`Successfully wrote file: ${filePath}`);
|
|
32
40
|
resolve();
|
|
33
41
|
})
|
|
34
|
-
.on('error', (err) =>
|
|
42
|
+
.on('error', (err) => {
|
|
43
|
+
reject(err);
|
|
44
|
+
});
|
|
35
45
|
});
|
|
36
46
|
// Take the given file (path or stream) and compute metadata for it.
|
|
37
47
|
// md5 + sha hashes, and size in bytes
|
|
@@ -58,7 +68,7 @@ export async function tgzDir(srcDir, outPath, options) {
|
|
|
58
68
|
// write the file to disk (outPath)
|
|
59
69
|
const [info] = await Promise.all([
|
|
60
70
|
getArtifactInfo(tgzStream),
|
|
61
|
-
writeStreamToFile(tgzStream,
|
|
71
|
+
writeStreamToFile(tgzStream, outPath),
|
|
62
72
|
]);
|
|
63
73
|
return {
|
|
64
74
|
...info,
|
package/src/version.d.ts
CHANGED
|
@@ -11,10 +11,10 @@ declare enum RepoType {
|
|
|
11
11
|
unknown = "unknown"
|
|
12
12
|
}
|
|
13
13
|
export declare function getRepoType(): Promise<RepoType>;
|
|
14
|
-
|
|
14
|
+
interface RepoInfo {
|
|
15
15
|
branch: null | string;
|
|
16
16
|
revision: string;
|
|
17
|
-
}
|
|
17
|
+
}
|
|
18
18
|
export declare function getRepoInfo(): Promise<RepoInfo>;
|
|
19
19
|
export interface SemverTag {
|
|
20
20
|
major: string;
|
package/src/version.js
CHANGED
|
@@ -4,7 +4,7 @@ import { getCfg, getPkg } from './pkg.js';
|
|
|
4
4
|
import { buildLog } from './run.js';
|
|
5
5
|
export function getBuild() {
|
|
6
6
|
const arg = process.argv.find((el) => /^--buildNum=\d+$/.test(el));
|
|
7
|
-
return
|
|
7
|
+
return arg?.substring(11) || process.env.BUILD_NUMBER || '0';
|
|
8
8
|
}
|
|
9
9
|
/**
|
|
10
10
|
* The one long-lived branch of the repository. All work happens on short-lived
|
|
@@ -21,9 +21,11 @@ var RepoType;
|
|
|
21
21
|
RepoType["hg"] = "hg";
|
|
22
22
|
RepoType["unknown"] = "unknown";
|
|
23
23
|
})(RepoType || (RepoType = {}));
|
|
24
|
-
|
|
24
|
+
// Returns a promise (rather than being `async`) because the body is synchronous
|
|
25
|
+
// but the exported signature is awaited by callers and must stay thenable.
|
|
26
|
+
export function getRepoType() {
|
|
25
27
|
const { repoType } = getCfg();
|
|
26
|
-
return (repoType ||
|
|
28
|
+
return Promise.resolve(repoType ||
|
|
27
29
|
(fs.existsSync('./.git')
|
|
28
30
|
? RepoType.git
|
|
29
31
|
: fs.existsSync('./.hg')
|
|
@@ -47,7 +49,7 @@ const semverTagRegex = /^v?(\d+)\.(\d+)\.(\d+)(?:-([0-9A-Za-z-]+(?:\.[0-9A-Za-z-
|
|
|
47
49
|
* semver version tag (an optional leading `v` is allowed).
|
|
48
50
|
*/
|
|
49
51
|
export function parseSemverTag(tag) {
|
|
50
|
-
const [, major, minor, patch, prerelease] =
|
|
52
|
+
const [, major, minor, patch, prerelease] = semverTagRegex.exec(tag) || [];
|
|
51
53
|
if (!major || !minor || !patch) {
|
|
52
54
|
return null;
|
|
53
55
|
}
|
|
@@ -113,7 +115,6 @@ export async function assertReleaseCommitIsOnMainBranch(mainBranch = getMainBran
|
|
|
113
115
|
const { revision } = await getRepoInfo();
|
|
114
116
|
const candidateRefs = [mainBranch, `origin/${mainBranch}`];
|
|
115
117
|
for (const ref of candidateRefs) {
|
|
116
|
-
// eslint-disable-next-line no-await-in-loop
|
|
117
118
|
if (await gitIsAncestor(revision, ref)) {
|
|
118
119
|
return;
|
|
119
120
|
}
|