Skip to content

Commit

Permalink
Standalone mode support
Browse files Browse the repository at this point in the history
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
  • Loading branch information
crazy-max committed Apr 27, 2022
1 parent ba31738 commit b0fdbe6
Show file tree
Hide file tree
Showing 10 changed files with 152 additions and 31 deletions.
59 changes: 59 additions & 0 deletions .github/workflows/ci.yml
Expand Up @@ -814,3 +814,62 @@ jobs:
name: Inspect
run: |
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@master # TODO: use v1 when standalone released
-
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@master # TODO: use v1 when standalone released
with:
driver: kubernetes
-
name: Build
uses: ./
with:
context: ./test
file: ./test/Dockerfile
11 changes: 11 additions & 0 deletions __tests__/buildx.test.ts
Expand Up @@ -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', () => {
it('valid', async () => {
const version = await buildx.getVersion();
Expand Down
16 changes: 0 additions & 16 deletions __tests__/context.test.ts
Expand Up @@ -151,7 +151,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'.'
Expand All @@ -168,7 +167,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--build-arg', 'MY_ARG=val1,val2,val3',
'--build-arg', 'ARG=val',
Expand All @@ -187,7 +185,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'--tag', 'name/app:7.4',
Expand All @@ -208,7 +205,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--label', 'org.opencontainers.image.title=buildkit',
'--label', 'org.opencontainers.image.description=concurrent, cache-efficient, and Dockerfile-agnostic builder toolkit',
Expand All @@ -228,7 +224,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--platform', 'linux/amd64,linux/arm64',
'.'
Expand All @@ -245,7 +240,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'.'
Expand All @@ -263,7 +257,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
Expand All @@ -282,7 +275,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--output', '.',
'--secret', 'id=GIT_AUTH_TOKEN,src=/tmp/.docker-build-push-jest/.tmpname-jest',
Expand All @@ -305,7 +297,6 @@ describe('getArgs', () => {
['pull', 'false'],
]),
[
'buildx',
'build',
'--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
Expand Down Expand Up @@ -340,7 +331,6 @@ ccc"`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
Expand Down Expand Up @@ -378,7 +368,6 @@ ccc`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
Expand Down Expand Up @@ -408,7 +397,6 @@ ccc`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--file', './test/Dockerfile',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
Expand All @@ -432,7 +420,6 @@ ccc`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--label', 'org.opencontainers.image.title=filter_results_top_n',
'--label', 'org.opencontainers.image.description=Reference implementation of operation "filter results (top-n)"',
Expand All @@ -455,7 +442,6 @@ ccc`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--add-host', 'docker:10.180.0.1',
'--add-host', 'foo:10.0.0.1',
Expand Down Expand Up @@ -484,7 +470,6 @@ nproc=3`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--add-host', 'docker:10.180.0.1',
'--add-host', 'foo:10.0.0.1',
Expand All @@ -509,7 +494,6 @@ nproc=3`],
['pull', 'false'],
]),
[
'buildx',
'build',
'--iidfile', '/tmp/.docker-build-push-jest/iidfile',
'--metadata-file', '/tmp/.docker-build-push-jest/metadata-file',
Expand Down
16 changes: 16 additions & 0 deletions __tests__/docker.test.ts
@@ -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 changes: 2 additions & 2 deletions dist/index.js

Large diffs are not rendered by default.

2 changes: 1 addition & 1 deletion dist/index.js.map

Large diffs are not rendered by default.

21 changes: 17 additions & 4 deletions src/buildx.ts
Expand Up @@ -107,9 +107,10 @@ export function hasGitAuthToken(secrets: string[]): boolean {
return false;
}

export async function isAvailable(): Promise<boolean> {
export async function isAvailable(standalone?: boolean): Promise<boolean> {
const cmd = getCommand([], standalone);
return await exec
.getExecOutput('docker', ['buildx'], {
.getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true,
silent: true
})
Expand All @@ -118,12 +119,17 @@ export async function isAvailable(): Promise<boolean> {
return false;
}
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
.getExecOutput('docker', ['buildx', 'version'], {
.getExecOutput(cmd.command, cmd.args, {
ignoreReturnCode: true,
silent: true
})
Expand All @@ -146,3 +152,10 @@ export function parseVersion(stdout: string): string {
export function satisfies(version: string, range: string): boolean {
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]
};
}
1 change: 0 additions & 1 deletion src/context.ts
Expand Up @@ -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>> {
// prettier-ignore
return [
'buildx',
...await getBuildArgs(inputs, defaultContext, buildxVersion),
...await getCommonArgs(inputs, buildxVersion),
handlebars.compile(inputs.context)({defaultContext})
Expand Down
19 changes: 19 additions & 0 deletions src/docker.ts
@@ -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;
});
}
34 changes: 27 additions & 7 deletions src/main.ts
@@ -1,30 +1,50 @@
import * as fs from 'fs';
import * as buildx from './buildx';
import * as context from './context';
import * as docker from './docker';
import * as stateHelper from './state-helper';
import * as core from '@actions/core';
import * as exec from '@actions/exec';

async function run(): Promise<void> {
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`);
await exec.exec('docker', ['version']);
await exec.exec('docker', ['info']);
if (standalone) {
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();

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.`);
return;
}
stateHelper.setTmpDir(context.tmpDir());

const buildxVersion = await buildx.getVersion();
const defContext = context.defaultContext();
const inputs: context.Inputs = await context.getInputs(defContext);
const buildxVersion = await buildx.getVersion(standalone);
await core.group(`Buildx version`, async () => {
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 buildCmd = buildx.getCommand(args, standalone);
await exec
.getExecOutput('docker', args, {
.getExecOutput(buildCmd.command, buildCmd.args, {
ignoreReturnCode: true
})
.then(res => {
Expand Down

0 comments on commit b0fdbe6

Please sign in to comment.