Standalone mode support

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2022-04-28 09:31:47 +02:00
parent ba317382dc
commit 38b45804b5
No known key found for this signature in database
GPG key ID: 3248E46B6BB8C7F7
10 changed files with 152 additions and 31 deletions

View file

@ -814,3 +814,62 @@ jobs:
name: Inspect name: Inspect
run: | run: |
docker buildx imagetools inspect localhost:5000/name/app:1.0.0 docker buildx imagetools inspect localhost:5000/name/app:1.0.0
standalone:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Uninstall moby cli
run: |
sudo apt-get purge -y moby-cli moby-buildx
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
-
name: Build
uses: ./
with:
context: ./test
file: ./test/Dockerfile
standalone-kubernetes:
runs-on: ubuntu-latest
steps:
-
name: Checkout
uses: actions/checkout@v3
-
name: Uninstall moby
run: |
sudo apt-get purge -y moby-engine moby-cli moby-buildx
-
name: Setup k8s cluster
run: |
set -x
sudo curl -fsSLo /usr/share/keyrings/kubernetes-archive-keyring.gpg https://packages.cloud.google.com/apt/doc/apt-key.gpg
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
sudo apt-get update
sudo apt-get install -y kubelet kubeadm kubectl
sudo swapoff -a
sudo kubeadm init --cri-socket /run/containerd/containerd.sock
mkdir -p $HOME/.kube/
sudo cp -i /etc/kubernetes/admin.conf $HOME/.kube/config
sudo chown $USER $HOME/.kube/config
kubectl taint nodes --all node-role.kubernetes.io/master-
kubectl apply -f https://docs.projectcalico.org/manifests/calico.yaml
kubectl wait --for=condition=ready --timeout=30s node --all
kubectl get nodes -o wide
-
name: Set up Docker Buildx
uses: docker/setup-buildx-action@v1
with:
driver: kubernetes
-
name: Build
uses: ./
with:
context: ./test
file: ./test/Dockerfile

View file

