Prepare for migrating to project-stacker/stacker-build-action

Signed-off-by: Petu Eusebiu <peusebiu@cisco.com>
This commit is contained in:
Petu Eusebiu 2022-03-28 13:43:04 +03:00
parent 46ac4f48e6
commit 4889bd5260
14 changed files with 25658 additions and 2 deletions

33
.github/workflows/ci.yaml vendored Normal file
View File

@ -0,0 +1,33 @@
on: [push]
jobs:
ci:
runs-on: ubuntu-latest
name: Test stacker-build action
steps:
- uses: actions/checkout@v2
- name: Run stacker-build with all inputs
uses: project-stacker/stacker-build-action@main
with:
stackerfile: 'test/stacker.yaml'
substitutes: 'SUB1=VAR1 SUB2=VAR2 SUB3=VAR3'
layer-type: 'tar squashfs'
- name: Run stacker-build with only substitutes
uses: project-stacker/stacker-build-action@main
with:
stackerfile: 'test/stacker.yaml'
substitutes: 'SUB1=VAR1 SUB2=VAR2 SUB3=VAR3'
- name: Run stacker-build with only layer-type
uses: project-stacker/stacker-build-action@main
with:
stackerfile: 'test/stacker_wo_subs.yaml'
layer-type: 'tar squashfs'
- name: Run stacker-build with only stackerfile
uses: project-stacker/stacker-build-action@main
with:
stackerfile: 'test/stacker_wo_subs.yaml'

1
.gitignore vendored Normal file
View File

@ -0,0 +1 @@
node_modules

View File

