Enhance builder inspection

Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2021-04-23 18:14:38 +02:00
parent abdb186058
commit 72750233ac
No known key found for this signature in database
GPG Key ID: 3248E46B6BB8C7F7
10 changed files with 215 additions and 53 deletions

View File

@ -35,11 +35,13 @@ jobs:
with: with:
version: ${{ matrix.buildx-version }} version: ${{ matrix.buildx-version }}
- -
name: Builder instance name name: Inspect builder
run: echo ${{ steps.buildx.outputs.name }} run: |
- echo "Name: ${{ steps.buildx.outputs.name }}"
name: Available platforms echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
run: echo ${{ steps.buildx.outputs.platforms }} echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
- -
name: Dump context name: Dump context
uses: crazy-max/ghaction-dump-context@v1 uses: crazy-max/ghaction-dump-context@v1
@ -55,15 +57,25 @@ jobs:
id: buildx1 id: buildx1
uses: ./ uses: ./
- -
name: Builder 1 instance name name: Inspect builder 1
run: echo ${{ steps.buildx1.outputs.name }} run: |
echo "Name: ${{ steps.buildx1.outputs.name }}"
echo "Endpoint: ${{ steps.buildx1.outputs.endpoint }}"
echo "Status: ${{ steps.buildx1.outputs.status }}"
echo "Flags: ${{ steps.buildx1.outputs.flags }}"
echo "Platforms: ${{ steps.buildx1.outputs.platforms }}"
- -
name: Set up Docker Buildx 2 name: Set up Docker Buildx 2
id: buildx2 id: buildx2
uses: ./ uses: ./
- -
name: Builder 2 instance name name: Inspect builder 2
run: echo ${{ steps.buildx2.outputs.name }} run: |
echo "Name: ${{ steps.buildx2.outputs.name }}"
echo "Endpoint: ${{ steps.buildx2.outputs.endpoint }}"
echo "Status: ${{ steps.buildx2.outputs.status }}"
echo "Flags: ${{ steps.buildx2.outputs.flags }}"
echo "Platforms: ${{ steps.buildx2.outputs.platforms }}"
install: install:
runs-on: ubuntu-latest runs-on: ubuntu-latest
@ -204,8 +216,10 @@ jobs:
with: with:
version: ${{ matrix.buildx-version }} version: ${{ matrix.buildx-version }}
- -
name: Available platforms name: Inspect builder
run: echo ${{ steps.buildx.outputs.platforms }} run: |
- echo "Name: ${{ steps.buildx.outputs.name }}"
name: Builder instance name echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
run: echo ${{ steps.buildx.outputs.name }} echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"

View File

@ -22,6 +22,9 @@ jobs:
uses: docker/bake-action@v1 uses: docker/bake-action@v1
with: with:
targets: validate targets: validate
-
name: Set up Docker Buildx
uses: ./
- -
name: Test name: Test
uses: docker/bake-action@v1 uses: docker/bake-action@v1

View File

