Skip to content

Commit 38b4580

Browse files
committed
Standalone mode support
Signed-off-by: CrazyMax <[email protected]>
1 parent ba31738 commit 38b4580

File tree

10 files changed

+152
-31
lines changed

10 files changed

+152
-31
lines changed

.github/workflows/ci.yml

+59
Original file line numberDiff line numberDiff line change
@@ -814,3 +814,62 @@ jobs:
814814
name: Inspect
815815
run: |
816816
docker buildx imagetools inspect localhost:5000/name/app:1.0.0
817+
818+
standalone:
819+
runs-on: ubuntu-latest
820+
steps:
821+
-
822+
name: Checkout
823+
uses: actions/checkout@v3
824+
-
825+
name: Uninstall moby cli
826+
run: |
827+
sudo apt-get purge -y moby-cli moby-buildx
828+
-
829+
name: Set up Docker Buildx
830+
uses: docker/setup-buildx-action@v1
831+
-
832+
name: Build
833+
uses: ./
834+
with:
835+
context: ./test
836+
file: ./test/Dockerfile
837+
838+
standalone-kubernetes:
839+
runs-on: ubuntu-latest
840+
steps:
841+
-
842+
name: Checkout
843+
uses: actions/checkout@v3
844+
-
845+
name: Uninstall moby
846+
run: |
847+
sudo apt-get purge -y moby-engine moby-cli moby-buildx
848+
-
849+
name: Setup k8s cluster
850+
run: |
851+
set -x
852+
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
853+
echo "deb [signed-by=/usr/share/keyrings/kubernetes-archive-keyring.gpg] https://apt.kubernetes.io/ kubernetes-xenial main" | sudo tee /etc/apt/sources.list.d/kubernetes.list
854+
sudo apt-get update
855+
sudo apt-get install -y kubelet kubeadm kubectl
856+
sudo swapoff -a
857+
sudo kubeadm init --cri-socket /run/containerd/containerd.sock
858+
mkdir -p $HOME/.kube/
859+
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
860+
sudo chown $USER $HOME/.kube/config
861+
kubectl taint nodes --all node-role.kubernetes.io/master-
862+
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
863+
kubectl wait --for=condition=ready --timeout=30s node --all
864+
kubectl get nodes -o wide
865+
-
866+
name: Set up Docker Buildx
867+
uses: docker/setup-buildx-action@v1
868+
with:
869+
driver: kubernetes
870+
-
871+
name: Build
872+
uses: ./
873+
with:
874+
context: ./test
875+
file: ./test/Dockerfile

__tests__/buildx.test.ts

+11
Original file line numberDiff line numberDiff line change
@@ -78,6 +78,17 @@ describe('isAvailable', () => {
7878
});
7979
});
8080

