build-strap 3.0.0-dev.42 → 4.0.0

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.
Files changed (130) hide show
  1. package/LICENSE +1 -1
  2. package/README.md +3 -34
  3. package/dist/src/ant.d.ts +19 -0
  4. package/dist/src/ant.js +18 -0
  5. package/dist/src/cert.d.ts +12 -0
  6. package/dist/src/cert.js +91 -0
  7. package/dist/src/clean.d.ts +4 -0
  8. package/dist/src/clean.js +11 -0
  9. package/dist/src/copy.d.ts +7 -0
  10. package/dist/src/copy.js +53 -0
  11. package/dist/src/cp.d.ts +17 -0
  12. package/dist/src/cp.js +88 -0
  13. package/dist/src/docker.container.d.ts +73 -0
  14. package/dist/src/docker.container.js +247 -0
  15. package/dist/src/docker.d.ts +68 -0
  16. package/dist/src/docker.js +188 -0
  17. package/dist/src/docker.network.d.ts +42 -0
  18. package/dist/src/docker.network.js +85 -0
  19. package/dist/src/docker.volume.d.ts +26 -0
  20. package/dist/src/docker.volume.js +62 -0
  21. package/dist/src/dockerCleanup.d.ts +38 -0
  22. package/dist/src/dockerCleanup.js +77 -0
  23. package/dist/src/dockerCompose.d.ts +154 -0
  24. package/dist/src/dockerCompose.js +439 -0
  25. package/dist/src/fs.d.ts +5 -0
  26. package/dist/src/fs.js +38 -0
  27. package/dist/src/git.d.ts +9 -0
  28. package/dist/src/git.js +24 -0
  29. package/dist/src/index.d.ts +20 -0
  30. package/dist/src/index.js +20 -0
  31. package/dist/src/maps.d.ts +33 -0
  32. package/dist/src/maps.js +41 -0
  33. package/dist/src/npm.d.ts +35 -0
  34. package/dist/src/npm.js +104 -0
  35. package/dist/src/pkg.d.ts +33 -0
  36. package/dist/src/pkg.js +33 -0
  37. package/dist/src/publish.d.ts +27 -0
  38. package/dist/src/publish.js +57 -0
  39. package/dist/src/run.d.ts +13 -0
  40. package/dist/src/run.js +44 -0
  41. package/dist/src/tgz.d.ts +13 -0
  42. package/dist/src/tgz.js +67 -0
  43. package/dist/src/version.d.ts +34 -0
  44. package/dist/src/version.js +101 -0
  45. package/dist/src/yarn.d.ts +26 -0
  46. package/dist/src/yarn.js +75 -0
  47. package/package.json +74 -20
  48. package/ant.js +0 -43
  49. package/ant.js.flow +0 -42
  50. package/artifactory.js +0 -617
  51. package/artifactory.js.flow +0 -616
  52. package/cert.conf +0 -20
  53. package/cert.js +0 -153
  54. package/cert.js.flow +0 -152
  55. package/clean.js +0 -20
  56. package/clean.js.flow +0 -19
  57. package/copy.js +0 -80
  58. package/copy.js.flow +0 -79
  59. package/cp.js +0 -173
  60. package/cp.js.flow +0 -172
  61. package/docker.container.js +0 -390
  62. package/docker.container.js.flow +0 -389
  63. package/docker.js +0 -368
  64. package/docker.js.flow +0 -367
  65. package/docker.network.js +0 -160
  66. package/docker.network.js.flow +0 -159
  67. package/docker.volume.js +0 -136
  68. package/docker.volume.js.flow +0 -136
  69. package/dockerCleanup.js +0 -152
  70. package/dockerCleanup.js.flow +0 -151
  71. package/dockerCompose.js +0 -766
  72. package/dockerCompose.js.flow +0 -765
  73. package/flow.js +0 -21
  74. package/flow.js.flow +0 -20
  75. package/fs.js +0 -95
  76. package/fs.js.flow +0 -96
  77. package/git.js +0 -35
  78. package/git.js.flow +0 -34
  79. package/hg.js +0 -35
  80. package/hg.js.flow +0 -34
  81. package/index.js +0 -27
  82. package/index.js.flow +0 -26
  83. package/inline-resources.js +0 -158
  84. package/inline-resources.js.flow +0 -157
  85. package/maps.js +0 -112
  86. package/maps.js.flow +0 -111
  87. package/npm.js +0 -145
  88. package/npm.js.flow +0 -144
  89. package/pkg.js +0 -81
  90. package/pkg.js.flow +0 -80
  91. package/publish.js +0 -145
  92. package/publish.js.flow +0 -144
  93. package/run.js +0 -90
  94. package/run.js.flow +0 -89
  95. package/src/ant.js +0 -42
  96. package/src/artifactory.js +0 -616
  97. package/src/cert.conf +0 -20
  98. package/src/cert.js +0 -152
  99. package/src/clean.js +0 -19
  100. package/src/copy.js +0 -79
  101. package/src/cp.js +0 -172
  102. package/src/docker.container.js +0 -389
  103. package/src/docker.js +0 -367
  104. package/src/docker.network.js +0 -159
  105. package/src/docker.volume.js +0 -136
  106. package/src/dockerCleanup.js +0 -151
  107. package/src/dockerCompose.js +0 -765
  108. package/src/flow.js +0 -20
  109. package/src/fs.js +0 -96
  110. package/src/git.js +0 -34
  111. package/src/hg.js +0 -34
  112. package/src/index.js +0 -26
  113. package/src/inline-resources.js +0 -157
  114. package/src/maps.js +0 -111
  115. package/src/npm.js +0 -144
  116. package/src/pkg.js +0 -80
  117. package/src/publish.js +0 -144
  118. package/src/run.js +0 -89
  119. package/src/test.js +0 -55
  120. package/src/tgz.js +0 -101
  121. package/src/version.js +0 -141
  122. package/src/yarn.js +0 -123
  123. package/test.js +0 -56
  124. package/test.js.flow +0 -55
  125. package/tgz.js +0 -102
  126. package/tgz.js.flow +0 -101
  127. package/version.js +0 -142
  128. package/version.js.flow +0 -141
  129. package/yarn.js +0 -124
  130. package/yarn.js.flow +0 -123