@ -50,11 +50,13 @@ jobs:
id: buildx id: buildx
uses: docker/setup-buildx-action@v1 uses: docker/setup-buildx-action@v1
- -
name: Builder instance name name: Inspect builder
run: echo ${{ steps.buildx.outputs.name }} run: |
- echo "Name: ${{ steps.buildx.outputs.name }}"
name: Available platforms echo "Endpoint: ${{ steps.buildx.outputs.endpoint }}"
run: echo ${{ steps.buildx.outputs.platforms }} echo "Status: ${{ steps.buildx.outputs.status }}"
echo "Flags: ${{ steps.buildx.outputs.flags }}"
echo "Platforms: ${{ steps.buildx.outputs.platforms }}"
``` ```
### With QEMU ### With QEMU
@ -129,7 +131,7 @@ Following inputs can be used as `step.with` keys
| `install` | Bool | Sets up `docker build` command as an alias to `docker buildx` (default `false`) | | `install` | Bool | Sets up `docker build` command as an alias to `docker buildx` (default `false`) |
| `use` | Bool | Switch to this builder instance (default `true`) | | `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` | | `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 | [Optional config file path](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#config) | | `config` | String | [BuildKit config file](https://github.com/docker/buildx/blob/master/docs/reference/buildx_create.md#config) |
> `CSV` type must be a newline-delimited string > `CSV` type must be a newline-delimited string
> ```yaml > ```yaml
@ -147,8 +149,12 @@ Following outputs are available
| Name | Type | Description | | Name | Type | Description |
|---------------|---------|---------------------------------------| |---------------|---------|---------------------------------------|
| `name` | String | Builder instance name | | `name` | String | Builder name |
| `platforms` | String | Available platforms (comma separated) | | `driver` | String | Builder driver |
| `endpoint` | String | Builder node endpoint |
| `status` | String | Builder node status |
| `flags` | String | Builder node flags (if applicable) |
| `platforms` | String | Builder node platforms available (comma separated) |
### environment variables ### environment variables

View File

@ -25,17 +25,19 @@ describe('parseVersion', () => {
}); });
}); });
describe('platforms', () => { describe('inspect', () => {
async function isDaemonRunning() { async function isDaemonRunning() {
return await docker.isDaemonRunning(); return await docker.isDaemonRunning();
} }
(isDaemonRunning() ? it : it.skip)( (isDaemonRunning() ? it : it.skip)(
'valid', 'valid',
async () => { async () => {
const platforms = buildx.platforms(); const builder = await buildx.inspect('');
console.log(`platforms: ${platforms}`); console.log('builder', builder);
expect(platforms).not.toBeUndefined(); expect(builder).not.toBeUndefined();
expect(platforms).not.toEqual(''); expect(builder.name).not.toEqual('');
expect(builder.driver).not.toEqual('');
expect(builder.node_platforms).not.toEqual('');
}, },
100000 100000
); );

View File

@ -1,3 +1,4 @@
import * as os from 'os';
import * as context from '../src/context'; import * as context from '../src/context';
describe('getInputList', () => { describe('getInputList', () => {
@ -78,6 +79,27 @@ describe('asyncForEach', () => {
}); });
}); });
describe('setOutput', () => {
beforeEach(() => {
process.stdout.write = jest.fn();
});
it('setOutput produces the correct command', () => {
context.setOutput('some output', 'some value');
assertWriteCalls([`::set-output name=some output::some value${os.EOL}`]);
});
it('setOutput handles bools', () => {
context.setOutput('some output', false);
assertWriteCalls([`::set-output name=some output::false${os.EOL}`]);
});
it('setOutput handles numbers', () => {
context.setOutput('some output', 1.01);
assertWriteCalls([`::set-output name=some output::1.01${os.EOL}`]);
});
});
// See: https://github.com/actions/toolkit/blob/master/packages/core/src/core.ts#L67 // See: https://github.com/actions/toolkit/blob/master/packages/core/src/core.ts#L67
function getInputName(name: string): string { function getInputName(name: string): string {
return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`; return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
@ -86,3 +108,11 @@ function getInputName(name: string): string {
function setInput(name: string, value: string): void { function setInput(name: string, value: string): void {
process.env[getInputName(name)] = value; process.env[getInputName(name)] = value;
} }
// Assert that process.stdout.write calls called only with the given arguments.
function assertWriteCalls(calls: string[]): void {
expect(process.stdout.write).toHaveBeenCalledTimes(calls.length);
for (let i = 0; i < calls.length; i++) {
expect(process.stdout.write).toHaveBeenNthCalledWith(i + 1, calls[i]);
}
}

View File

@ -33,14 +33,22 @@ inputs:
description: 'Optional address for docker socket or context from `docker context ls`' description: 'Optional address for docker socket or context from `docker context ls`'
required: false required: false
config: config:
description: 'Optional config file path' description: 'BuildKit config file'
required: false required: false
outputs: outputs:
name: name:
description: 'Builder instance name' description: 'Builder name'
driver:
description: 'Builder driver'
endpoint:
description: 'Builder node endpoint'
status:
description: 'Builder node status'
flags:
description: 'Builder node flags (if applicable)'
platforms: platforms:
description: 'Available platforms (comma separated)' description: 'Builder node platforms available (comma separated)'
runs: runs:
using: 'node12' using: 'node12'

71
dist/index.js generated vendored
View File

@ -535,7 +535,7 @@ function run() {
const buildxVersion = yield buildx.getVersion(); const buildxVersion = yield buildx.getVersion();
core.info(`Using buildx ${buildxVersion}`); core.info(`Using buildx ${buildxVersion}`);
const builderName = inputs.driver == 'docker' ? 'default' : `builder-${__webpack_require__(840).v4()}`; const builderName = inputs.driver == 'docker' ? 'default' : `builder-${__webpack_require__(840).v4()}`;
core.setOutput('name', builderName); context.setOutput('name', builderName);
stateHelper.setBuilderName(builderName); stateHelper.setBuilderName(builderName);
if (inputs.driver !== 'docker') { if (inputs.driver !== 'docker') {
core.startGroup(`Creating a new builder instance`); core.startGroup(`Creating a new builder instance`);
@ -572,10 +572,14 @@ function run() {
yield exec.exec('docker', ['buildx', 'install']); yield exec.exec('docker', ['buildx', 'install']);
core.endGroup(); core.endGroup();
} }
core.startGroup(`Extracting available platforms`); core.startGroup(`Inspect builder`);
const platforms = yield buildx.platforms(); const builder = yield buildx.inspect(builderName);
core.info(`${platforms}`); core.info(JSON.stringify(builder, undefined, 2));
core.setOutput('platforms', platforms); context.setOutput('driver', builder.driver);
context.setOutput('endpoint', builder.node_endpoint);
context.setOutput('status', builder.node_status);
context.setOutput('flags', builder.node_flags);
context.setOutput('platforms', builder.node_platforms);
core.endGroup(); core.endGroup();
} }
catch (error) { catch (error) {
@ -2126,7 +2130,7 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.install = exports.platforms = exports.isAvailable = exports.parseVersion = exports.getVersion = void 0; exports.install = exports.inspect = exports.isAvailable = exports.parseVersion = exports.getVersion = void 0;
const fs = __importStar(__webpack_require__(747)); const fs = __importStar(__webpack_require__(747));
const path = __importStar(__webpack_require__(622)); const path = __importStar(__webpack_require__(622));
const semver = __importStar(__webpack_require__(383)); const semver = __importStar(__webpack_require__(383));
@ -2168,21 +2172,56 @@ function isAvailable() {
}); });
} }
exports.isAvailable = isAvailable; exports.isAvailable = isAvailable;
function platforms() { function inspect(name) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
return yield exec.exec(`docker`, ['buildx', 'inspect'], true).then(res => { return yield exec.exec(`docker`, ['buildx', 'inspect', name], true).then(res => {
if (res.stderr != '' && !res.success) { if (res.stderr != '' && !res.success) {
throw new Error(res.stderr); throw new Error(res.stderr);
} }
for (const line of res.stdout.trim().split(`\n`)) { const builder = {};
if (line.startsWith('Platforms')) { itlines: for (const line of res.stdout.trim().split(`\n`)) {
return line.replace('Platforms: ', '').replace(/\s/g, '').trim(); const [key, ...rest] = line.split(':');
const value = rest.map(v => v.trim()).join(':');
if (key.length == 0 || value.length == 0) {
continue;
}
switch (key) {
case 'Name': {
if (builder.name == undefined) {
builder.name = value;
}
else {
builder.node_name = value;
}
break;
}
case 'Driver': {
builder.driver = value;
break;
}
case 'Endpoint': {
builder.node_endpoint = value;
break;
}
case 'Status': {
builder.node_status = value;
break;
}
case 'Flags': {
builder.node_flags = value;
break;
}
case 'Platforms': {
builder.node_platforms = value.replace(/\s/g, '');
break itlines;
}
} }
} }
return builder;
}); });
}); });
} }
exports.platforms = platforms; exports.inspect = inspect;
function install(inputVersion, dockerConfigHome) { function install(inputVersion, dockerConfigHome) {
return __awaiter(this, void 0, void 0, function* () { return __awaiter(this, void 0, void 0, function* () {
const release = yield github.getRelease(inputVersion); const release = yield github.getRelease(inputVersion);
@ -8011,9 +8050,10 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
}); });
}; };
Object.defineProperty(exports, "__esModule", { value: true }); Object.defineProperty(exports, "__esModule", { value: true });
exports.asyncForEach = exports.getInputList = exports.getInputs = exports.osArch = exports.osPlat = void 0; exports.setOutput = exports.asyncForEach = exports.getInputList = exports.getInputs = exports.osArch = exports.osPlat = void 0;
const os = __importStar(__webpack_require__(87)); const os = __importStar(__webpack_require__(87));
const core = __importStar(__webpack_require__(186)); const core = __importStar(__webpack_require__(186));
const command_1 = __webpack_require__(351);
exports.osPlat = os.platform(); exports.osPlat = os.platform();
exports.osArch = os.arch(); exports.osArch = os.arch();
function getInputs() { function getInputs() {
@ -8050,6 +8090,11 @@ exports.asyncForEach = (array, callback) => __awaiter(void 0, void 0, void 0, fu
yield callback(array[index], index, array); yield callback(array[index], index, array);
} }
}); });
// FIXME: Temp fix https://github.com/actions/toolkit/issues/777
function setOutput(name, value) {
command_1.issueCommand('set-output', { name }, value);
}
exports.setOutput = setOutput;
//# sourceMappingURL=context.js.map //# sourceMappingURL=context.js.map
/***/ }), /***/ }),