@ -1,2 +1,32 @@
# stacker-build-push-action # stacker-build-action
github action build and push container images # [![ci](https://github.com/project-stacker/stacker-build-action/actions/workflows/ci.yaml/badge.svg?branch=main)](https://github.com/project-stacker/stacker-build-action/actions)
stacker build action builds OCI container images via a declarative yaml format.
stacker only works on Linux
For more information about stacker tool see: https://github.com/project-stacker/stacker
## Action Inputs
<a id="dockerfile-build-inputs"></a>
| Input Name | Description | Default |
| ---------- | ----------- | ------- |
| stackerfile | the yaml file to be built as an OCI image, example: [stacker.yaml](./test/stacker.yaml) | stacker.yaml
| layer-type | output layer type (supported values: tar, squashfs), ca be both separated by whitespace | tar
| substitutes | variable substitution in stackerfile, see [stacker.yaml doc](https://github.com/project-stacker/stacker/blob/master/doc/stacker_yaml.md) | None
| version | stacker version to use | latest
Example:
```
- name: Run stacker-build
uses: project-stacker/stacker-build-action@main
with:
stackerfile: 'test/stacker.yaml'
substitutes: 'SUB1=VAR1 SUB2=VAR2 SUB3=VAR3'
layer-type: 'tar squashfs'
```

25
action.yml Normal file
View File

@ -0,0 +1,25 @@
name: 'Stacker build action'
description: 'Builds OCI images via a declarative yaml format.'
inputs:
version:
description: 'Which stacker version to use'
required: false
default: 'latest'
stackerfile: # id of input
description: 'Which stackerfile to build'
required: false
default: 'stacker.yaml'
layer-type:
description: 'Set the output layer type (supported values: tar, squashfs) separated by whitespace'
required: false
default: "tar"
token:
description: 'Used to pull stacker release binaries'
required: false
default: ${{ github.token }}
substitutes:
description: 'The list of subtitutes to make in stacker file separated by whitespace, eg: ONE=1 TWO=2 THREE=3'
required: false
runs:
using: 'node16'
main: 'dist/index.js'

23869
dist/index.js vendored Normal file

File diff suppressed because one or more lines are too long

84
dist/licenses.txt vendored Normal file
View File

@ -0,0 +1,84 @@
@actions/core
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/exec
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/http-client
MIT
Actions Http Client for Node.js
Copyright (c) GitHub, Inc.
All rights reserved.
MIT License
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and
associated documentation files (the "Software"), to deal in the Software without restriction,
including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense,
and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so,
subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED *AS IS*, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT
LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@actions/io
MIT
The MIT License (MIT)
Copyright 2019 GitHub
Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
tunnel
MIT
The MIT License (MIT)
Copyright (c) 2012 Koichi Kobayashi
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
THE SOFTWARE.

1287
package-lock.json generated Normal file

File diff suppressed because it is too large Load Diff

32
package.json Normal file
View File

@ -0,0 +1,32 @@
{
"name": "stacker-build-action",
"version": "1.0.0",
"description": "github action build OCI container images",
"main": "index.js",
"scripts": {
"test": "echo \"Error: no test specified\" && exit 1"
},
"repository": {
"type": "git",
"url": "git+https://github.com/project-stacker/stacker-build-action.git"
},
"keywords": [],
"author": "",
"license": "ISC",
"bugs": {
"url": "https://github.com/project-stacker/stacker-build-action/issues"
},
"homepage": "https://github.com/project-stacker/stacker-build-action#readme",
"dependencies": {
"@actions/core": "^1.6.0",
"@actions/exec": "^1.1.1",
"@actions/github": "^5.0.0",
"@actions/io": "^1.1.2",
"@actions/tool-cache": "^1.7.2",
"@octokit/rest": "^18.12.0",
"octokit": "^1.7.1"
},
"devDependencies": {
"@types/node": "^17.0.23"
}
}

60
src/index.ts Normal file
View File

@ -0,0 +1,60 @@
import * as core from "@actions/core";
import * as io from "@actions/io";
import * as tc from "@actions/tool-cache";
import { StackerCLI } from "./stacker";
import * as installer from "./installer";
export const stackerBin = "stacker";
export const stackerOrg = "project-stacker";
export const stackerRepo = "stacker";
export async function run(): Promise<void> {
if (process.env.RUNNER_OS !== "Linux") {
throw new Error("stacker, and therefore this action, only works on Linux. Please use a Linux runner.");
}
let releaseData = await installer.resolveReleaseData();
let version = releaseData.tag_name
core.info(`Installing stacker version: ${version}`);
// check cache (works just for the same run, not between)
let path = tc.find(stackerBin, version);
if (!path) {
let download = await installer.downloadStacker(releaseData);
await installer.makeAvailableInPath(download, version);
core.info(`${stackerBin} version ${version} installed successfully`);
} else {
core.info(`${stackerBin} version ${version} already installed`)
}
// get stacker cli
const stackerPath = await io.which("stacker", true);
const cli: StackerCLI = new StackerCLI(stackerPath);
// print stacker version
await cli.execute(["--version"], { group: true });
// get stacker file path from input
const stackerfile = core.getInput("stackerfile");
// get substitutes from input
var substitutesList: string[] = [];
const substitutes = core.getInput("substitutes");
if (substitutes != "") {
substitutesList = substitutes.trim().split(/\s+/);
}
var layerTypeList: string[] = [];
const layerType = core.getInput("layer-type");
if (layerType != "") {
layerTypeList = layerType.trim().split(/\s+/);
}
await cli.build(stackerfile, layerTypeList, substitutesList);
}
run().catch(core.setFailed);

68
src/installer.ts Normal file
View File

@ -0,0 +1,68 @@
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as tc from "@actions/tool-cache";
import { Octokit } from "octokit";
import path from "path";
import { stackerBin, stackerRepo, stackerOrg } from "src";
export async function resolveReleaseData() {
let version = core.getInput('version');
let token = core.getInput('token');
const octokit = new Octokit();
let releaseData: any = {}
if ((!version) || (version.toLowerCase() === 'latest')) {
core.info("Get release info for latest version")
releaseData = await octokit.rest.repos.getLatestRelease({
"owner": stackerOrg,
"repo": stackerRepo,
"header": {
authorization: `token ${token}`,
}
});
} else {
core.info(`Get release info for release ${version}`)
releaseData = await octokit.rest.repos.getReleaseByTag({
"owner": stackerOrg,
"repo": stackerRepo,
"tag": version,
});
}
return releaseData.data
}
export async function downloadStacker(releaseData) {
core.info(`Downloading ${stackerBin} from ${releaseData.html_url}`)
let token = core.getInput('token');
let asset = releaseData.assets.find(obj => {
return obj.name == stackerBin
})
const toolDownload = await tc.downloadTool(
asset.url,
undefined,
`token ${token}`,
{
accept: 'application/octet-stream'
}
);
return toolDownload;
}
export async function makeAvailableInPath(download, version) {
core.info(`Cache file ${download} and rename to generic name`);
const cachedPath = await tc.cacheFile(download, stackerBin, stackerBin, version);
const filePath = path.join(cachedPath, stackerBin)
core.info(`Making ${stackerBin} binary executable`);
await exec.exec("chmod", ["+x", filePath]);
core.info(`Make ${cachedPath} available in path`);
core.addPath(cachedPath);
}

109
src/stacker.ts Normal file
View File

@ -0,0 +1,109 @@
import * as core from "@actions/core";
import * as exec from "@actions/exec";
import * as path from "path";
export interface StackerConfig {
workingdir?: string;
}
export type CommandResult = {
exitCode: number
output: string
error: string
};
export class StackerCLI {
private readonly executable: string;
constructor(executable: string) {
this.executable = executable;
}
async build(stackerfile: string, layerType: string[], substitutes: string[]): Promise<CommandResult> {
const args: string[] = ["--debug", "build"];
layerType.forEach((layerType) => {
args.push("--layer-type");
args.push(layerType);
})
substitutes.forEach((substitute) => {
args.push("--substitute");
args.push(substitute);
})
args.push("-f");
args.push(stackerfile);
const res = this.execute(args).then((res) => {
if (res.exitCode == 0) {
core.info("printing oci layout index.json")
exec.exec('/bin/bash -c "cat oci/index.json | jq"', [])
}
return res
})
return res
}
async execute(
args: string[],
execOptions: exec.ExecOptions & { group?: boolean } = {},
): Promise<CommandResult> {
let stdout = "";
let stderr = "";
const finalExecOptions = { ...execOptions };
finalExecOptions.ignoreReturnCode = true; // the return code is processed below
finalExecOptions.listeners = {
stdline: (line): void => {
stdout += line + "\n";
},
errline: (line): void => {
stderr += line + "\n";
},
};
if (execOptions.group) {
const groupName = [this.executable, ...args].join(" ");
core.startGroup(groupName);
}
const execEnv: { [key: string]: string } = {};
Object.entries(process.env).forEach(([key, value]) => {
if (value != null) {
execEnv[key] = value;
}
});
finalExecOptions.env = execEnv;
try {
const exitCode = await exec.exec(this.executable, args, finalExecOptions);
if (execOptions.ignoreReturnCode !== true && exitCode !== 0) {
// Throwing the stderr as part of the Error makes the stderr
// show up in the action outline, which saves some clicking when debugging.
let error = `${path.basename(this.executable)} exited with code ${exitCode}`;
if (stderr) {
error += `\n${stderr}`;
}
throw new Error(error);
}
return {
exitCode, output: stdout, error: stderr,
};
}
finally {
if (execOptions.group) {
core.endGroup();
}
}
}
}

12
test/stacker.yaml Normal file
View File

@ -0,0 +1,12 @@
test:
from:
type: docker
url: docker://centos:latest
run: |
echo "echoing substitutes"
echo "${{SUB1}}"
echo "$SUB2"
echo "$SUB3"
ls -al /
echo "Your layer is ${STACKER_LAYER_NAME}"
echo "success"

View File

@ -0,0 +1,8 @@
test:
from:
type: docker
url: docker://centos:latest
run: |
ls -al /
echo "Your layer is ${STACKER_LAYER_NAME}"
echo "success"

38
tsconfig.json Normal file
View File

@ -0,0 +1,38 @@
{
"compilerOptions": {
// project options
"lib": [
"ESNext",
"dom"
], // specifies which default set of type definitions to use ("DOM", "ES6", etc)
"outDir": "lib", // .js (as well as .d.ts, .js.map, etc.) files will be emitted into this directory.,
"removeComments": true, // Strips all comments from TypeScript files when converting into JavaScript- you rarely read compiled code so this saves space
"target": "ES6", // Target environment. Most modern browsers support ES6, but you may want to set it to newer or older. (defaults to ES3)
// Module resolution
"baseUrl": "./", // Lets you set a base directory to resolve non-absolute module names.
"esModuleInterop": true, // fixes some issues TS originally had with the ES6 spec where TypeScript treats CommonJS/AMD/UMD modules similar to ES6 module
"moduleResolution": "node", // Pretty much always node for modern JS. Other option is "classic"
"paths": {}, // A series of entries which re-map imports to lookup locations relative to the baseUrl
// Source Map
"sourceMap": true, // enables the use of source maps for debuggers and error reporting etc
"sourceRoot": "/", // Specify the location where a debugger should locate TypeScript files instead of relative source locations.
// Strict Checks
"alwaysStrict": true, // Ensures that your files are parsed in the ECMAScript strict mode, and emit use strict for each source file.
"allowUnreachableCode": false, // pick up dead code paths
"noImplicitAny": false, // In some cases where no type annotations are present, TypeScript will fall back to a type of any for a variable when it cannot infer the type.
"strictNullChecks": true, // When strictNullChecks is true, null and undefined have their own distinct types and youll get a type error if you try to use them where a concrete value is expected.
// Linter Checks
"noImplicitReturns": true,
"noUncheckedIndexedAccess": true, // accessing index must always check for undefined
"noUnusedLocals": true, // Report errors on unused local variables.
"noUnusedParameters": true // Report errors on unused parameters in functions
},
"include": ["./**/*.ts"],
"exclude": [
"node_modules/**/*"
]
}