@@ -0,0 +1,247 @@
1
+ import bytes from 'bytes';
2
+ import { throttle } from 'lodash-es';
3
+ import { spawn } from './cp.js';
4
+ import { parseDockerDate } from './docker.js';
5
+ import { buildLog } from './run.js';
6
+ // calling this function in rapid succession can lead to errors.
7
+ // prefer throttled version
8
+ export async function unthrottledDockerContainerLs({ all, filter, } = {}) {
9
+ return (await spawn('docker', [
10
+ 'container',
11
+ 'ls',
12
+ ...(all ? ['--all'] : []),
13
+ '--no-trunc',
14
+ '--format',
15
+ '{{json .}}',
16
+ ], { captureOutput: true })).output
17
+ .split('\n')
18
+ .filter((line) => !!line.trim())
19
+ .map((line) => {
20
+ const { Command: command, CreatedAt, ID: id, Image: image, Labels, Mounts, Names, Networks, Ports, RunningFor: runningFor, Size, Status: status, } = JSON.parse(line.trim());
21
+ const names = Names.split(',').map((n) => n.trim());
22
+ const [name] = names;
23
+ return {
24
+ command,
25
+ created: parseDockerDate(CreatedAt),
26
+ async exists() {
27
+ try {
28
+ return !!(await this.inspect());
29
+ }
30
+ catch {
31
+ return false;
32
+ }
33
+ },
34
+ exited: /^Exited/.test(status),
35
+ id,
36
+ image,
37
+ inspect: async () => (await dockerContainerInspect(id))?.[0],
38
+ async isRunning() {
39
+ try {
40
+ return (await this.inspect())?.State?.Running || false;
41
+ }
42
+ catch {
43
+ return false;
44
+ }
45
+ },
46
+ kill: async (ignoreError = true) => dockerContainerKill(id, ignoreError),
47
+ labels: Labels.split(',').map((l) => l.trim()),
48
+ mounts: Mounts.split(',').map((m) => m.trim()),
49
+ name,
50
+ names,
51
+ networks: Networks.split(',').map((n) => n.trim()),
52
+ ports: Ports.split(',').map((p) => {
53
+ const [, iface, src, dest] = p.match(/^([^:]+):(\d+)-\\u003e(\d+)/i) || [];
54
+ return {
55
+ dest: parseInt(dest, 10),
56
+ iface,
57
+ src: parseInt(src, 10),
58
+ };
59
+ }),
60
+ restart: async () => dockerContainerRestart(id),
61
+ rm: async (ignoreError = true) => dockerContainerRm(id, ignoreError),
62
+ runningFor,
63
+ size: bytes.parse(Size),
64
+ start: async () => dockerContainerStart(id),
65
+ status,
66
+ stop: async (ignoreError = true) => dockerContainerStop(id, ignoreError),
67
+ async teardown(verbose = true) {
68
+ const log = (msg) => verbose && buildLog(msg);
69
+ if (await this.isRunning()) {
70
+ try {
71
+ log(`Stopping container: <${this.name}>...`);
72
+ await this.stop(false);
73
+ log(`<${this.name}> container stopped.`);
74
+ }
75
+ catch (e1) {
76
+ log(`Failed to stop <${this.name}> container: ${e1.message}`);
77
+ try {
78
+ log(`Killing container: <${this.name}>...`);
79
+ await this.kill(false);
80
+ log(`<${this.name}> container killed.`);
81
+ }
82
+ catch (e2) {
83
+ log(`Failed to kill <${this.name}> container: ${e2.message}`);
84
+ }
85
+ }
86
+ }
87
+ else {
88
+ log(`<${this.name}> container not running, skipping stop.`);
89
+ }
90
+ if (await this.exists()) {
91
+ await this.rm(true);
92
+ }
93
+ else {
94
+ log(`<${this.name}> container does not exist, skipping rm.`);
95
+ }
96
+ },
97
+ };
98
+ })
99
+ .filter((c) => c.id && (typeof filter === 'function' ? filter(c) : true));
100
+ }
101
+ export const dockerContainerLs = throttle(unthrottledDockerContainerLs, 500, {
102
+ trailing: false,
103
+ });
104
+ export async function dockerContainerFind(search, options) {
105
+ return ((await dockerContainerLs(options))?.find((c) => c.name === search ||
106
+ c.id.startsWith(search) ||
107
+ c.names.includes(search)) || null);
108
+ }
109
+ export async function dockerContainerStop(id, ignoreErrors = true) {
110
+ const ids = Array.isArray(id) ? id : [id];
111
+ try {
112
+ await spawn('docker', ['container', 'stop', ...ids]);
113
+ }
114
+ catch (e) {
115
+ if (!ignoreErrors) {
116
+ throw new Error(`Failed to stop container(s): ${e.message}`);
117
+ }
118
+ }
119
+ }
120
+ export async function dockerContainerKill(id, ignoreErrors = true) {
121
+ const ids = Array.isArray(id) ? id : [id];
122
+ try {
123
+ await spawn('docker', ['container', 'kill', ...ids]);
124
+ }
125
+ catch (e) {
126
+ if (!ignoreErrors) {
127
+ throw new Error(`Failed to kill container(s): ${e.message}`);
128
+ }
129
+ }
130
+ }
131
+ export async function dockerTryStopContainer(id, name = '') {
132
+ if (id) {
133
+ try {
134
+ await dockerContainerStop(id, false);
135
+ }
136
+ catch (e) {
137
+ buildLog(`Failed to stop ${name} container: ${e.message}`);
138
+ }
139
+ }
140
+ }
141
+ export async function dockerContainerRm(id, ignoreErrors = true) {
142
+ const ids = Array.isArray(id) ? id : [id];
143
+ try {
144
+ await spawn('docker', ['container', 'rm', ...ids]);
145
+ }
146
+ catch (e) {
147
+ if (ignoreErrors) {
148
+ buildLog(`Warning (ignored Error): Failed to remove container(s): ${e.message}`);
149
+ }
150
+ else {
151
+ throw new Error(`Failed to remove container(s): ${e.message}`);
152
+ }
153
+ }
154
+ }
155
+ export async function dockerContainerRestart(id) {
156
+ const ids = Array.isArray(id) ? id : [id];
157
+ await spawn('docker', ['container', 'restart', ...ids]);
158
+ }
159
+ export async function dockerContainerStart(id) {
160
+ const ids = Array.isArray(id) ? id : [id];
161
+ await spawn('docker', ['container', 'start', ...ids]);
162
+ }
163
+ export async function unthrottledDockerContainerInspect(id) {
164
+ const ids = Array.isArray(id) ? id : [id];
165
+ return ((await spawn('docker', ['container', 'inspect', '--format', '{{json .}}', ...ids], { captureOutput: true })).output
166
+ .split('\n')
167
+ .filter((line) => !!line.trim())
168
+ .map((line) => JSON.parse(line.trim())) || []);
169
+ }
170
+ export const dockerContainerInspect = throttle(unthrottledDockerContainerInspect, 500, {
171
+ trailing: false,
172
+ });
173
+ export async function dockerContainerRun({ image, runArgs = [], cmd = [], spawnOptions, }) {
174
+ return (await spawn('docker', ['container', 'run', ...runArgs, image, ...cmd], {
175
+ pipeOutput: true,
176
+ stdio: 'inherit',
177
+ ...spawnOptions,
178
+ })).output;
179
+ }
180
+ export async function dockerContainerRunDaemon({ image, runArgs = [], cmd = [], waitAttempts = 10, waitDuration = 501, }) {
181
+ const id = (await dockerContainerRun({
182
+ cmd,
183
+ image,
184
+ runArgs: ['-d', ...runArgs],
185
+ spawnOptions: {
186
+ captureOutput: true,
187
+ pipeOutput: false,
188
+ stdio: 'pipe',
189
+ },
190
+ })).trim();
191
+ let container = await dockerContainerFind(id);
192
+ let attempts = 0;
193
+ // containers don't always show up in the list right away
194
+ /* eslint-disable no-await-in-loop */
195
+ while (!container && attempts < waitAttempts) {
196
+ await new Promise((resolve) => {
197
+ setTimeout(resolve, waitDuration);
198
+ });
199
+ container = await dockerContainerFind(id);
200
+ attempts += 1;
201
+ }
202
+ /* eslint-enable no-await-in-loop */
203
+ if (!container) {
204
+ throw new Error(`Failed to find newly created container: ${id}`);
205
+ }
206
+ return container;
207
+ }
208
+ const indicators = ['.', ':', '*', '+', '-', '=', '%', '$', '@', '^', '&'];
209
+ let curIndicator = 0;
210
+ /**
211
+ * Waits for the given container to be fully started. Checks that the container is
212
+ * running, and test with the provided testFn every second until it returns true.
213
+ * @param {DockerContainer} container The container to test
214
+ * @param {Function} testFn A function that validates that the container is ready (e.g. by calling an API on the container)
215
+ * @param {number} timeoutMs The amount of time to wait between attempts.
216
+ * @param {number} maxAttempts The maximum number of attempts before giving up (rejects promise)
217
+ */
218
+ export const dockerContainerWaitForStart = async (container, testFn, timeoutMs = 1000, maxAttempts = 1200) => {
219
+ // claim an indicator
220
+ const indicator = indicators[curIndicator] || '.';
221
+ curIndicator = (curIndicator + 1) % indicators.length;
222
+ const { name } = container;
223
+ buildLog(`Waiting for ${name} to fully start... ${indicator}`);
224
+ await new Promise((resolve, reject) => {
225
+ const check = (tries = 0) => {
226
+ process.stdout.write(indicator);
227
+ setTimeout(async () => {
228
+ try {
229
+ if (!(await container.isRunning())) {
230
+ return reject(new Error(`The '${name}' container is no longer running.`));
231
+ }
232
+ if (await testFn(container)) {
233
+ return resolve();
234
+ }
235
+ }
236
+ catch (err) {
237
+ // ignore
238
+ }
239
+ return tries < maxAttempts
240
+ ? check(tries + 1)
241
+ : reject(new Error(`Timeout waiting for '${name}' container to start.`));
242
+ }, timeoutMs);
243
+ };
244
+ check();
245
+ });
246
+ process.stdout.write('\n');
247
+ };
@@ -0,0 +1,68 @@
1
+ export interface DockerConfig {
2
+ name?: string;
3
+ registry?: string;
4
+ repository?: string;
5
+ }
6
+ export declare function getDockerConfig(): DockerConfig;
7
+ export declare const parseDockerDate: (date: string) => Date;
8
+ export declare function dockerBuild(tags?: string[], buildArgs?: string[], target?: string | null, extraArgs?: string[], repo?: string, dockerFile?: string, workDir?: string): Promise<string>;
9
+ export declare function dockerTag(imageId: string, tags?: string[], repo?: string): Promise<void>;
10
+ /**
11
+ * Apply the standard convention for tagging the docker image for the project,
12
+ * based on the source control branch being built.
13
+ * For releases, applies `latest`, `M.m.p`, `M.m`, `M`
14
+ * For release candidates, applies `latest-rc`
15
+ * For feature branches, applies `latest-feature`
16
+ * For development branch, applies `latest-dev`
17
+ * All other branches, no tag is applies
18
+ * @param {string} imageId id of the docker image to which to apply the tags
19
+ * @param {string} repo optional override of the docker repo url to use (derived from package.json by default)
20
+ */
21
+ export declare function dockerTagVersion(imageId: string, repo?: string): Promise<void>;
22
+ export interface DockerImage {
23
+ created: Date;
24
+ digest: string;
25
+ id: string;
26
+ repository: string;
27
+ size: number;
28
+ tag: string;
29
+ }
30
+ export declare type DockerImageFilter = (i: DockerImage) => boolean;
31
+ export declare function dockerImages(repo?: null | string, filter?: null | DockerImageFilter): Promise<DockerImage[]>;
32
+ export declare function getDockerId(tag?: string, repo?: null | string): Promise<string>;
33
+ export declare function getUntaggedDockerIds(repo?: string, filter?: null | DockerImageFilter): Promise<string[]>;
34
+ export declare function getDockerTags(id?: null | string, repo?: null | string, fullName?: boolean, filter?: null | DockerImageFilter): Promise<string[]>;
35
+ export declare function getDockerDigest(imageId: string, repo?: string): Promise<string>;
36
+ interface DockerLoginOptions {
37
+ password?: string;
38
+ registry?: string;
39
+ user?: string;
40
+ }
41
+ export declare function dockerLogin({ password, registry, user, }?: DockerLoginOptions): Promise<void>;
42
+ export declare function dockerPush(tags?: string[], repo?: string): Promise<void>;
43
+ export interface DockerPullOptions {
44
+ image: string;
45
+ offline?: boolean;
46
+ testUrl?: string | null;
47
+ }
48
+ export declare function dockerPull({ image, offline, testUrl, }: DockerPullOptions): Promise<void>;
49
+ export declare function dockerRmi(images?: string[], ignoreErrors?: boolean): Promise<void>;
50
+ export declare function getDockerRepo(): string;
51
+ export interface PullAndRunContainerOptions extends DockerPullOptions {
52
+ alias?: string | null;
53
+ cmd?: string[];
54
+ network?: string | null;
55
+ runArgs?: string[];
56
+ }
57
+ export declare function dockerPullAndRunContainer(options: PullAndRunContainerOptions): Promise<string>;
58
+ export interface CopyFilePathConfig {
59
+ from: string;
60
+ to: string;
61
+ }
62
+ export interface CopyFilesFromDockerImageConfig {
63
+ filePaths: CopyFilePathConfig[];
64
+ ignoreErrors?: boolean;
65
+ imageId: string;
66
+ }
67
+ export declare function copyFilesFromDockerImage({ filePaths, ignoreErrors, imageId, }: CopyFilesFromDockerImageConfig): Promise<any>;
68
+ export {};
@@ -0,0 +1,188 @@
1
+ import bytes from 'bytes';
2
+ import isReachable from 'is-reachable';
3
+ import moment from 'moment';
4
+ import path from 'path';
5
+ import { spawn } from './cp.js';
6
+ import { dockerContainerKill, dockerContainerRunDaemon, } from './docker.container.js';
7
+ import { dockerNetworkConnect } from './docker.network.js';
8
+ import { getCfg, getPkgName, getPkgScope } from './pkg.js';
9
+ import { buildLog } from './run.js';
10
+ import { getDevBranch, getVersion } from './version.js';
11
+ export function getDockerConfig() {
12
+ return getCfg().docker || {};
13
+ }
14
+ export const parseDockerDate = (date) => moment(date, 'YYYY-MM-DD HH:mm:ss ZZ').toDate();
15
+ export async function dockerBuild(tags = ['latest'], buildArgs = [], target = null, extraArgs = [], repo = getDockerRepo(), dockerFile = './Dockerfile', workDir = '.') {
16
+ const args = [
17
+ 'build',
18
+ ...['-f', path.resolve(dockerFile)],
19
+ ...(target ? ['--target', target] : []),
20
+ ...tags.reduce((ar, t) => [...ar, '-t', `${repo}:${t}`], []),
21
+ ...buildArgs.reduce((ar, a) => [...ar, '--build-arg', a], []),
22
+ ...(extraArgs || []),
23
+ path.resolve(workDir),
24
+ ];
25
+ return (await spawn('docker', args, { stdio: 'inherit' })).output;
26
+ }
27
+ export async function dockerTag(imageId, tags = ['latest'], repo = getDockerRepo()) {
28
+ await Promise.all(tags.map(async (t) => {
29
+ await spawn('docker', ['tag', imageId, `${repo}:${t}`]);
30
+ buildLog(`Tagged image ${imageId} as ${repo}:${t}`);
31
+ }));
32
+ }
33
+ /**
34
+ * Apply the standard convention for tagging the docker image for the project,
35
+ * based on the source control branch being built.
36
+ * For releases, applies `latest`, `M.m.p`, `M.m`, `M`
37
+ * For release candidates, applies `latest-rc`
38
+ * For feature branches, applies `latest-feature`
39
+ * For development branch, applies `latest-dev`
40
+ * All other branches, no tag is applies
41
+ * @param {string} imageId id of the docker image to which to apply the tags
42
+ * @param {string} repo optional override of the docker repo url to use (derived from package.json by default)
43
+ */
44
+ export async function dockerTagVersion(imageId, repo = getDockerRepo()) {
45
+ const { branch, isRelease, major, minor, patch } = await getVersion();
46
+ // determine what tags to apply
47
+ if (isRelease) {
48
+ await dockerTag(imageId, ['latest', `${major}`, `${major}.${minor}`, `${major}.${minor}.${patch}`], repo);
49
+ }
50
+ else if (branch === (await getDevBranch())) {
51
+ await dockerTag(imageId, ['latest-dev'], repo);
52
+ }
53
+ else if (branch.match(/^(release|patch)-/)) {
54
+ await dockerTag(imageId, ['latest-rc'], repo);
55
+ }
56
+ else if (branch.match(/^feature-/)) {
57
+ await dockerTag(imageId, ['latest-feature'], repo);
58
+ }
59
+ }
60
+ export async function dockerImages(repo = getDockerRepo(), filter = null) {
61
+ return (await spawn('docker', ['images', '--format', `{{json .}}`, ...(repo ? [repo] : [])], { captureOutput: true })).output
62
+ .split('\n')
63
+ .filter((line) => !!line.trim())
64
+ .map((l) => {
65
+ const { CreatedAt, Digest: digest, ID: id, Repository: repository, Size, Tag: tag, } = JSON.parse(l.trim());
66
+ return {
67
+ created: parseDockerDate(CreatedAt),
68
+ digest,
69
+ id,
70
+ repository,
71
+ size: bytes.parse(Size),
72
+ tag,
73
+ };
74
+ })
75
+ .filter((m) => (m && m.id) != null &&
76
+ (typeof filter === 'function' ? filter(m) : true));
77
+ }
78
+ export async function getDockerId(tag = 'latest', repo = null) {
79
+ return (await dockerImages(repo, (m) => m.tag === tag)).map((m) => m.id)[0];
80
+ }
81
+ export async function getUntaggedDockerIds(repo, filter = null) {
82
+ return (await dockerImages(repo, (m) => m.tag === '<none>' && (typeof filter === 'function' ? filter(m) : true))).map((m) => m.id);
83
+ }
84
+ export async function getDockerTags(id, repo, fullName = false, filter = null) {
85
+ return (await dockerImages(repo, (m) => (!id || m.id === id) &&
86
+ (typeof filter === 'function' ? filter(m) : true))).map((m) => (fullName ? `${m.repository}:${m.tag}` : m.tag));
87
+ }
88
+ export async function getDockerDigest(imageId, repo) {
89
+ return (await dockerImages(repo, (m) => m.id === imageId)).map((m) => m.digest)[0];
90
+ }
91
+ export async function dockerLogin({ password = process.env.DOCKER_PASSWORD, registry = process.env.DOCKER_REGISTRY, user = process.env.DOCKER_PASSWORD, } = {}) {
92
+ const reg = registry || getDockerConfig().registry;
93
+ await spawn('docker', ['login', '-u', user || '', '-p', password || '', ...(reg ? [reg] : [])], { stdio: 'inherit' });
94
+ }
95
+ export async function dockerPush(tags = ['latest'], repo = getDockerRepo()) {
96
+ // do this sequentially, so you don't push the same image simultaneously
97
+ await tags.reduce(async (prev, tag) => {
98
+ await prev;
99
+ await spawn('docker', ['push', `${repo}:${tag}`], { stdio: 'inherit' });
100
+ }, Promise.resolve());
101
+ }
102
+ export async function dockerPull({ image, offline = process.argv.includes('--offline'), testUrl, }) {
103
+ if (offline || !(await isReachable(testUrl || 'https://hub.docker.com'))) {
104
+ if (offline === false) {
105
+ throw new Error('Offline, cannot docker pull');
106
+ }
107
+ buildLog('It looks like you are offline, skipping docker pull');
108
+ }
109
+ else {
110
+ // throws on failure
111
+ await spawn('docker', ['pull', image], { stdio: 'inherit' });
112
+ }
113
+ }
114
+ export async function dockerRmi(images = [], ignoreErrors = true) {
115
+ // must do these sequentially, or they will interfere with each other
116
+ await images.reduce(async (prev, image) => {
117
+ await prev;
118
+ await spawn('docker', ['rmi', image], { stdio: 'inherit' }).catch((err) => {
119
+ if (ignoreErrors) {
120
+ buildLog(`Warning (ignored Error): Failed to rm image(s): ${err.message}`);
121
+ }
122
+ else {
123
+ throw err;
124
+ }
125
+ });
126
+ }, Promise.resolve());
127
+ }
128
+ export function getDockerRepo() {
129
+ const { name: dkrName, registry, repository: dkrRepo } = getDockerConfig();
130
+ const repository = dkrRepo || getPkgScope();
131
+ const name = dkrName || getPkgName(false);
132
+ if (!repository || !name) {
133
+ throw new Error('Docker configuration is missing or incomplete in package.json');
134
+ }
135
+ return `${registry ? `${registry}/` : ''}${repository}/${name}`;
136
+ }
137
+ export async function dockerPullAndRunContainer(options) {
138
+ const { image, runArgs = [], cmd = [], network, alias, offline = process.argv.includes('--offline'), testUrl, } = options;
139
+ try {
140
+ await dockerPull({ image, offline, testUrl });
141
+ }
142
+ catch (err) {
143
+ buildLog(`docker pull failed (${err.message}), attempting to continue...`);
144
+ }
145
+ const { id } = await dockerContainerRunDaemon({ cmd, image, runArgs });
146
+ if (network) {
147
+ await dockerNetworkConnect(network, id, alias);
148
+ }
149
+ return id;
150
+ }
151
+ export async function copyFilesFromDockerImage({ filePaths, ignoreErrors, imageId, }) {
152
+ const container = await dockerContainerRunDaemon({
153
+ cmd: ['read', '-p', 'pause'],
154
+ image: imageId,
155
+ runArgs: ['-it', '--rm', '--entrypoint=/bin/bash'],
156
+ });
157
+ buildLog(`Copying Files from Docker Image - imageId (${imageId})`);
158
+ const spawnOptions = {
159
+ captureOutput: false,
160
+ pipeOutput: true,
161
+ stdio: 'inherit',
162
+ };
163
+ const copyErrors = [];
164
+ try {
165
+ filePaths.forEach(async (fp) => {
166
+ // Try to copy from the Debug Folder
167
+ try {
168
+ await spawn('docker', ['cp', `${container.id}:${fp.from}`, fp.to], spawnOptions);
169
+ buildLog(`Copied ${fp.from} => ${fp.to}`);
170
+ }
171
+ catch (error) {
172
+ copyErrors.push(error);
173
+ buildLog(`Error copying file ${fp.from}`);
174
+ }
175
+ });
176
+ }
177
+ finally {
178
+ try {
179
+ await dockerContainerKill(container.id);
180
+ }
181
+ catch (error) {
182
+ copyErrors.push(error);
183
+ }
184
+ }
185
+ if (copyErrors.length > 0 && ignoreErrors !== true) {
186
+ throw new Error('Some files failed to copy');
187
+ }
188
+ }
@@ -0,0 +1,42 @@
1
+ export interface DockerNetwork {
2
+ IPv6: boolean;
3
+ created: Date;
4
+ driver: string;
5
+ exists: () => Promise<boolean>;
6
+ id: string;
7
+ labels: string[];
8
+ name: string;
9
+ rm: () => Promise<void>;
10
+ scope: string;
11
+ }
12
+ interface DockerNetworkLsOptions {
13
+ filter?: (network: DockerNetwork) => boolean;
14
+ }
15
+ export declare function unthrottledDockerNetworkLs({ filter, }?: DockerNetworkLsOptions): Promise<DockerNetwork[]>;
16
+ export declare const dockerNetworkLs: (options?: DockerNetworkLsOptions) => Promise<DockerNetwork[]> | undefined;
17
+ export declare function dockerNetworkFind(networkName: string, options?: DockerNetworkLsOptions): Promise<DockerNetwork | null>;
18
+ export interface DockerNetworkCreateOptions {
19
+ auxAddresses?: string[];
20
+ driver?: string;
21
+ driverOpts?: Record<string, string>;
22
+ gateways?: string[];
23
+ ingress?: boolean;
24
+ internal?: boolean;
25
+ ipRange?: string;
26
+ ipv6?: boolean;
27
+ labels?: string[];
28
+ subnets?: string[];
29
+ }
30
+ export declare function dockerNetworkCreate(networkName: string, { driver, driverOpts, ipRange, gateways, subnets, auxAddresses, ingress, internal, ipv6, labels, }?: DockerNetworkCreateOptions): Promise<DockerNetwork | null>;
31
+ /**
32
+ * Removes a network by id
33
+ * @param {string} id The id of the network
34
+ */
35
+ export declare function dockerNetworkRm(id: string): Promise<void>;
36
+ /**
37
+ * Removes all networks with the given name
38
+ * @param {string} networkName The name of the network
39
+ */
40
+ export declare function dockerNetworkDelete(networkName: string): Promise<void>;
41
+ export declare function dockerNetworkConnect(networkName: string, containerId: string, alias?: string | null): Promise<void>;
42
+ export {};
@@ -0,0 +1,85 @@
1
+ import { throttle } from 'lodash-es';
2
+ import { spawn } from './cp.js';
3
+ import { parseDockerDate } from './docker.js';
4
+ // calling this function in rapid succession can lead to errors.
5
+ // prefer throttled version
6
+ export async function unthrottledDockerNetworkLs({ filter, } = {}) {
7
+ return (await spawn('docker', ['network', 'ls', '--format', '{{json .}}'], {
8
+ captureOutput: true,
9
+ })).output
10
+ .split('\n')
11
+ .filter((line) => !!line.trim())
12
+ .map((line) => {
13
+ const { CreatedAt, Driver: driver, ID: id, IPv6, Labels, Name: name, Scope: scope, } = JSON.parse(line.trim());
14
+ return {
15
+ IPv6: IPv6 === 'true',
16
+ created: parseDockerDate(CreatedAt),
17
+ driver,
18
+ exists: async () => !!(await dockerNetworkFind(id)),
19
+ id,
20
+ labels: Labels.split(',').map((l) => l.trim()),
21
+ name,
22
+ rm: async () => dockerNetworkRm(id),
23
+ scope,
24
+ };
25
+ })
26
+ .filter((n) => n.id && (typeof filter === 'function' ? filter(n) : true));
27
+ }
28
+ export const dockerNetworkLs = throttle(unthrottledDockerNetworkLs, 500, {
29
+ trailing: false,
30
+ });
31
+ export async function dockerNetworkFind(networkName, options) {
32
+ return ((await dockerNetworkLs(options))?.find((n) => n.name === networkName || n.id === networkName) || null);
33
+ }
34
+ export async function dockerNetworkCreate(networkName, { driver, driverOpts = {}, ipRange, gateways = [], subnets = [], auxAddresses = [], ingress, internal, ipv6, labels = [], } = {}) {
35
+ const existing = await dockerNetworkFind(networkName);
36
+ if (existing)
37
+ return existing;
38
+ await spawn('docker', [
39
+ 'network',
40
+ 'create',
41
+ ...labels.flatMap((l) => ['--label', l]),
42
+ ...gateways.flatMap((g) => ['--gateway', g]),
43
+ ...subnets.flatMap((s) => ['--subnet', s]),
44
+ ...auxAddresses.flatMap((a) => ['--aux-address', a]),
45
+ ...(ipRange ? ['--ip-range', ipRange] : []),
46
+ ...(ingress ? ['--ingress'] : []),
47
+ ...(internal ? ['--internal'] : []),
48
+ ...(ipv6 ? ['--ipv6'] : []),
49
+ ...(driver ? ['-d', driver] : []),
50
+ ...Object.entries(driverOpts).flatMap(([k, v]) => ['-o', `${k}=${v}`]),
51
+ networkName,
52
+ ]);
53
+ return dockerNetworkFind(networkName);
54
+ }
55
+ /**
56
+ * Removes a network by id
57
+ * @param {string} id The id of the network
58
+ */
59
+ export async function dockerNetworkRm(id) {
60
+ await spawn('docker', ['network', 'rm', id]);
61
+ }
62
+ /**
63
+ * Removes all networks with the given name
64
+ * @param {string} networkName The name of the network
65
+ */
66
+ export async function dockerNetworkDelete(networkName) {
67
+ let existing;
68
+ // eslint-disable-next-line no-cond-assign, no-await-in-loop
69
+ while ((existing = await dockerNetworkFind(networkName))) {
70
+ // eslint-disable-next-line no-await-in-loop
71
+ await existing.rm();
72
+ }
73
+ }
74
+ export async function dockerNetworkConnect(networkName, containerId, alias) {
75
+ const net = await dockerNetworkCreate(networkName);
76
+ if (!net)
77
+ throw new Error('Failed to create docker network');
78
+ await spawn('docker', [
79
+ 'network',
80
+ 'connect',
81
+ ...(alias ? ['--alias', alias] : []),
82
+ net.id,
83
+ containerId,
84
+ ]);
85
+ }
@@ -0,0 +1,26 @@
1
+ declare type DockerVolumeInspectOutput = Record<string, any>;
2
+ export interface DockerVolume {
3
+ driver: string;
4
+ inspect: () => Promise<null | DockerVolumeInspectOutput>;
5
+ labels: string[];
6
+ mountPoint: string;
7
+ name: string;
8
+ rm: () => Promise<void>;
9
+ scope: string;
10
+ }
11
+ interface DockerVolumeLsOptions {
12
+ filter?: (vol: DockerVolume) => boolean;
13
+ }
14
+ export declare function unthrottledDockerVolumeLs({ filter, }?: DockerVolumeLsOptions): Promise<DockerVolume[]>;
15
+ export declare const dockerVolumeLs: (options?: DockerVolumeLsOptions) => undefined | Promise<DockerVolume[]>;
16
+ export declare function dockerVolumeFind(search: string, options?: DockerVolumeLsOptions): Promise<null | DockerVolume>;
17
+ export interface DockerVolumeCreateOptions {
18
+ driver?: string;
19
+ driverOpts?: Record<string, string>;
20
+ labels?: string[];
21
+ }
22
+ export declare function dockerVolumeCreate(name: string, { labels, driver, driverOpts }?: DockerVolumeCreateOptions): Promise<null | DockerVolume>;
23
+ export declare function dockerVolumeRm(id: string | string[], ignoreErrors?: boolean): Promise<void>;
24
+ export declare function unthrottledDockerVolumeInspect(id: string | string[]): Promise<DockerVolumeInspectOutput[]>;
25
+ export declare const dockerVolumeInspect: (id: string | string[]) => undefined | Promise<DockerVolumeInspectOutput[]>;
26
+ export {};