View File

@ -8,6 +8,16 @@ import * as github from './github';
import * as core from '@actions/core'; import * as core from '@actions/core';
import * as tc from '@actions/tool-cache'; import * as tc from '@actions/tool-cache';
export type Builder = {
name?: string;
driver?: string;
node_name?: string;
node_endpoint?: string;
node_status?: string;
node_flags?: string;
node_platforms?: string;
};
export async function getVersion(): Promise<string> { export async function getVersion(): Promise<string> {
return await exec.exec(`docker`, ['buildx', 'version'], true).then(res => { return await exec.exec(`docker`, ['buildx', 'version'], true).then(res => {
if (res.stderr != '' && !res.success) { if (res.stderr != '' && !res.success) {
@ -34,16 +44,50 @@ export async function isAvailable(): Promise<Boolean> {
}); });
} }
export async function platforms(): Promise<String | undefined> { export async function inspect(name: string): Promise<Builder> {
return await exec.exec(`docker`, ['buildx', 'inspect'], true).then(res => { return await exec.exec(`docker`, ['buildx', 'inspect', name], true).then(res => {
if (res.stderr != '' && !res.success) { if (res.stderr != '' && !res.success) {
throw new Error(res.stderr); throw new Error(res.stderr);
} }
for (const line of res.stdout.trim().split(`\n`)) { const builder: Builder = {};
if (line.startsWith('Platforms')) { itlines: for (const line of res.stdout.trim().split(`\n`)) {
return line.replace('Platforms: ', '').replace(/\s/g, '').trim(); const [key, ...rest] = line.split(':');
const value = rest.map(v => v.trim()).join(':');
if (key.length == 0 || value.length == 0) {
continue;
}
switch (key) {
case 'Name': {
if (builder.name == undefined) {
builder.name = value;
} else {
builder.node_name = value;
}
break;
}
case 'Driver': {
builder.driver = value;
break;
}
case 'Endpoint': {
builder.node_endpoint = value;
break;
}
case 'Status': {
builder.node_status = value;
break;
}
case 'Flags': {
builder.node_flags = value;
break;
}
case 'Platforms': {
builder.node_platforms = value.replace(/\s/g, '');
break itlines;
}
} }
} }
return builder;
}); });
} }

View File

@ -1,5 +1,6 @@
import * as os from 'os'; import * as os from 'os';
import * as core from '@actions/core'; import * as core from '@actions/core';
import {issueCommand} from '@actions/core/lib/command';
export const osPlat: string = os.platform(); export const osPlat: string = os.platform();
export const osArch: string = os.arch(); export const osArch: string = os.arch();
@ -49,3 +50,8 @@ export const asyncForEach = async (array, callback) => {
await callback(array[index], index, array); await callback(array[index], index, array);
} }
}; };
// FIXME: Temp fix https://github.com/actions/toolkit/issues/777
export function setOutput(name: string, value: any): void {
issueCommand('set-output', {name}, value);
}

