Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
CrazyMax 2020-08-18 17:40:31 +02:00
commit 04eaa7c789
No known key found for this signature in database
GPG key ID: 3248E46B6BB8C7F7
21 changed files with 12365 additions and 0 deletions

98
src/buildx.ts Normal file
View file

@ -0,0 +1,98 @@
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as semver from 'semver';
import * as util from 'util';
import * as exec from './exec';
import * as github from './github';
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
const osPlat: string = os.platform();
export async function isAvailable(): Promise<Boolean> {
return await exec.exec(`docker`, ['buildx'], true).then(res => {
if (res.stderr != '' && !res.success) {
return false;
}
return res.success;
});
}
export async function countBuilders(): Promise<number> {
return await exec.exec(`docker`, ['buildx', 'ls'], true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(`Cannot list builders: ${res.stderr}`);
}
return (res.stdout.trim().split(`\n`).length - 1) / 2;
});
}
export async function platforms(): Promise<String | undefined> {
return await exec.exec(`docker`, ['buildx', 'inspect'], true).then(res => {
if (res.stderr != '' && !res.success) {
throw new Error(res.stderr);
}
for (const line of res.stdout.trim().split(`\n`)) {
if (line.startsWith('Platforms')) {
return line.replace('Platforms: ', '').replace(/\s/g, '').trim();
}
}
});
}
export async function install(inputVersion: string, dockerConfigHome: string): Promise<string> {
const release: github.GitHubRelease | null = await github.getRelease(inputVersion);
if (!release) {
throw new Error(`Cannot find buildx ${inputVersion} release`);
}
core.debug(`Release found: ${release.tag_name}`);
const version = release.tag_name.replace(/^v+|v+$/g, '');
let toolPath: string;
toolPath = tc.find('buildx', version);
if (!toolPath) {
const c = semver.clean(version) || '';
if (!semver.valid(c)) {
throw new Error(`Invalid Buildx version "${version}".`);
}
toolPath = await download(version);
}
const pluginsDir: string = path.join(dockerConfigHome, 'cli-plugins');
core.debug(`Plugins dir is ${pluginsDir}`);
if (!fs.existsSync(pluginsDir)) {
fs.mkdirSync(pluginsDir, {recursive: true});
}
const filename: string = 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);
core.info('🔨 Fixing perms...');
fs.chmodSync(pluginPath, '0755');
return pluginPath;
}
async function download(version: string): Promise<string> {
version = semver.clean(version) || '';
const platform: string = osPlat == 'win32' ? 'windows' : osPlat;
const ext: string = osPlat == 'win32' ? '.exe' : '';
const filename: string = util.format('buildx-v%s.%s-amd64%s', version, platform, ext);
const targetFile: string = osPlat == 'win32' ? 'docker-buildx.exe' : 'docker-buildx';
const downloadUrl = util.format('https://github.com/docker/buildx/releases/download/v%s/%s', version, filename);
let downloadPath: string;
try {
core.info(`⬇️ Downloading ${downloadUrl}...`);
downloadPath = await tc.downloadTool(downloadUrl);
core.debug(`Downloaded to ${downloadPath}`);
} catch (error) {
throw error;
}
return await tc.cacheFile(downloadPath, targetFile, 'buildx', version);
}

34
src/exec.ts Normal file
View file

@ -0,0 +1,34 @@
import * as aexec from '@actions/exec';
import {ExecOptions} from '@actions/exec';
export interface ExecResult {
success: boolean;
stdout: string;
stderr: string;
}
export const exec = async (command: string, args: string[] = [], silent: boolean): Promise<ExecResult> => {
let stdout: string = '';
let stderr: string = '';
const options: ExecOptions = {
silent: silent,
ignoreReturnCode: true
};
options.listeners = {
stdout: (data: Buffer) => {
stdout += data.toString();
},
stderr: (data: Buffer) => {
stderr += data.toString();
}
};
const returnCode: number = await aexec.exec(command, args, options);
return {
success: returnCode === 0,
stdout: stdout.trim(),
stderr: stderr.trim()
};
};

12
src/github.ts Normal file
View file

@ -0,0 +1,12 @@
import * as httpm from '@actions/http-client';
export interface GitHubRelease {
id: number;
tag_name: string;
}
export const getRelease = async (version: string): Promise<GitHubRelease | null> => {
const url: string = `https://github.com/docker/buildx/releases/${version}`;
const http: httpm.HttpClient = new httpm.HttpClient('setup-buildx');
return (await http.getJson<GitHubRelease>(url)).result;
};

82
src/main.ts Normal file
View file

@ -0,0 +1,82 @@
import * as os from 'os';
import * as path from 'path';
import * as buildx from './buildx';
import * as mexec from './exec';
import * as stateHelper from './state-helper';
import * as core from '@actions/core';
import * as exec from '@actions/exec';
async function run(): Promise<void> {
try {
if (os.platform() !== 'linux') {
core.setFailed('Only supported on linux platform');
return;
}
const bxVersion: string = core.getInput('version');
const bxDriver: string = core.getInput('driver') || 'docker-container';
const bxDriverOpt: string = core.getInput('driver-opt');
const bxBuildkitdFlags: string = core.getInput('buildkitd-flags');
const bxInstall: boolean = /true/i.test(core.getInput('install'));
const bxUse: boolean = /true/i.test(core.getInput('use'));
const dockerConfigHome: string = process.env.DOCKER_CONFIG || path.join(os.homedir(), '.docker');
if (!(await buildx.isAvailable()) || bxVersion) {
await buildx.install(bxVersion || 'latest', dockerConfigHome);
}
core.info('📣 Buildx info');
await exec.exec('docker', ['buildx', 'version']);
const builderName: string = `builder-${process.env.GITHUB_JOB}-${(await buildx.countBuilders()) + 1}`;
core.setOutput('name', builderName);
stateHelper.setBuilderName(builderName);
core.info('🔨 Creating a new builder instance...');
let createArgs: Array<string> = ['buildx', 'create', '--name', builderName, '--driver', bxDriver];
if (bxDriverOpt) {
createArgs.push('--driver-opt', bxDriverOpt);
}
if (bxBuildkitdFlags) {
createArgs.push('--buildkitd-flags', bxBuildkitdFlags);
}
if (bxUse) {
createArgs.push('--use');
}
await exec.exec('docker', createArgs);
core.info('🏃 Booting builder...');
await exec.exec('docker', ['buildx', 'inspect', '--bootstrap']);
if (bxInstall) {
core.info('🤝 Setting buildx as default builder...');
await exec.exec('docker', ['buildx', 'install']);
}
core.info('🛒 Extracting available platforms...');
const platforms = await buildx.platforms();
core.info(`${platforms}`);
core.setOutput('platforms', platforms);
} catch (error) {
core.setFailed(error.message);
}
}
async function cleanup(): Promise<void> {
if (stateHelper.builderName.length == 0) {
return;
}
await mexec.exec('docker', ['buildx', 'rm', `${stateHelper.builderName}`], false).then(res => {
if (res.stderr != '' && !res.success) {
core.warning(res.stderr);
}
});
}
if (!stateHelper.IsPost) {
run();
} else {
cleanup();
}

12
src/state-helper.ts Normal file
View file

@ -0,0 +1,12 @@
import * as core from '@actions/core';
export const IsPost = !!process.env['STATE_isPost'];
export const builderName = process.env['STATE_builderName'] || '';
export function setBuilderName(builderName: string) {
core.saveState('builderName', builderName);
}
if (!IsPost) {
core.saveState('isPost', 'true');
}