81+
describe('isAvailable standalone', () => {
82+
const execSpy = jest.spyOn(exec, 'getExecOutput');
83+
buildx.isAvailable(true);
84+
85+
// eslint-disable-next-line jest/no-standalone-expect
86+
expect(execSpy).toHaveBeenCalledWith(`buildx`, [], {
87+
silent: true,
88+
ignoreReturnCode: true
89+
});
90+
});
91+
8192
describe('getVersion', () => {
8293
it('valid', async () => {
8394
const version = await buildx.getVersion();

__tests__/context.test.ts

-16
Original file line numberDiff line numberDiff line change
@@ -151,7 +151,6 @@ describe('getArgs', () => {
151151
['pull', 'false'],
152152
]),
153153
[
154-
'buildx',
155154
'build',
156155
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
157156
'.'
@@ -168,7 +167,6 @@ describe('getArgs', () => {
168167
['pull', 'false'],
169168
]),
170169
[
171-
'buildx',
172170
'build',
173171
'--build-arg', 'MY_ARG=val1,val2,val3',
174172
'--build-arg', 'ARG=val',
@@ -187,7 +185,6 @@ describe('getArgs', () => {
187185
['pull', 'false'],
188186
]),
189187
[
190-
'buildx',
191188
'build',
192189
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
193190
'--tag', 'name/app:7.4',
@@ -208,7 +205,6 @@ describe('getArgs', () => {
208205
['pull', 'false'],
209206
]),
210207
[
211-
'buildx',
212208
'build',
213209
'--label', 'org.opencontainers.image.title=buildkit',
214210
'--label', 'org.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit',
@@ -228,7 +224,6 @@ describe('getArgs', () => {
228224
['pull', 'false'],
229225
]),
230226
[
231-
'buildx',
232227
'build',
233228
'--platform', 'linux/amd64,linux/arm64',
234229
'.'
@@ -245,7 +240,6 @@ describe('getArgs', () => {
245240
['pull', 'false'],
246241
]),
247242
[
248-
'buildx',
249243
'build',
250244
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
251245
'.'
@@ -263,7 +257,6 @@ describe('getArgs', () => {
263257
['pull', 'false'],
264258
]),
265259
[
266-
'buildx',
267260
'build',
268261
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
269262
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
@@ -282,7 +275,6 @@ describe('getArgs', () => {
282275
['pull', 'false'],
283276
]),
284277
[
285-
'buildx',
286278
'build',
287279
'--output', '.',
288280
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
@@ -305,7 +297,6 @@ describe('getArgs', () => {
305297
['pull', 'false'],
306298
]),
307299
[
308-
'buildx',
309300
'build',
310301
'--file', './test/Dockerfile',
311302
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@@ -340,7 +331,6 @@ ccc"`],
340331
['pull', 'false'],
341332
]),
342333
[
343-
'buildx',
344334
'build',
345335
'--file', './test/Dockerfile',
346336
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@@ -378,7 +368,6 @@ ccc`],
378368
['pull', 'false'],
379369
]),
380370
[
381-
'buildx',
382371
'build',
383372
'--file', './test/Dockerfile',
384373
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@@ -408,7 +397,6 @@ ccc`],
408397
['pull', 'false'],
409398
]),
410399
[
411-
'buildx',
412400
'build',
413401
'--file', './test/Dockerfile',
414402
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@@ -432,7 +420,6 @@ ccc`],
432420
['pull', 'false'],
433421
]),
434422
[
435-
'buildx',
436423
'build',
437424
'--label', 'org.opencontainers.image.title=filter_results_top_n',
438425
'--label', 'org.opencontainers.image.description=Reference implementation of operation "filter results (top-n)"',
@@ -455,7 +442,6 @@ ccc`],
455442
['pull', 'false'],
456443
]),
457444
[
458-
'buildx',
459445
'build',
460446
'--add-host', 'docker:10.180.0.1',
461447
'--add-host', 'foo:10.0.0.1',
@@ -484,7 +470,6 @@ nproc=3`],
484470
['pull', 'false'],
485471
]),
486472
[
487-
'buildx',
488473
'build',
489474
'--add-host', 'docker:10.180.0.1',
490475
'--add-host', 'foo:10.0.0.1',
@@ -509,7 +494,6 @@ nproc=3`],
509494
['pull', 'false'],
510495
]),
511496
[
512-
'buildx',
513497
'build',
514498
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
515499
'--metadata-file', '/tmp/.docker-build-push-jest/metadata-file',

__tests__/docker.test.ts

+16
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import {describe, expect, it, jest} from '@jest/globals';
2+
import * as docker from '../src/docker';
3+
import * as exec from '@actions/exec';
4+
5+
describe('isAvailable', () => {
6+
it('cli', () => {
7+
const execSpy = jest.spyOn(exec, 'getExecOutput');
8+
docker.isAvailable();
9+
10+
// eslint-disable-next-line jest/no-standalone-expect
11+
expect(execSpy).toHaveBeenCalledWith(`docker`, undefined, {
12+
silent: true,
13+
ignoreReturnCode: true
14+
});
15+
});
16+
});

dist/index.js

+2-2
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

dist/index.js.map

+1-1
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

src/buildx.ts

+17-4
Original file line numberDiff line numberDiff line change
@@ -107,9 +107,10 @@ export function hasGitAuthToken(secrets: string[]): boolean {
107107
return false;
108108
}
109109

110-
export async function isAvailable(): Promise<boolean> {
110+
export async function isAvailable(standalone?: boolean): Promise<boolean> {
111+
const cmd = getCommand([], standalone);
111112
return await exec
112-
.getExecOutput('docker', ['buildx'], {
113+
.getExecOutput(cmd.command, cmd.args, {
113114
ignoreReturnCode: true,
114115
silent: true
115116
})
@@ -118,12 +119,17 @@ export async function isAvailable(): Promise<boolean> {
118119
return false;
119120
}
120121
return res.exitCode == 0;
122+
})
123+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
124+
.catch(error => {
125+
return false;
121126
});
122127
}
123128

124-
export async function getVersion(): Promise<string> {
129+
export async function getVersion(standalone?: boolean): Promise<string> {
130+
const cmd = getCommand(['version'], standalone);
125131
return await exec
126-
.getExecOutput('docker', ['buildx', 'version'], {
132+
.getExecOutput(cmd.command, cmd.args, {
127133
ignoreReturnCode: true,
128134
silent: true
129135
})
@@ -146,3 +152,10 @@ export function parseVersion(stdout: string): string {
146152
export function satisfies(version: string, range: string): boolean {
147153
return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null;
148154
}
155+
156+
export function getCommand(args: Array<string>, standalone?: boolean) {
157+
return {
158+
command: standalone ? 'buildx' : 'docker',
159+
args: standalone ? args : ['buildx', ...args]
160+
};
161+
}

src/context.ts

-1
Original file line numberDiff line numberDiff line change
@@ -101,7 +101,6 @@ export async function getInputs(defaultContext: string): Promise<Inputs> {
101101
export async function getArgs(inputs: Inputs, defaultContext: string, buildxVersion: string): Promise<Array<string>> {
102102
// prettier-ignore
103103
return [
104-
'buildx',
105104
...await getBuildArgs(inputs, defaultContext, buildxVersion),
106105
...await getCommonArgs(inputs, buildxVersion),
107106
handlebars.compile(inputs.context)({defaultContext})

src/docker.ts

+19
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,19 @@
1+
import * as exec from '@actions/exec';
2+
3+
export async function isAvailable(): Promise<boolean> {
4+
return await exec
5+
.getExecOutput('docker', undefined, {
6+
ignoreReturnCode: true,
7+
silent: true
8+
})
9+
.then(res => {
10+
if (res.stderr.length > 0 && res.exitCode != 0) {
11+
return false;
12+
}
13+
return res.exitCode == 0;
14+
})
15+
// eslint-disable-next-line @typescript-eslint/no-unused-vars
16+
.catch(error => {
17+
return false;
18+
});
19+
}

src/main.ts

+27-7
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,50 @@
11
import * as fs from 'fs';
22
import * as buildx from './buildx';
33
import * as context from './context';
4+
import * as docker from './docker';
45
import * as stateHelper from './state-helper';
56
import * as core from '@actions/core';
67
import * as exec from '@actions/exec';
78

89
async function run(): Promise<void> {
910
try {
11+
const defContext = context.defaultContext();
12+
const inputs: context.Inputs = await context.getInputs(defContext);
13+
14+
// standalone if docker cli not available
15+
const standalone = !(await docker.isAvailable());
16+
1017
core.startGroup(`Docker info`);
11-
await exec.exec('docker', ['version']);
12-
await exec.exec('docker', ['info']);
18+
if (standalone) {
19+
core.info(`Docker info skipped in standalone mode`);
20+
} else {
21+
await exec.exec('docker', ['version'], {
22+
failOnStdErr: false
23+
});
24+
await exec.exec('docker', ['info'], {
25+
failOnStdErr: false
26+
});
27+
}
1328
core.endGroup();
1429

15-
if (!(await buildx.isAvailable())) {
30+
if (!(await buildx.isAvailable(standalone))) {
1631
core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
1732
return;
1833
}
1934
stateHelper.setTmpDir(context.tmpDir());
2035

21-
const buildxVersion = await buildx.getVersion();
22-
const defContext = context.defaultContext();
23-
const inputs: context.Inputs = await context.getInputs(defContext);
36+
const buildxVersion = await buildx.getVersion(standalone);
37+
await core.group(`Buildx version`, async () => {
38+
const versionCmd = buildx.getCommand(['version'], standalone);
39+
await exec.exec(versionCmd.command, versionCmd.args, {
40+
failOnStdErr: false
41+
});
42+
});
2443

2544
const args: string[] = await context.getArgs(inputs, defContext, buildxVersion);
45+
const buildCmd = buildx.getCommand(args, standalone);
2646
await exec
27-
.getExecOutput('docker', args, {
47+
.getExecOutput(buildCmd.command, buildCmd.args, {
2848
ignoreReturnCode: true
2949
})
3050
.then(res => {

0 commit comments

Comments
 (0)