Skip to content

Commit

Permalink
Standalone mode
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 12, 2022
1 parent 2a6fbda commit f512a10
Show file tree
Hide file tree
Showing 10 changed files with 169 additions and 47 deletions.
22 changes: 21 additions & 1 deletion .github/workflows/ci.yml
Expand Up @@ -24,6 +24,9 @@ jobs:
- latest
- v0.4.1
- ""
standalone:
- true
- false
steps:
-
name: Checkout
Expand All @@ -34,6 +37,15 @@ jobs:
uses: ./
with:
version: ${{ matrix.buildx-version }}
standalone: ${{ matrix.standalone }}
-
name: Check version
run: |
if [ "${{ matrix.standalone }}" = "true" ]; then
buildx version
else
docker buildx version
fi
-
name: Inspect builder
run: |
Expand Down Expand Up @@ -351,6 +363,9 @@ jobs:
- refs/tags/v0.5.1
- refs/pull/731/head
- cb185f095fd3d9444e0aa605d3789e9e05f2a1e7
standalone:
- true
- false
steps:
-
name: Checkout
Expand All @@ -360,10 +375,15 @@ jobs:
uses: ./
with:
version: https://github.com/docker/buildx.git#${{ matrix.ref }}
standalone: ${{ matrix.standalone }}
-
name: Check version
run: |
docker buildx version
if [ "${{ matrix.standalone }}" = "true" ]; then
buildx version
else
docker buildx version
fi
-
name: Create Dockerfile
run: |
Expand Down
8 changes: 5 additions & 3 deletions README.md
Expand Up @@ -189,16 +189,18 @@ Following inputs can be used as `step.with` keys
| Name | Type | Description |
|--------------------|---------|-----------------------------------|
| `version` | String | [buildx](https://github.com/docker/buildx) version. (eg. `v0.3.0`, `latest`, `https://github.com/docker/buildx.git#master`) |
| `standalone`¹ | Bool | Install and use buildx in standalone mode (default `false`) |
| `driver` | String | Sets the [builder driver](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#driver) to be used (default `docker-container`) |
| `driver-opts` | CSV | List of additional [driver-specific options](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#driver-opt) (eg. `image=moby/buildkit:master`) |
| `buildkitd-flags` | String | [Flags for buildkitd](https://github.com/moby/buildkit/blob/master/docs/buildkitd.toml.md) daemon (since [buildx v0.3.0](https://github.com/docker/buildx/releases/tag/v0.3.0)) |
| `install` | Bool | Sets up `docker build` command as an alias to `docker buildx` (default `false`) |
| `use` | Bool | Switch to this builder instance (default `true`) |
| `endpoint` | String | [Optional address for docker socket](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#description) or context from `docker context ls` |
| `config` | String | [BuildKit config file](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#config) |
| `config-inline` | String | Same as `config` but inline |
| `config`² | String | [BuildKit config file](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#config) |
| `config-inline`² | String | Same as `config` but inline |

> `config` and `config-inline` are mutually exclusive.
> * ¹ `standalone` mode allows using buildx without the Docker CLI (e.g., `buildx build ...`)
> * ² `config` and `config-inline` are mutually exclusive
> `CSV` type must be a newline-delimited string
> ```yaml
Expand Down
38 changes: 26 additions & 12 deletions __tests__/buildx.test.ts
Expand Up @@ -32,6 +32,17 @@ describe('isAvailable', () => {
});
});

describe('isAvailable standalone', () => {
const execSpy: jest.SpyInstance = 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 Expand Up @@ -75,29 +86,32 @@ describe('build', () => {

// eslint-disable-next-line jest/no-disabled-tests
it.skip('builds refs/pull/648/head', async () => {
const buildxBin = await buildx.build('https://github.com/docker/buildx.git#refs/pull/648/head', tmpDir);
const buildxBin = await buildx.build('https://github.com/docker/buildx.git#refs/pull/648/head', tmpDir, false);
expect(fs.existsSync(buildxBin)).toBe(true);
}, 100000);

// eslint-disable-next-line jest/no-disabled-tests
it.skip('builds 67bd6f4dc82a9cd96f34133dab3f6f7af803bb14', async () => {
const buildxBin = await buildx.build('https://github.com/docker/buildx.git#67bd6f4dc82a9cd96f34133dab3f6f7af803bb14', tmpDir);
const buildxBin = await buildx.build('https://github.com/docker/buildx.git#67bd6f4dc82a9cd96f34133dab3f6f7af803bb14', tmpDir, false);
expect(fs.existsSync(buildxBin)).toBe(true);
}, 100000);
});

describe('install', () => {
const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'setup-buildx-'));

it('acquires v0.4.1 version of buildx', async () => {
const buildxBin = await buildx.install('v0.4.1', tmpDir);
expect(fs.existsSync(buildxBin)).toBe(true);
}, 100000);

it('acquires latest version of buildx', async () => {
const buildxBin = await buildx.install('latest', tmpDir);
expect(fs.existsSync(buildxBin)).toBe(true);
}, 100000);
test.each([
['v0.4.1', false],
['latest', false],
['v0.4.1', true],
['latest', true]
])(
'acquires %p of buildx (standalone: %p)',
async (version, standalone) => {
const buildxBin = await buildx.install(version, tmpDir, standalone);
expect(fs.existsSync(buildxBin)).toBe(true);
},
100000
);
});

describe('getConfig', () => {
Expand Down
4 changes: 4 additions & 0 deletions action.yml
Expand Up @@ -10,6 +10,10 @@ inputs:
version:
description: 'Buildx version. (eg. v0.3.0)'
required: false
standalone:
description: 'Install and use buildx in standalone mode'
default: 'false'
required: false
driver:
description: 'Sets the builder driver to be used'
default: 'docker-container'
Expand Down
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.

86 changes: 74 additions & 12 deletions src/buildx.ts
Expand Up @@ -41,9 +41,10 @@ export async function getConfig(s: string, file: boolean): Promise<string> {
return configFile;
}

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.commandLine, cmd.args, {
ignoreReturnCode: true,
silent: true
})
Expand All @@ -55,9 +56,10 @@ export async function isAvailable(): Promise<boolean> {
});
}

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.commandLine, cmd.args, {
ignoreReturnCode: true,
silent: true
})
Expand All @@ -81,9 +83,10 @@ export function satisfies(version: string, range: string): boolean {
return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null;
}

export async function inspect(name: string): Promise<Builder> {
export async function inspect(name: string, standalone?: boolean): Promise<Builder> {
const cmd = getCommand(['inspect', name], standalone);
return await exec
.getExecOutput(`docker`, ['buildx', 'inspect', name], {
.getExecOutput(cmd.commandLine, cmd.args, {
ignoreReturnCode: true,
silent: true
})
Expand Down Expand Up @@ -133,7 +136,7 @@ export async function inspect(name: string): Promise<Builder> {
});
}

export async function build(inputBuildRef: string, dockerConfigHome: string): Promise<string> {
export async function build(inputBuildRef: string, dest: string, standalone: boolean): Promise<string> {
// eslint-disable-next-line prefer-const
let [repo, ref] = inputBuildRef.split('#');
if (ref.length == 0) {
Expand All @@ -152,8 +155,27 @@ export async function build(inputBuildRef: string, dockerConfigHome: string): Pr
toolPath = tc.find('buildx', vspec);
if (!toolPath) {
const outFolder = path.join(context.tmpDir(), 'out').split(path.sep).join(path.posix.sep);
let buildWithStandalone = false;
const standaloneFound = await isAvailable(true);
const pluginFound = await isAvailable(false);
if (standalone && standaloneFound) {
core.debug(`Buildx standalone found, build with it`);
buildWithStandalone = true;
} else if (!standalone && pluginFound) {
core.debug(`Buildx plugin found, build with it`);
buildWithStandalone = false;
} else if (standaloneFound) {
core.debug(`Buildx plugin not found, but standalone found so trying to build with it`);
buildWithStandalone = true;
} else if (pluginFound) {
core.debug(`Buildx standalone not found, but plugin found so trying to build with it`);
buildWithStandalone = false;
} else {
throw new Error(`Neither buildx standalone or plugin have been found to build from ref`);
}
const buildCmd = getCommand(['build', '--target', 'binaries', '--build-arg', 'BUILDKIT_CONTEXT_KEEP_GIT_DIR=1', '--output', `type=local,dest=${outFolder}`, inputBuildRef], buildWithStandalone);
toolPath = await exec
.getExecOutput('docker', ['buildx', 'build', '--target', 'binaries', '--build-arg', 'BUILDKIT_CONTEXT_KEEP_GIT_DIR=1', '--output', `type=local,dest=${outFolder}`, inputBuildRef], {
.getExecOutput(buildCmd.commandLine, buildCmd.args, {
ignoreReturnCode: true
})
.then(res => {
Expand All @@ -164,10 +186,13 @@ export async function build(inputBuildRef: string, dockerConfigHome: string): Pr
});
}

return setPlugin(toolPath, dockerConfigHome);
if (standalone) {
return setStandalone(toolPath, dest);
}
return setPlugin(toolPath, dest);
}

export async function install(inputVersion: string, dockerConfigHome: string): Promise<string> {
export async function install(inputVersion: string, dest: string, standalone: boolean): Promise<string> {
const release: github.GitHubRelease | null = await github.getRelease(inputVersion);
if (!release) {
throw new Error(`Cannot find buildx ${inputVersion} release`);
Expand All @@ -185,10 +210,40 @@ export async function install(inputVersion: string, dockerConfigHome: string): P
toolPath = await download(version);
}

return setPlugin(toolPath, dockerConfigHome);
if (standalone) {
return setStandalone(toolPath, dest);
}
return setPlugin(toolPath, dest);
}

async function setStandalone(toolPath: string, dest: string): Promise<string> {
core.info('Standalone mode');
const toolBinPath = path.join(toolPath, context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx');

const binDir = path.join(dest, 'bin');
core.debug(`Bin dir is ${binDir}`);
if (!fs.existsSync(binDir)) {
fs.mkdirSync(binDir, {recursive: true});
}

const filename: string = context.osPlat == 'win32' ? 'buildx.exe' : 'buildx';
const buildxPath: string = path.join(binDir, filename);
core.debug(`Bin path is ${buildxPath}`);
fs.copyFileSync(toolBinPath, buildxPath);

core.info('Fixing perms');
fs.chmodSync(buildxPath, '0755');

core.addPath(binDir);
core.info('Added buildx to the path');

return buildxPath;
}

async function setPlugin(toolPath: string, dockerConfigHome: string): Promise<string> {
core.info('Docker plugin mode');
const toolBinPath = path.join(toolPath, context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx');

const pluginsDir: string = path.join(dockerConfigHome, 'cli-plugins');
core.debug(`Plugins dir is ${pluginsDir}`);
if (!fs.existsSync(pluginsDir)) {
Expand All @@ -198,7 +253,7 @@ async function setPlugin(toolPath: string, dockerConfigHome: string): Promise<st
const filename: string = context.osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx';
const pluginPath: string = path.join(pluginsDir, filename);
core.debug(`Plugin path is ${pluginPath}`);
fs.copyFileSync(path.join(toolPath, filename), pluginPath);
fs.copyFileSync(toolBinPath, pluginPath);

core.info('Fixing perms');
fs.chmodSync(pluginPath, '0755');
Expand Down Expand Up @@ -269,3 +324,10 @@ export async function getBuildKitVersion(containerID: string): Promise<string> {
return bkitimage.stdout.trim();
});
}

export function getCommand(args: Array<string>, standalone?: boolean) {
return {
commandLine: standalone ? 'buildx' : 'docker',
args: standalone ? args : ['buildx', ...args]
};
}
2 changes: 2 additions & 0 deletions src/context.ts
Expand Up @@ -22,6 +22,7 @@ export function tmpNameSync(options?: tmp.TmpNameOptions): string {

export interface Inputs {
version: string;
standalone: boolean;
driver: string;
driverOpts: string[];
buildkitdFlags: string;
Expand All @@ -35,6 +36,7 @@ export interface Inputs {
export async function getInputs(): Promise<Inputs> {
return {
version: core.getInput('version'),
standalone: core.getBooleanInput('standalone'),
driver: core.getInput('driver') || 'docker-container',
driverOpts: await getInputList('driver-opts', true),
buildkitdFlags: core.getInput('buildkitd-flags') || '--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host',
Expand Down

0 comments on commit f512a10

Please sign in to comment.