@ -78,6 +78,17 @@ describe('isAvailable', () => {
}); });
}); });
describe('isAvailable standalone', () => {
const execSpy = jest.spyOn(exec, 'getExecOutput');
buildx.isAvailable(true);
// eslint-disable-next-line jest/no-standalone-expect
expect(execSpy).toHaveBeenCalledWith(`buildx`, [], {
silent: true,
ignoreReturnCode: true
});
});
describe('getVersion', () => { describe('getVersion', () => {
it('valid', async () => { it('valid', async () => {
const version = await buildx.getVersion(); const version = await buildx.getVersion();

View file

@ -151,7 +151,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'.' '.'
@ -168,7 +167,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--build-arg', 'MY_ARG=val1,val2,val3', '--build-arg', 'MY_ARG=val1,val2,val3',
'--build-arg', 'ARG=val', '--build-arg', 'ARG=val',
@ -187,7 +185,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'--tag', 'name/app:7.4', '--tag', 'name/app:7.4',
@ -208,7 +205,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--label', 'org.opencontainers.image.title=buildkit', '--label', 'org.opencontainers.image.title=buildkit',
'--label', 'org.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit', '--label', 'org.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit',
@ -228,7 +224,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--platform', 'linux/amd64,linux/arm64', '--platform', 'linux/amd64,linux/arm64',
'.' '.'
@ -245,7 +240,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'.' '.'
@ -263,7 +257,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest', '--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
@ -282,7 +275,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--output', '.', '--output', '.',
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest', '--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
@ -305,7 +297,6 @@ describe('getArgs', () => {
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--file', './test/Dockerfile', '--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@ -340,7 +331,6 @@ ccc"`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--file', './test/Dockerfile', '--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@ -378,7 +368,6 @@ ccc`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--file', './test/Dockerfile', '--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@ -408,7 +397,6 @@ ccc`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--file', './test/Dockerfile', '--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
@ -432,7 +420,6 @@ ccc`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--label', 'org.opencontainers.image.title=filter_results_top_n', '--label', 'org.opencontainers.image.title=filter_results_top_n',
'--label', 'org.opencontainers.image.description=Reference implementation of operation "filter results (top-n)"', '--label', 'org.opencontainers.image.description=Reference implementation of operation "filter results (top-n)"',
@ -455,7 +442,6 @@ ccc`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--add-host', 'docker:10.180.0.1', '--add-host', 'docker:10.180.0.1',
'--add-host', 'foo:10.0.0.1', '--add-host', 'foo:10.0.0.1',
@ -484,7 +470,6 @@ nproc=3`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--add-host', 'docker:10.180.0.1', '--add-host', 'docker:10.180.0.1',
'--add-host', 'foo:10.0.0.1', '--add-host', 'foo:10.0.0.1',
@ -509,7 +494,6 @@ nproc=3`],
['pull', 'false'], ['pull', 'false'],
]), ]),
[ [
'buildx',
'build', 'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile', '--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'--metadata-file', '/tmp/.docker-build-push-jest/metadata-file', '--metadata-file', '/tmp/.docker-build-push-jest/metadata-file',

16
__tests__/docker.test.ts Normal file
View file

@ -0,0 +1,16 @@
import {describe, expect, it, jest} from '@jest/globals';
import * as docker from '../src/docker';
import * as exec from '@actions/exec';
describe('isAvailable', () => {
it('cli', () => {
const execSpy = jest.spyOn(exec, 'getExecOutput');
docker.isAvailable();
// eslint-disable-next-line jest/no-standalone-expect
expect(execSpy).toHaveBeenCalledWith(`docker`, undefined, {
silent: true,
ignoreReturnCode: true
});
});
});

4
dist/index.js generated vendored

File diff suppressed because one or more lines are too long

2
dist/index.js.map generated vendored

File diff suppressed because one or more lines are too long

View file

@ -107,9 +107,10 @@ export function hasGitAuthToken(secrets: string[]): boolean {
return false; return false;
} }
export async function isAvailable(): Promise<boolean> { export async function isAvailable(standalone?: boolean): Promise<boolean> {
const cmd = getCommand([], standalone);
return await exec return await exec
.getExecOutput('docker', ['buildx'], { .getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true, ignoreReturnCode: true,
silent: true silent: true
}) })
@ -118,12 +119,17 @@ export async function isAvailable(): Promise<boolean> {
return false; return false;
} }
return res.exitCode == 0; return res.exitCode == 0;
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch(error => {
return false;
}); });
} }
export async function getVersion(): Promise<string> { export async function getVersion(standalone?: boolean): Promise<string> {
const cmd = getCommand(['version'], standalone);
return await exec return await exec
.getExecOutput('docker', ['buildx', 'version'], { .getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true, ignoreReturnCode: true,
silent: true silent: true
}) })
@ -146,3 +152,10 @@ export function parseVersion(stdout: string): string {
export function satisfies(version: string, range: string): boolean { export function satisfies(version: string, range: string): boolean {
return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null; return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null;
} }
export function getCommand(args: Array<string>, standalone?: boolean) {
return {
command: standalone ? 'buildx' : 'docker',
args: standalone ? args : ['buildx', ...args]
};
}

View file

@ -101,7 +101,6 @@ export async function getInputs(defaultContext: string): Promise<Inputs> {
export async function getArgs(inputs: Inputs, defaultContext: string, buildxVersion: string): Promise<Array<string>> { export async function getArgs(inputs: Inputs, defaultContext: string, buildxVersion: string): Promise<Array<string>> {
// prettier-ignore // prettier-ignore
return [ return [
'buildx',
...await getBuildArgs(inputs, defaultContext, buildxVersion), ...await getBuildArgs(inputs, defaultContext, buildxVersion),
...await getCommonArgs(inputs, buildxVersion), ...await getCommonArgs(inputs, buildxVersion),
handlebars.compile(inputs.context)({defaultContext}) handlebars.compile(inputs.context)({defaultContext})

19
src/docker.ts Normal file
View file

@ -0,0 +1,19 @@
import * as exec from '@actions/exec';
export async function isAvailable(): Promise<boolean> {
return await exec
.getExecOutput('docker', undefined, {
ignoreReturnCode: true,
silent: true
})
.then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
return false;
}
return res.exitCode == 0;
})
// eslint-disable-next-line @typescript-eslint/no-unused-vars
.catch(error => {
return false;
});
}

View file

@ -1,30 +1,50 @@
import * as fs from 'fs'; import * as fs from 'fs';
import * as buildx from './buildx'; import * as buildx from './buildx';
import * as context from './context'; import * as context from './context';
import * as docker from './docker';
import * as stateHelper from './state-helper'; import * as stateHelper from './state-helper';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as exec from '@actions/exec'; import * as exec from '@actions/exec';
async function run(): Promise<void> { async function run(): Promise<void> {
try { try {
const defContext = context.defaultContext();
const inputs: context.Inputs = await context.getInputs(defContext);
// standalone if docker cli not available
const standalone = !(await docker.isAvailable());
core.startGroup(`Docker info`); core.startGroup(`Docker info`);
await exec.exec('docker', ['version']); if (standalone) {
await exec.exec('docker', ['info']); core.info(`Docker info skipped in standalone mode`);
} else {
await exec.exec('docker', ['version'], {
failOnStdErr: false
});
await exec.exec('docker', ['info'], {
failOnStdErr: false
});
}
core.endGroup(); core.endGroup();
if (!(await buildx.isAvailable())) { if (!(await buildx.isAvailable(standalone))) {
core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`); core.setFailed(`Docker buildx is required. See https://github.com/docker/setup-buildx-action to set up buildx.`);
return; return;
} }
stateHelper.setTmpDir(context.tmpDir()); stateHelper.setTmpDir(context.tmpDir());
const buildxVersion = await buildx.getVersion(); const buildxVersion = await buildx.getVersion(standalone);
const defContext = context.defaultContext(); await core.group(`Buildx version`, async () => {
const inputs: context.Inputs = await context.getInputs(defContext); const versionCmd = buildx.getCommand(['version'], standalone);
await exec.exec(versionCmd.command, versionCmd.args, {
failOnStdErr: false
});
});
const args: string[] = await context.getArgs(inputs, defContext, buildxVersion); const args: string[] = await context.getArgs(inputs, defContext, buildxVersion);
const buildCmd = buildx.getCommand(args, standalone);
await exec await exec
.getExecOutput('docker', args, { .getExecOutput(buildCmd.command, buildCmd.args, {
ignoreReturnCode: true ignoreReturnCode: true
}) })
.then(res => { .then(res => {