From 7cfe2a742f1cc710a8ff7eb2a9f1f935407145e7 Mon Sep 17 00:00:00 2001 From: Luca Stocchi Date: Fri, 6 Nov 2020 18:42:45 +0100 Subject: [PATCH] first commit Signed-off-by: Luca Stocchi --- package.json | 12 +++++++ src/buildah.ts | 95 ++++++++++++++++++++++++++++++++++++++++++++++++++ src/index.ts | 69 ++++++++++++++++++++++++++++++++++++ 3 files changed, 176 insertions(+) create mode 100644 package.json create mode 100644 src/buildah.ts create mode 100644 src/index.ts diff --git a/package.json b/package.json new file mode 100644 index 0000000..ee73888 --- /dev/null +++ b/package.json @@ -0,0 +1,12 @@ +{ + "name": "buildah-action", + "version": "1.0.0", + "description": "", + "main": "index.js", + "scripts": { + "test": "echo \"Error: no test specified\" && exit 1" + }, + "keywords": [], + "author": "", + "license": "ISC" +} diff --git a/src/buildah.ts b/src/buildah.ts new file mode 100644 index 0000000..1350365 --- /dev/null +++ b/src/buildah.ts @@ -0,0 +1,95 @@ +import * as exec from "@actions/exec"; + +interface Buildah { + from(baseImage?: string): Promise; + copy(container: string, content: string): Promise; + config(container: string, setting: {}): Promise; + commit(container: string, newImageName: string, flags?: string[]): Promise; +} + +export interface BuildahConfigSettings { + author?: string; + annotation?: string; + arch?: string; + created_by?: string; + entrypoint?: string[]; + labels?: string[]; + envs?: string[]; + port?: string; +} + +export interface CommandSucceeeded { + readonly succeeded: true; + readonly output?: string; +} + +export interface CommandFailed { + readonly succeeded: false; + readonly reason?: string; +} + +export type CommandResult = CommandFailed | CommandSucceeeded; + +export class BuildahCli implements Buildah { + private executable: string; + constructor(executable: string) { + this.executable = executable; + } + async from(baseImage?: string): Promise { + if (!baseImage) { + // find correct baseImage based on language project + } + + return await this.execute(['from', baseImage]); + } + async copy(container: string, content: string, path?: string): Promise { + const args: string[] = ["copy", container, content]; + if (path) { + args.push(path); + } + return await this.execute(args); + } + async config(container: string, settings: BuildahConfigSettings): Promise { + const args: string[] = ['config']; + if (settings.entrypoint) { + args.push('--entrypoint'); + args.push(...settings.entrypoint); + } + if (settings.port) { + args.push('--port'); + args.push(...settings.port); + } + args.push(container); + return await this.execute(args); + } + async commit(container: string, newImageName: string, flags: string[] = []): Promise { + const args: string[] = ["commit", ...flags, container, newImageName]; + return await this.execute(args); + } + + + private async execute(args: string[]): Promise { + if (!this.executable) { + return Promise.reject(new Error('Unable to call buildah executable')); + } + + let output = ''; + let error = ''; + + const options: exec.ExecOptions = { + listeners: { + stdout: (data: Buffer) => { + output += data.toString(); + }, + stderr: (data: Buffer) => { + error += data.toString(); + } + } + }; + await exec.exec(`${this.executable}`, args, options); + if (error) { + return Promise.resolve({ succeeded: false, error: error }); + } + return Promise.resolve({ succeeded: true, output: output }); + } +} \ No newline at end of file diff --git a/src/index.ts b/src/index.ts new file mode 100644 index 0000000..587cedb --- /dev/null +++ b/src/index.ts @@ -0,0 +1,69 @@ +import * as core from '@actions/core'; +import * as io from '@actions/io'; +import { BuildahCli, BuildahConfigSettings } from './buildah'; + +/** + * 1. Buildah only works on Linux as the docker action + * 2. Does this action also need to setup buildah? + * 3. Does this action also have the ability to push to a registry? + * + */ +export async function run(): Promise { + const baseImage = core.getInput('base-image'); + const content = core.getInput('content'); + const entrypoint = await getInputList('entrypoint'); + const port = core.getInput('port'); + const newImageName = core.getInput('new-image-name'); + const runnerOS = process.env.RUNNER_OS; + + if (runnerOS !== 'linux') { + throw new Error(`Only supported on linux platform`); + } + // get buildah cli + const buildahPath = await io.which('buildah', true); + + // create image + const cli: BuildahCli = new BuildahCli(buildahPath); + const creationResult = await cli.from(baseImage); + if (creationResult.succeeded === false) { + return Promise.reject(new Error(creationResult.reason)); + } + const containerId = creationResult.output; + + const copyResult = await cli.copy(containerId, content); + if (copyResult.succeeded === false) { + return Promise.reject(new Error(copyResult.reason)); + } + + const configuration: BuildahConfigSettings = { + entrypoint: entrypoint, + port: port + } + const configResult = await cli.config(containerId, configuration); + if (configResult.succeeded === false) { + return Promise.reject(new Error(configResult.reason)); + } + + const commit = await cli.commit(containerId, newImageName, ['--squash']); + if (commit.succeeded === false) { + return Promise.reject(new Error(commit.reason)); + } + + core.setOutput('image', newImageName); +} + +export async function getInputList(name: string, ignoreComma?: boolean): Promise { + const items = core.getInput(name); + if (items == '') { + return []; + } + return items + .split(/\r?\n/) + .filter(x => x) + .reduce( + (acc, line) => acc.concat(!ignoreComma ? line.split(',').filter(x => x) : line).map(pat => pat.trim()), + [] + ); + } + +run().catch(core.setFailed); \ No newline at end of file