View File

@ -28,7 +28,7 @@ async function run(): Promise<void> {
core.info(`Using buildx ${buildxVersion}`); core.info(`Using buildx ${buildxVersion}`);
const builderName: string = inputs.driver == 'docker' ? 'default' : `builder-${require('uuid').v4()}`; const builderName: string = inputs.driver == 'docker' ? 'default' : `builder-${require('uuid').v4()}`;
core.setOutput('name', builderName); context.setOutput('name', builderName);
stateHelper.setBuilderName(builderName); stateHelper.setBuilderName(builderName);
if (inputs.driver !== 'docker') { if (inputs.driver !== 'docker') {
@ -69,10 +69,14 @@ async function run(): Promise<void> {
core.endGroup(); core.endGroup();
} }
core.startGroup(`Extracting available platforms`); core.startGroup(`Inspect builder`);
const platforms = await buildx.platforms(); const builder = await buildx.inspect(builderName);
core.info(`${platforms}`); core.info(JSON.stringify(builder, undefined, 2));
core.setOutput('platforms', platforms); context.setOutput('driver', builder.driver);
context.setOutput('endpoint', builder.node_endpoint);
context.setOutput('status', builder.node_status);
context.setOutput('flags', builder.node_flags);
context.setOutput('platforms', builder.node_platforms);
core.endGroup(); core.endGroup();
} catch (error) { } catch (error) {
core.setFailed(error.message); core.setFailed(error.message);