mirror of
https://github.com/docker/actions-toolkit.git
synced 2024-11-23 03:16:09 +08:00
initial implementation
carries most of the logic used across our actions Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
parent
3b6c627995
commit
70326fd842
15
.editorconfig
Normal file
15
.editorconfig
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# This file is for unifying the coding style for different editors and IDEs.
|
||||||
|
# More information at http://editorconfig.org
|
||||||
|
|
||||||
|
root = true
|
||||||
|
|
||||||
|
[*]
|
||||||
|
indent_style = space
|
||||||
|
indent_size = 2
|
||||||
|
end_of_line = lf
|
||||||
|
charset = utf-8
|
||||||
|
trim_trailing_whitespace = true
|
||||||
|
insert_final_newline = true
|
||||||
|
|
||||||
|
[*.md]
|
||||||
|
trim_trailing_whitespace = false
|
23
.eslintrc.json
Normal file
23
.eslintrc.json
Normal file
@ -0,0 +1,23 @@
|
|||||||
|
{
|
||||||
|
"env": {
|
||||||
|
"node": true,
|
||||||
|
"es2021": true,
|
||||||
|
"jest/globals": true
|
||||||
|
},
|
||||||
|
"extends": [
|
||||||
|
"eslint:recommended",
|
||||||
|
"plugin:@typescript-eslint/recommended",
|
||||||
|
"plugin:jest/recommended",
|
||||||
|
"plugin:prettier/recommended"
|
||||||
|
],
|
||||||
|
"parser": "@typescript-eslint/parser",
|
||||||
|
"parserOptions": {
|
||||||
|
"ecmaVersion": "latest",
|
||||||
|
"sourceType": "module"
|
||||||
|
},
|
||||||
|
"plugins": [
|
||||||
|
"@typescript-eslint",
|
||||||
|
"jest",
|
||||||
|
"prettier"
|
||||||
|
]
|
||||||
|
}
|
90
.gitignore
vendored
Normal file
90
.gitignore
vendored
Normal file
@ -0,0 +1,90 @@
|
|||||||
|
node_modules
|
||||||
|
|
||||||
|
# Rest of the file pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
|
||||||
|
# Logs
|
||||||
|
logs
|
||||||
|
*.log
|
||||||
|
npm-debug.log*
|
||||||
|
yarn-debug.log*
|
||||||
|
yarn-error.log*
|
||||||
|
lerna-debug.log*
|
||||||
|
|
||||||
|
# Diagnostic reports (https://nodejs.org/api/report.html)
|
||||||
|
report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
|
||||||
|
|
||||||
|
# Runtime data
|
||||||
|
pids
|
||||||
|
*.pid
|
||||||
|
*.seed
|
||||||
|
*.pid.lock
|
||||||
|
|
||||||
|
# Directory for instrumented libs generated by jscoverage/JSCover
|
||||||
|
lib-cov
|
||||||
|
|
||||||
|
# Coverage directory used by tools like istanbul
|
||||||
|
coverage
|
||||||
|
*.lcov
|
||||||
|
|
||||||
|
# nyc test coverage
|
||||||
|
.nyc_output
|
||||||
|
|
||||||
|
# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
|
||||||
|
.grunt
|
||||||
|
|
||||||
|
# Bower dependency directory (https://bower.io/)
|
||||||
|
bower_components
|
||||||
|
|
||||||
|
# node-waf configuration
|
||||||
|
.lock-wscript
|
||||||
|
|
||||||
|
# Compiled binary addons (https://nodejs.org/api/addons.html)
|
||||||
|
build/Release
|
||||||
|
|
||||||
|
# Dependency directories
|
||||||
|
jspm_packages/
|
||||||
|
|
||||||
|
# TypeScript v1 declaration files
|
||||||
|
typings/
|
||||||
|
|
||||||
|
# TypeScript cache
|
||||||
|
*.tsbuildinfo
|
||||||
|
|
||||||
|
# Optional npm cache directory
|
||||||
|
.npm
|
||||||
|
|
||||||
|
# Optional eslint cache
|
||||||
|
.eslintcache
|
||||||
|
|
||||||
|
# Optional REPL history
|
||||||
|
.node_repl_history
|
||||||
|
|
||||||
|
# Output of 'npm pack'
|
||||||
|
*.tgz
|
||||||
|
|
||||||
|
# Yarn Integrity file
|
||||||
|
.yarn-integrity
|
||||||
|
|
||||||
|
# dotenv environment variables file
|
||||||
|
.env
|
||||||
|
.env.test
|
||||||
|
|
||||||
|
# parcel-bundler cache (https://parceljs.org/)
|
||||||
|
.cache
|
||||||
|
|
||||||
|
# next.js build output
|
||||||
|
.next
|
||||||
|
|
||||||
|
# nuxt.js build output
|
||||||
|
.nuxt
|
||||||
|
|
||||||
|
# vuepress build output
|
||||||
|
.vuepress/dist
|
||||||
|
|
||||||
|
# Serverless directories
|
||||||
|
.serverless/
|
||||||
|
|
||||||
|
# FuseBox cache
|
||||||
|
.fusebox/
|
||||||
|
|
||||||
|
# DynamoDB Local files
|
||||||
|
.dynamodb/
|
11
.prettierrc.json
Normal file
11
.prettierrc.json
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
{
|
||||||
|
"printWidth": 240,
|
||||||
|
"tabWidth": 2,
|
||||||
|
"useTabs": false,
|
||||||
|
"semi": true,
|
||||||
|
"singleQuote": true,
|
||||||
|
"trailingComma": "none",
|
||||||
|
"bracketSpacing": false,
|
||||||
|
"arrowParens": "avoid",
|
||||||
|
"parser": "typescript"
|
||||||
|
}
|
85
__tests__/buildkit.test.ts
Normal file
85
__tests__/buildkit.test.ts
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
import {describe, expect, it, jest, test} from '@jest/globals';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as buildkit from '../src/buildkit';
|
||||||
|
import * as buildx from '../src/buildx';
|
||||||
|
import * as util from '../src/util';
|
||||||
|
|
||||||
|
const tmpdir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-actions-toolkit-')).split(path.sep).join(path.posix.sep);
|
||||||
|
jest.spyOn(util, 'tmpDir').mockImplementation((): string => {
|
||||||
|
return tmpdir;
|
||||||
|
});
|
||||||
|
|
||||||
|
const tmpname = path.join(tmpdir, '.tmpname').split(path.sep).join(path.posix.sep);
|
||||||
|
jest.spyOn(util, 'tmpNameSync').mockImplementation((): string => {
|
||||||
|
return tmpname;
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(buildx, 'inspect').mockImplementation(async (): Promise<buildx.Builder> => {
|
||||||
|
return {
|
||||||
|
name: 'builder2',
|
||||||
|
driver: 'docker-container',
|
||||||
|
'last-activity': new Date('2023-01-16 09:45:23 +0000 UTC'),
|
||||||
|
nodes: [
|
||||||
|
{
|
||||||
|
buildkit: 'v0.11.0',
|
||||||
|
'buildkitd-flags': '--debug --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host',
|
||||||
|
'driver-opts': ['BUILDKIT_STEP_LOG_MAX_SIZE=10485760', 'BUILDKIT_STEP_LOG_MAX_SPEED=10485760', 'JAEGER_TRACE=localhost:6831', 'image=moby/buildkit:latest', 'network=host'],
|
||||||
|
endpoint: 'unix:///var/run/docker.sock',
|
||||||
|
name: 'builder20',
|
||||||
|
platforms: 'linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6',
|
||||||
|
status: 'running'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
};
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getVersion', () => {
|
||||||
|
it('valid', async () => {
|
||||||
|
const version = await buildkit.getVersion('builder2');
|
||||||
|
expect(semver.valid(version)).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('satisfies', () => {
|
||||||
|
test.each([
|
||||||
|
['builder2', '>=0.10.0', true],
|
||||||
|
['builder2', '>0.11.0', false]
|
||||||
|
])('given %p', async (builderName, range, expected) => {
|
||||||
|
expect(await buildkit.satisfies(builderName, range)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getConfig', () => {
|
||||||
|
test.each([
|
||||||
|
['debug = true', false, 'debug = true', false],
|
||||||
|
[`notfound.toml`, true, '', true],
|
||||||
|
[
|
||||||
|
`${path.join(__dirname, 'fixtures', 'buildkitd.toml').split(path.sep).join(path.posix.sep)}`,
|
||||||
|
true,
|
||||||
|
`debug = true
|
||||||
|
[registry."docker.io"]
|
||||||
|
mirrors = ["mirror.gcr.io"]
|
||||||
|
`,
|
||||||
|
false
|
||||||
|
]
|
||||||
|
])('given %p config', async (val, file, exValue, invalid) => {
|
||||||
|
try {
|
||||||
|
let config: string;
|
||||||
|
if (file) {
|
||||||
|
config = await buildkit.getConfigFile(val);
|
||||||
|
} else {
|
||||||
|
config = await buildkit.getConfigInline(val);
|
||||||
|
}
|
||||||
|
expect(true).toBe(!invalid);
|
||||||
|
expect(config).toEqual(tmpname);
|
||||||
|
const configValue = fs.readFileSync(tmpname, 'utf-8');
|
||||||
|
expect(configValue).toEqual(exValue);
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
|
expect(true).toBe(invalid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
298
__tests__/buildx.test.ts
Normal file
298
__tests__/buildx.test.ts
Normal file
@ -0,0 +1,298 @@
|
|||||||
|
import {describe, expect, it, jest, test} from '@jest/globals';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as buildx from '../src/buildx';
|
||||||
|
import * as util from '../src/util';
|
||||||
|
|
||||||
|
const tmpNameSync = path.join('/tmp/.docker-actions-toolkit-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
|
||||||
|
const imageID = 'sha256:bfb45ab72e46908183546477a08f8867fc40cebadd00af54b071b097aed127a9';
|
||||||
|
const metadata = `{
|
||||||
|
"containerimage.config.digest": "sha256:059b68a595b22564a1cbc167af369349fdc2ecc1f7bc092c2235cbf601a795fd",
|
||||||
|
"containerimage.digest": "sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c"
|
||||||
|
}`;
|
||||||
|
|
||||||
|
jest.spyOn(util, 'tmpDir').mockImplementation((): string => {
|
||||||
|
const tmpDir = path.join('/tmp/.docker-actions-toolkit-jest').split(path.sep).join(path.posix.sep);
|
||||||
|
if (!fs.existsSync(tmpDir)) {
|
||||||
|
fs.mkdirSync(tmpDir, {recursive: true});
|
||||||
|
}
|
||||||
|
return tmpDir;
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(util, 'tmpNameSync').mockImplementation((): string => {
|
||||||
|
return tmpNameSync;
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getImageID', () => {
|
||||||
|
it('matches', async () => {
|
||||||
|
const imageIDFile = await buildx.getImageIDFile();
|
||||||
|
await fs.writeFileSync(imageIDFile, imageID);
|
||||||
|
const expected = await buildx.getImageID();
|
||||||
|
expect(expected).toEqual(imageID);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getMetadata', () => {
|
||||||
|
it('matches', async () => {
|
||||||
|
const metadataFile = await buildx.getMetadataFile();
|
||||||
|
await fs.writeFileSync(metadataFile, metadata);
|
||||||
|
const expected = await buildx.getMetadata();
|
||||||
|
expect(expected).toEqual(metadata);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getDigest', () => {
|
||||||
|
it('matches', async () => {
|
||||||
|
const metadataFile = await buildx.getMetadataFile();
|
||||||
|
await fs.writeFileSync(metadataFile, metadata);
|
||||||
|
const expected = await buildx.getDigest(metadata);
|
||||||
|
expect(expected).toEqual('sha256:b09b9482c72371486bb2c1d2c2a2633ed1d0b8389e12c8d52b9e052725c0c83c');
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('hasLocalOrTarExporter', () => {
|
||||||
|
test.each([
|
||||||
|
[['type=registry,ref=user/app'], false],
|
||||||
|
[['type=docker'], false],
|
||||||
|
[['type=local,dest=./release-out'], true],
|
||||||
|
[['type=tar,dest=/tmp/image.tar'], true],
|
||||||
|
[['type=docker', 'type=tar,dest=/tmp/image.tar'], true],
|
||||||
|
[['"type=tar","dest=/tmp/image.tar"'], true],
|
||||||
|
[['" type= local" , dest=./release-out'], true],
|
||||||
|
[['.'], true]
|
||||||
|
])('given %p returns %p', async (outputs: Array<string>, expected: boolean) => {
|
||||||
|
expect(buildx.hasLocalOrTarExporter(outputs)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isAvailable', () => {
|
||||||
|
it('docker cli', async () => {
|
||||||
|
const execSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
await buildx.isAvailable();
|
||||||
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
expect(execSpy).toHaveBeenCalledWith(`docker`, ['buildx'], {
|
||||||
|
silent: true,
|
||||||
|
ignoreReturnCode: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
it('standalone', async () => {
|
||||||
|
const execSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
await buildx.isAvailable(true);
|
||||||
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
expect(execSpy).toHaveBeenCalledWith(`buildx`, [], {
|
||||||
|
silent: true,
|
||||||
|
ignoreReturnCode: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('inspect', () => {
|
||||||
|
it('valid', async () => {
|
||||||
|
const builder = await buildx.inspect('');
|
||||||
|
expect(builder).not.toBeUndefined();
|
||||||
|
expect(builder.name).not.toEqual('');
|
||||||
|
expect(builder.driver).not.toEqual('');
|
||||||
|
expect(builder.nodes).not.toEqual({});
|
||||||
|
}, 100000);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseInspect', () => {
|
||||||
|
// prettier-ignore
|
||||||
|
test.each([
|
||||||
|
[
|
||||||
|
'inspect1.txt',
|
||||||
|
{
|
||||||
|
"name": "builder-5cb467f7-0940-47e1-b94b-d51f54054d62",
|
||||||
|
"driver": "docker-container",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "builder-5cb467f7-0940-47e1-b94b-d51f54054d620",
|
||||||
|
"endpoint": "unix:///var/run/docker.sock",
|
||||||
|
"status": "running",
|
||||||
|
"buildkitd-flags": "--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host",
|
||||||
|
"buildkit": "v0.10.4",
|
||||||
|
"platforms": "linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/amd64/v4,linux/arm64,linux/riscv64,linux/386,linux/arm/v7,linux/arm/v6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'inspect2.txt',
|
||||||
|
{
|
||||||
|
"name": "builder-5f449644-ff29-48af-8344-abb0292d0673",
|
||||||
|
"driver": "docker-container",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "builder-5f449644-ff29-48af-8344-abb0292d06730",
|
||||||
|
"endpoint": "unix:///var/run/docker.sock",
|
||||||
|
"driver-opts": [
|
||||||
|
"image=moby/buildkit:latest"
|
||||||
|
],
|
||||||
|
"status": "running",
|
||||||
|
"buildkitd-flags": "--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host",
|
||||||
|
"buildkit": "v0.10.4",
|
||||||
|
"platforms": "linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/amd64/v4,linux/386"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'inspect3.txt',
|
||||||
|
{
|
||||||
|
"name": "builder-9929e463-7954-4dc3-89cd-514cca29ff80",
|
||||||
|
"driver": "docker-container",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "builder-9929e463-7954-4dc3-89cd-514cca29ff800",
|
||||||
|
"endpoint": "unix:///var/run/docker.sock",
|
||||||
|
"driver-opts": [
|
||||||
|
"image=moby/buildkit:master",
|
||||||
|
"network=host"
|
||||||
|
],
|
||||||
|
"status": "running",
|
||||||
|
"buildkitd-flags": "--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host",
|
||||||
|
"buildkit": "3fab389",
|
||||||
|
"platforms": "linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/amd64/v4,linux/386"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'inspect4.txt',
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"driver": "docker",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "default",
|
||||||
|
"endpoint": "default",
|
||||||
|
"status": "running",
|
||||||
|
"buildkit": "20.10.17",
|
||||||
|
"platforms": "linux/amd64,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/arm/v7,linux/arm/v6"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'inspect5.txt',
|
||||||
|
{
|
||||||
|
"name": "remote-builder",
|
||||||
|
"driver": "remote",
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "aws_graviton2",
|
||||||
|
"endpoint": "tcp://1.23.45.67:1234",
|
||||||
|
"driver-opts": [
|
||||||
|
"cert=/home/user/.certs/aws_graviton2/cert.pem",
|
||||||
|
"key=/home/user/.certs/aws_graviton2/key.pem",
|
||||||
|
"cacert=/home/user/.certs/aws_graviton2/ca.pem"
|
||||||
|
],
|
||||||
|
"status": "running",
|
||||||
|
"platforms": "darwin/arm64,linux/arm64,linux/arm/v5,linux/arm/v6,linux/arm/v7,windows/arm64"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'inspect6.txt',
|
||||||
|
{
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"name": "builder-17cfff01-48d9-4c3d-9332-9992e308a5100",
|
||||||
|
"endpoint": "unix:///var/run/docker.sock",
|
||||||
|
"status": "running",
|
||||||
|
"buildkitd-flags": "--allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host",
|
||||||
|
"platforms": "linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/386"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"name": "builder-17cfff01-48d9-4c3d-9332-9992e308a510",
|
||||||
|
"driver": "docker-container"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
[
|
||||||
|
'inspect7.txt',
|
||||||
|
{
|
||||||
|
"name": "builder2",
|
||||||
|
"driver": "docker-container",
|
||||||
|
"last-activity": new Date("2023-01-16T09:45:23.000Z"),
|
||||||
|
"nodes": [
|
||||||
|
{
|
||||||
|
"buildkit": "v0.11.0",
|
||||||
|
"buildkitd-flags": "--debug --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host",
|
||||||
|
"driver-opts": [
|
||||||
|
"BUILDKIT_STEP_LOG_MAX_SIZE=10485760",
|
||||||
|
"BUILDKIT_STEP_LOG_MAX_SPEED=10485760",
|
||||||
|
"JAEGER_TRACE=localhost:6831",
|
||||||
|
"image=moby/buildkit:latest",
|
||||||
|
"network=host"
|
||||||
|
],
|
||||||
|
"endpoint": "unix:///var/run/docker.sock",
|
||||||
|
"name": "builder20",
|
||||||
|
"platforms": "linux/amd64,linux/amd64/v2,linux/amd64/v3,linux/arm64,linux/riscv64,linux/ppc64le,linux/s390x,linux/386,linux/mips64le,linux/mips64,linux/arm/v7,linux/arm/v6",
|
||||||
|
"status": "running"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
])('given %p', async (inspectFile, expected) => {
|
||||||
|
expect(await buildx.parseInspect(fs.readFileSync(path.join(__dirname, 'fixtures', inspectFile)).toString())).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getVersion', () => {
|
||||||
|
it('valid', async () => {
|
||||||
|
const version = await buildx.getVersion();
|
||||||
|
expect(semver.valid(version)).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('parseVersion', () => {
|
||||||
|
test.each([
|
||||||
|
['github.com/docker/buildx 0.4.1+azure bda4882a65349ca359216b135896bddc1d92461c', '0.4.1'],
|
||||||
|
['github.com/docker/buildx v0.4.1 bda4882a65349ca359216b135896bddc1d92461c', '0.4.1'],
|
||||||
|
['github.com/docker/buildx v0.4.2 fb7b670b764764dc4716df3eba07ffdae4cc47b2', '0.4.2'],
|
||||||
|
['github.com/docker/buildx f117971 f11797113e5a9b86bd976329c5dbb8a8bfdfadfa', 'f117971']
|
||||||
|
])('given %p', async (stdout, expected) => {
|
||||||
|
expect(buildx.parseVersion(stdout)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('satisfies', () => {
|
||||||
|
test.each([
|
||||||
|
['0.4.1', '>=0.3.2', true],
|
||||||
|
['bda4882a65349ca359216b135896bddc1d92461c', '>0.1.0', false],
|
||||||
|
['f117971', '>0.6.0', true]
|
||||||
|
])('given %p', async (version, range, expected) => {
|
||||||
|
expect(buildx.satisfies(version, range)).toBe(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getSecret', () => {
|
||||||
|
test.each([
|
||||||
|
['A_SECRET=abcdef0123456789', false, 'A_SECRET', 'abcdef0123456789', false],
|
||||||
|
['GIT_AUTH_TOKEN=abcdefghijklmno=0123456789', false, 'GIT_AUTH_TOKEN', 'abcdefghijklmno=0123456789', false],
|
||||||
|
['MY_KEY=c3RyaW5nLXdpdGgtZXF1YWxzCg==', false, 'MY_KEY', 'c3RyaW5nLXdpdGgtZXF1YWxzCg==', false],
|
||||||
|
['aaaaaaaa', false, '', '', true],
|
||||||
|
['aaaaaaaa=', false, '', '', true],
|
||||||
|
['=bbbbbbb', false, '', '', true],
|
||||||
|
[`foo=${path.join(__dirname, 'fixtures', 'secret.txt').split(path.sep).join(path.posix.sep)}`, true, 'foo', 'bar', false],
|
||||||
|
[`notfound=secret`, true, '', '', true]
|
||||||
|
])('given %p key and %p secret', async (kvp, file, exKey, exValue, invalid) => {
|
||||||
|
try {
|
||||||
|
let secret: string;
|
||||||
|
if (file) {
|
||||||
|
secret = await buildx.getSecretFile(kvp);
|
||||||
|
} else {
|
||||||
|
secret = await buildx.getSecretString(kvp);
|
||||||
|
}
|
||||||
|
expect(true).toBe(!invalid);
|
||||||
|
expect(secret).toEqual(`id=${exKey},src=${tmpNameSync}`);
|
||||||
|
expect(fs.readFileSync(tmpNameSync, 'utf-8')).toEqual(exValue);
|
||||||
|
} catch (err) {
|
||||||
|
// eslint-disable-next-line jest/no-conditional-expect
|
||||||
|
expect(true).toBe(invalid);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
16
__tests__/docker.test.ts
Normal file
16
__tests__/docker.test.ts
Normal file
@ -0,0 +1,16 @@
|
|||||||
|
import {describe, expect, it, jest} from '@jest/globals';
|
||||||
|
import * as docker from '../src/docker';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
|
||||||
|
describe('isAvailable', () => {
|
||||||
|
it('cli', () => {
|
||||||
|
const execSpy = jest.spyOn(exec, 'getExecOutput');
|
||||||
|
docker.isAvailable();
|
||||||
|
|
||||||
|
// eslint-disable-next-line jest/no-standalone-expect
|
||||||
|
expect(execSpy).toHaveBeenCalledWith(`docker`, undefined, {
|
||||||
|
silent: true,
|
||||||
|
ignoreReturnCode: true
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
3
__tests__/fixtures/buildkitd.toml
Normal file
3
__tests__/fixtures/buildkitd.toml
Normal file
@ -0,0 +1,3 @@
|
|||||||
|
debug = true
|
||||||
|
[registry."docker.io"]
|
||||||
|
mirrors = ["mirror.gcr.io"]
|
10
__tests__/fixtures/inspect1.txt
Normal file
10
__tests__/fixtures/inspect1.txt
Normal file
@ -0,0 +1,10 @@
|
|||||||
|
Name: builder-5cb467f7-0940-47e1-b94b-d51f54054d62
|
||||||
|
Driver: docker-container
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: builder-5cb467f7-0940-47e1-b94b-d51f54054d620
|
||||||
|
Endpoint: unix:///var/run/docker.sock
|
||||||
|
Status: running
|
||||||
|
Flags: --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host
|
||||||
|
Buildkit: v0.10.4
|
||||||
|
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/arm64, linux/riscv64, linux/386, linux/arm/v7, linux/arm/v6
|
11
__tests__/fixtures/inspect2.txt
Normal file
11
__tests__/fixtures/inspect2.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Name: builder-5f449644-ff29-48af-8344-abb0292d0673
|
||||||
|
Driver: docker-container
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: builder-5f449644-ff29-48af-8344-abb0292d06730
|
||||||
|
Endpoint: unix:///var/run/docker.sock
|
||||||
|
Driver Options: image="moby/buildkit:latest"
|
||||||
|
Status: running
|
||||||
|
Flags: --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host
|
||||||
|
Buildkit: v0.10.4
|
||||||
|
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
|
11
__tests__/fixtures/inspect3.txt
Normal file
11
__tests__/fixtures/inspect3.txt
Normal file
@ -0,0 +1,11 @@
|
|||||||
|
Name: builder-9929e463-7954-4dc3-89cd-514cca29ff80
|
||||||
|
Driver: docker-container
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: builder-9929e463-7954-4dc3-89cd-514cca29ff800
|
||||||
|
Endpoint: unix:///var/run/docker.sock
|
||||||
|
Driver Options: image="moby/buildkit:master" network="host"
|
||||||
|
Status: running
|
||||||
|
Flags: --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host
|
||||||
|
Buildkit: 3fab389
|
||||||
|
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/amd64/v4, linux/386
|
9
__tests__/fixtures/inspect4.txt
Normal file
9
__tests__/fixtures/inspect4.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Name: default
|
||||||
|
Driver: docker
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: default
|
||||||
|
Endpoint: default
|
||||||
|
Status: running
|
||||||
|
Buildkit: 20.10.17
|
||||||
|
Platforms: linux/amd64, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/arm/v7, linux/arm/v6
|
9
__tests__/fixtures/inspect5.txt
Normal file
9
__tests__/fixtures/inspect5.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Name: remote-builder
|
||||||
|
Driver: remote
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: aws_graviton2
|
||||||
|
Endpoint: tcp://1.23.45.67:1234
|
||||||
|
Driver Options: cert="/home/user/.certs/aws_graviton2/cert.pem" key="/home/user/.certs/aws_graviton2/key.pem" cacert="/home/user/.certs/aws_graviton2/ca.pem"
|
||||||
|
Status: running
|
||||||
|
Platforms: darwin/arm64*, linux/arm64*, linux/arm/v5*, linux/arm/v6*, linux/arm/v7*, windows/arm64*, linux/amd64, linux/amd64/v2, linux/riscv64, linux/ppc64le, linux/s390x, linux/mips64le, linux/mips64
|
9
__tests__/fixtures/inspect6.txt
Normal file
9
__tests__/fixtures/inspect6.txt
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
Name: builder-17cfff01-48d9-4c3d-9332-9992e308a510
|
||||||
|
Driver: docker-container
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: builder-17cfff01-48d9-4c3d-9332-9992e308a5100
|
||||||
|
Endpoint: unix:///var/run/docker.sock
|
||||||
|
Status: running
|
||||||
|
Flags: --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host
|
||||||
|
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/386
|
12
__tests__/fixtures/inspect7.txt
Normal file
12
__tests__/fixtures/inspect7.txt
Normal file
@ -0,0 +1,12 @@
|
|||||||
|
Name: builder2
|
||||||
|
Driver: docker-container
|
||||||
|
Last Activity: 2023-01-16 09:45:23 +0000 UTC
|
||||||
|
|
||||||
|
Nodes:
|
||||||
|
Name: builder20
|
||||||
|
Endpoint: unix:///var/run/docker.sock
|
||||||
|
Driver Options: env.BUILDKIT_STEP_LOG_MAX_SIZE="10485760" env.BUILDKIT_STEP_LOG_MAX_SPEED="10485760" env.JAEGER_TRACE="localhost:6831" image="moby/buildkit:latest" network="host"
|
||||||
|
Status: running
|
||||||
|
Flags: --debug --allow-insecure-entitlement security.insecure --allow-insecure-entitlement network.host
|
||||||
|
Buildkit: v0.11.0
|
||||||
|
Platforms: linux/amd64, linux/amd64/v2, linux/amd64/v3, linux/arm64, linux/riscv64, linux/ppc64le, linux/s390x, linux/386, linux/mips64le, linux/mips64, linux/arm/v7, linux/arm/v6
|
106
__tests__/fixtures/pgp.txt
Normal file
106
__tests__/fixtures/pgp.txt
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
|
||||||
|
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
|
||||||
|
PejgXO0uIRolYQ3sz2tMGhx1MfBqH64=
|
||||||
|
=WbwB
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----
|
362
__tests__/fixtures/repo.json
Normal file
362
__tests__/fixtures/repo.json
Normal file
@ -0,0 +1,362 @@
|
|||||||
|
{
|
||||||
|
"id": 1296269,
|
||||||
|
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
|
||||||
|
"name": "Hello-World",
|
||||||
|
"full_name": "octocat/Hello-World",
|
||||||
|
"owner": {
|
||||||
|
"login": "octocat",
|
||||||
|
"id": 1,
|
||||||
|
"node_id": "MDQ6VXNlcjE=",
|
||||||
|
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
|
||||||
|
"gravatar_id": "",
|
||||||
|
"url": "https://api.github.com/users/octocat",
|
||||||
|
"html_url": "https://github.com/octocat",
|
||||||
|
"followers_url": "https://api.github.com/users/octocat/followers",
|
||||||
|
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
|
||||||
|
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
|
||||||
|
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
|
||||||
|
"organizations_url": "https://api.github.com/users/octocat/orgs",
|
||||||
|
"repos_url": "https://api.github.com/users/octocat/repos",
|
||||||
|
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
|
||||||
|
"received_events_url": "https://api.github.com/users/octocat/received_events",
|
||||||
|
"type": "User",
|
||||||
|
"site_admin": false
|
||||||
|
},
|
||||||
|
"private": false,
|
||||||
|
"html_url": "https://github.com/octocat/Hello-World",
|
||||||
|
"description": "This your first repo!",
|
||||||
|
"fork": false,
|
||||||
|
"url": "https://api.github.com/repos/octocat/Hello-World",
|
||||||
|
"archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
|
||||||
|
"assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}",
|
||||||
|
"blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
|
||||||
|
"branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}",
|
||||||
|
"collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
|
||||||
|
"comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}",
|
||||||
|
"commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}",
|
||||||
|
"compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
|
||||||
|
"contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}",
|
||||||
|
"contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors",
|
||||||
|
"deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments",
|
||||||
|
"downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads",
|
||||||
|
"events_url": "http://api.github.com/repos/octocat/Hello-World/events",
|
||||||
|
"forks_url": "http://api.github.com/repos/octocat/Hello-World/forks",
|
||||||
|
"git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
|
||||||
|
"git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
|
||||||
|
"git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
|
||||||
|
"git_url": "git:github.com/octocat/Hello-World.git",
|
||||||
|
"issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
|
||||||
|
"issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
|
||||||
|
"issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}",
|
||||||
|
"keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
|
||||||
|
"labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}",
|
||||||
|
"languages_url": "http://api.github.com/repos/octocat/Hello-World/languages",
|
||||||
|
"merges_url": "http://api.github.com/repos/octocat/Hello-World/merges",
|
||||||
|
"milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}",
|
||||||
|
"notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
|
||||||
|
"pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}",
|
||||||
|
"releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}",
|
||||||
|
"ssh_url": "git@github.com:octocat/Hello-World.git",
|
||||||
|
"stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers",
|
||||||
|
"statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
|
||||||
|
"subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers",
|
||||||
|
"subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription",
|
||||||
|
"tags_url": "http://api.github.com/repos/octocat/Hello-World/tags",
|
||||||
|
"teams_url": "http://api.github.com/repos/octocat/Hello-World/teams",
|
||||||
|
"trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
|
||||||
|
"clone_url": "https://github.com/octocat/Hello-World.git",
|
||||||
|
"mirror_url": "git:git.example.com/octocat/Hello-World",
|
||||||
|
"hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks",
|
||||||
|
"svn_url": "https://svn.github.com/octocat/Hello-World",
|
||||||
|
"homepage": "https://github.com",
|
||||||
|
"language": null,
|
||||||
|
"forks_count": 9,
|
||||||
|
"stargazers_count": 80,
|
||||||
|
"watchers_count": 80,
|
||||||
|
"size": 108,
|
||||||
|
"default_branch": "master",
|
||||||
|
"open_issues_count": 0,
|
||||||
|
"is_template": true,
|
||||||
|
"topics": [
|
||||||
|
"octocat",
|
||||||
|
"atom",
|
||||||
|
"electron",
|
||||||
|
"api"
|
||||||
|
],
|
||||||
|
"has_issues": true,
|
||||||
|
"has_projects": true,
|
||||||
|
"has_wiki": true,
|
||||||
|
"has_pages": false,
|
||||||
|
"has_downloads": true,
|
||||||
|
"archived": false,
|
||||||
|
"disabled": false,
|
||||||
|
"visibility": "public",
|
||||||
|
"pushed_at": "2011-01-26T19:06:43Z",
|
||||||
|
"created_at": "2011-01-26T19:01:12Z",
|
||||||
|
"updated_at": "2011-01-26T19:14:43Z",
|
||||||
|
"permissions": {
|
||||||
|
"pull": true,
|
||||||
|
"triage": true,
|
||||||
|
"push": false,
|
||||||
|
"maintain": false,
|
||||||
|
"admin": false
|
||||||
|
},
|
||||||
|
"allow_rebase_merge": true,
|
||||||
|
"template_repository": null,
|
||||||
|
"temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O",
|
||||||
|
"allow_squash_merge": true,
|
||||||
|
"delete_branch_on_merge": true,
|
||||||
|
"allow_merge_commit": true,
|
||||||
|
"subscribers_count": 42,
|
||||||
|
"network_count": 0,
|
||||||
|
"license": {
|
||||||
|
"key": "mit",
|
||||||
|
"name": "MIT License",
|
||||||
|
"spdx_id": "MIT",
|
||||||
|
"url": "https://api.github.com/licenses/mit",
|
||||||
|
"node_id": "MDc6TGljZW5zZW1pdA=="
|
||||||
|
},
|
||||||
|
"organization": {
|
||||||
|
"login": "octocat",
|
||||||
|
"id": 1,
|
||||||
|
"node_id": "MDQ6VXNlcjE=",
|
||||||
|
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
|
||||||
|
"gravatar_id": "",
|
||||||
|
"url": "https://api.github.com/users/octocat",
|
||||||
|
"html_url": "https://github.com/octocat",
|
||||||
|
"followers_url": "https://api.github.com/users/octocat/followers",
|
||||||
|
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
|
||||||
|
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
|
||||||
|
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
|
||||||
|
"organizations_url": "https://api.github.com/users/octocat/orgs",
|
||||||
|
"repos_url": "https://api.github.com/users/octocat/repos",
|
||||||
|
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
|
||||||
|
"received_events_url": "https://api.github.com/users/octocat/received_events",
|
||||||
|
"type": "Organization",
|
||||||
|
"site_admin": false
|
||||||
|
},
|
||||||
|
"parent": {
|
||||||
|
"id": 1296269,
|
||||||
|
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
|
||||||
|
"name": "Hello-World",
|
||||||
|
"full_name": "octocat/Hello-World",
|
||||||
|
"owner": {
|
||||||
|
"login": "octocat",
|
||||||
|
"id": 1,
|
||||||
|
"node_id": "MDQ6VXNlcjE=",
|
||||||
|
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
|
||||||
|
"gravatar_id": "",
|
||||||
|
"url": "https://api.github.com/users/octocat",
|
||||||
|
"html_url": "https://github.com/octocat",
|
||||||
|
"followers_url": "https://api.github.com/users/octocat/followers",
|
||||||
|
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
|
||||||
|
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
|
||||||
|
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
|
||||||
|
"organizations_url": "https://api.github.com/users/octocat/orgs",
|
||||||
|
"repos_url": "https://api.github.com/users/octocat/repos",
|
||||||
|
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
|
||||||
|
"received_events_url": "https://api.github.com/users/octocat/received_events",
|
||||||
|
"type": "User",
|
||||||
|
"site_admin": false
|
||||||
|
},
|
||||||
|
"private": false,
|
||||||
|
"html_url": "https://github.com/octocat/Hello-World",
|
||||||
|
"description": "This your first repo!",
|
||||||
|
"fork": false,
|
||||||
|
"url": "https://api.github.com/repos/octocat/Hello-World",
|
||||||
|
"archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
|
||||||
|
"assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}",
|
||||||
|
"blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
|
||||||
|
"branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}",
|
||||||
|
"collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
|
||||||
|
"comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}",
|
||||||
|
"commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}",
|
||||||
|
"compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
|
||||||
|
"contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}",
|
||||||
|
"contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors",
|
||||||
|
"deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments",
|
||||||
|
"downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads",
|
||||||
|
"events_url": "http://api.github.com/repos/octocat/Hello-World/events",
|
||||||
|
"forks_url": "http://api.github.com/repos/octocat/Hello-World/forks",
|
||||||
|
"git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
|
||||||
|
"git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
|
||||||
|
"git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
|
||||||
|
"git_url": "git:github.com/octocat/Hello-World.git",
|
||||||
|
"issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
|
||||||
|
"issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
|
||||||
|
"issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}",
|
||||||
|
"keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
|
||||||
|
"labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}",
|
||||||
|
"languages_url": "http://api.github.com/repos/octocat/Hello-World/languages",
|
||||||
|
"merges_url": "http://api.github.com/repos/octocat/Hello-World/merges",
|
||||||
|
"milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}",
|
||||||
|
"notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
|
||||||
|
"pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}",
|
||||||
|
"releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}",
|
||||||
|
"ssh_url": "git@github.com:octocat/Hello-World.git",
|
||||||
|
"stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers",
|
||||||
|
"statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
|
||||||
|
"subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers",
|
||||||
|
"subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription",
|
||||||
|
"tags_url": "http://api.github.com/repos/octocat/Hello-World/tags",
|
||||||
|
"teams_url": "http://api.github.com/repos/octocat/Hello-World/teams",
|
||||||
|
"trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
|
||||||
|
"clone_url": "https://github.com/octocat/Hello-World.git",
|
||||||
|
"mirror_url": "git:git.example.com/octocat/Hello-World",
|
||||||
|
"hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks",
|
||||||
|
"svn_url": "https://svn.github.com/octocat/Hello-World",
|
||||||
|
"homepage": "https://github.com",
|
||||||
|
"language": null,
|
||||||
|
"forks_count": 9,
|
||||||
|
"stargazers_count": 80,
|
||||||
|
"watchers_count": 80,
|
||||||
|
"size": 108,
|
||||||
|
"default_branch": "master",
|
||||||
|
"open_issues_count": 0,
|
||||||
|
"is_template": true,
|
||||||
|
"topics": [
|
||||||
|
"octocat",
|
||||||
|
"atom",
|
||||||
|
"electron",
|
||||||
|
"api"
|
||||||
|
],
|
||||||
|
"has_issues": true,
|
||||||
|
"has_projects": true,
|
||||||
|
"has_wiki": true,
|
||||||
|
"has_pages": false,
|
||||||
|
"has_downloads": true,
|
||||||
|
"archived": false,
|
||||||
|
"disabled": false,
|
||||||
|
"visibility": "public",
|
||||||
|
"pushed_at": "2011-01-26T19:06:43Z",
|
||||||
|
"created_at": "2011-01-26T19:01:12Z",
|
||||||
|
"updated_at": "2011-01-26T19:14:43Z",
|
||||||
|
"permissions": {
|
||||||
|
"admin": false,
|
||||||
|
"push": false,
|
||||||
|
"pull": true
|
||||||
|
},
|
||||||
|
"allow_rebase_merge": true,
|
||||||
|
"template_repository": null,
|
||||||
|
"temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O",
|
||||||
|
"allow_squash_merge": true,
|
||||||
|
"delete_branch_on_merge": true,
|
||||||
|
"allow_merge_commit": true,
|
||||||
|
"subscribers_count": 42,
|
||||||
|
"network_count": 0
|
||||||
|
},
|
||||||
|
"source": {
|
||||||
|
"id": 1296269,
|
||||||
|
"node_id": "MDEwOlJlcG9zaXRvcnkxMjk2MjY5",
|
||||||
|
"name": "Hello-World",
|
||||||
|
"full_name": "octocat/Hello-World",
|
||||||
|
"owner": {
|
||||||
|
"login": "octocat",
|
||||||
|
"id": 1,
|
||||||
|
"node_id": "MDQ6VXNlcjE=",
|
||||||
|
"avatar_url": "https://github.com/images/error/octocat_happy.gif",
|
||||||
|
"gravatar_id": "",
|
||||||
|
"url": "https://api.github.com/users/octocat",
|
||||||
|
"html_url": "https://github.com/octocat",
|
||||||
|
"followers_url": "https://api.github.com/users/octocat/followers",
|
||||||
|
"following_url": "https://api.github.com/users/octocat/following{/other_user}",
|
||||||
|
"gists_url": "https://api.github.com/users/octocat/gists{/gist_id}",
|
||||||
|
"starred_url": "https://api.github.com/users/octocat/starred{/owner}{/repo}",
|
||||||
|
"subscriptions_url": "https://api.github.com/users/octocat/subscriptions",
|
||||||
|
"organizations_url": "https://api.github.com/users/octocat/orgs",
|
||||||
|
"repos_url": "https://api.github.com/users/octocat/repos",
|
||||||
|
"events_url": "https://api.github.com/users/octocat/events{/privacy}",
|
||||||
|
"received_events_url": "https://api.github.com/users/octocat/received_events",
|
||||||
|
"type": "User",
|
||||||
|
"site_admin": false
|
||||||
|
},
|
||||||
|
"private": false,
|
||||||
|
"html_url": "https://github.com/octocat/Hello-World",
|
||||||
|
"description": "This your first repo!",
|
||||||
|
"fork": false,
|
||||||
|
"url": "https://api.github.com/repos/octocat/Hello-World",
|
||||||
|
"archive_url": "http://api.github.com/repos/octocat/Hello-World/{archive_format}{/ref}",
|
||||||
|
"assignees_url": "http://api.github.com/repos/octocat/Hello-World/assignees{/user}",
|
||||||
|
"blobs_url": "http://api.github.com/repos/octocat/Hello-World/git/blobs{/sha}",
|
||||||
|
"branches_url": "http://api.github.com/repos/octocat/Hello-World/branches{/branch}",
|
||||||
|
"collaborators_url": "http://api.github.com/repos/octocat/Hello-World/collaborators{/collaborator}",
|
||||||
|
"comments_url": "http://api.github.com/repos/octocat/Hello-World/comments{/number}",
|
||||||
|
"commits_url": "http://api.github.com/repos/octocat/Hello-World/commits{/sha}",
|
||||||
|
"compare_url": "http://api.github.com/repos/octocat/Hello-World/compare/{base}...{head}",
|
||||||
|
"contents_url": "http://api.github.com/repos/octocat/Hello-World/contents/{+path}",
|
||||||
|
"contributors_url": "http://api.github.com/repos/octocat/Hello-World/contributors",
|
||||||
|
"deployments_url": "http://api.github.com/repos/octocat/Hello-World/deployments",
|
||||||
|
"downloads_url": "http://api.github.com/repos/octocat/Hello-World/downloads",
|
||||||
|
"events_url": "http://api.github.com/repos/octocat/Hello-World/events",
|
||||||
|
"forks_url": "http://api.github.com/repos/octocat/Hello-World/forks",
|
||||||
|
"git_commits_url": "http://api.github.com/repos/octocat/Hello-World/git/commits{/sha}",
|
||||||
|
"git_refs_url": "http://api.github.com/repos/octocat/Hello-World/git/refs{/sha}",
|
||||||
|
"git_tags_url": "http://api.github.com/repos/octocat/Hello-World/git/tags{/sha}",
|
||||||
|
"git_url": "git:github.com/octocat/Hello-World.git",
|
||||||
|
"issue_comment_url": "http://api.github.com/repos/octocat/Hello-World/issues/comments{/number}",
|
||||||
|
"issue_events_url": "http://api.github.com/repos/octocat/Hello-World/issues/events{/number}",
|
||||||
|
"issues_url": "http://api.github.com/repos/octocat/Hello-World/issues{/number}",
|
||||||
|
"keys_url": "http://api.github.com/repos/octocat/Hello-World/keys{/key_id}",
|
||||||
|
"labels_url": "http://api.github.com/repos/octocat/Hello-World/labels{/name}",
|
||||||
|
"languages_url": "http://api.github.com/repos/octocat/Hello-World/languages",
|
||||||
|
"merges_url": "http://api.github.com/repos/octocat/Hello-World/merges",
|
||||||
|
"milestones_url": "http://api.github.com/repos/octocat/Hello-World/milestones{/number}",
|
||||||
|
"notifications_url": "http://api.github.com/repos/octocat/Hello-World/notifications{?since,all,participating}",
|
||||||
|
"pulls_url": "http://api.github.com/repos/octocat/Hello-World/pulls{/number}",
|
||||||
|
"releases_url": "http://api.github.com/repos/octocat/Hello-World/releases{/id}",
|
||||||
|
"ssh_url": "git@github.com:octocat/Hello-World.git",
|
||||||
|
"stargazers_url": "http://api.github.com/repos/octocat/Hello-World/stargazers",
|
||||||
|
"statuses_url": "http://api.github.com/repos/octocat/Hello-World/statuses/{sha}",
|
||||||
|
"subscribers_url": "http://api.github.com/repos/octocat/Hello-World/subscribers",
|
||||||
|
"subscription_url": "http://api.github.com/repos/octocat/Hello-World/subscription",
|
||||||
|
"tags_url": "http://api.github.com/repos/octocat/Hello-World/tags",
|
||||||
|
"teams_url": "http://api.github.com/repos/octocat/Hello-World/teams",
|
||||||
|
"trees_url": "http://api.github.com/repos/octocat/Hello-World/git/trees{/sha}",
|
||||||
|
"clone_url": "https://github.com/octocat/Hello-World.git",
|
||||||
|
"mirror_url": "git:git.example.com/octocat/Hello-World",
|
||||||
|
"hooks_url": "http://api.github.com/repos/octocat/Hello-World/hooks",
|
||||||
|
"svn_url": "https://svn.github.com/octocat/Hello-World",
|
||||||
|
"homepage": "https://github.com",
|
||||||
|
"language": null,
|
||||||
|
"forks_count": 9,
|
||||||
|
"stargazers_count": 80,
|
||||||
|
"watchers_count": 80,
|
||||||
|
"size": 108,
|
||||||
|
"default_branch": "master",
|
||||||
|
"open_issues_count": 0,
|
||||||
|
"is_template": true,
|
||||||
|
"topics": [
|
||||||
|
"octocat",
|
||||||
|
"atom",
|
||||||
|
"electron",
|
||||||
|
"api"
|
||||||
|
],
|
||||||
|
"has_issues": true,
|
||||||
|
"has_projects": true,
|
||||||
|
"has_wiki": true,
|
||||||
|
"has_pages": false,
|
||||||
|
"has_downloads": true,
|
||||||
|
"archived": false,
|
||||||
|
"disabled": false,
|
||||||
|
"visibility": "public",
|
||||||
|
"pushed_at": "2011-01-26T19:06:43Z",
|
||||||
|
"created_at": "2011-01-26T19:01:12Z",
|
||||||
|
"updated_at": "2011-01-26T19:14:43Z",
|
||||||
|
"permissions": {
|
||||||
|
"admin": false,
|
||||||
|
"push": false,
|
||||||
|
"pull": true
|
||||||
|
},
|
||||||
|
"allow_rebase_merge": true,
|
||||||
|
"template_repository": null,
|
||||||
|
"temp_clone_token": "ABTLWHOULUVAXGTRYU7OC2876QJ2O",
|
||||||
|
"allow_squash_merge": true,
|
||||||
|
"delete_branch_on_merge": true,
|
||||||
|
"allow_merge_commit": true,
|
||||||
|
"subscribers_count": 42,
|
||||||
|
"network_count": 0
|
||||||
|
}
|
||||||
|
}
|
1
__tests__/fixtures/secret.txt
Normal file
1
__tests__/fixtures/secret.txt
Normal file
@ -0,0 +1 @@
|
|||||||
|
bar
|
9
__tests__/git.test.ts
Normal file
9
__tests__/git.test.ts
Normal file
@ -0,0 +1,9 @@
|
|||||||
|
import {describe, expect, it} from '@jest/globals';
|
||||||
|
import * as git from '../src/git';
|
||||||
|
|
||||||
|
describe('git', () => {
|
||||||
|
it('returns git remote ref', async () => {
|
||||||
|
const ref: string = await git.getRemoteSha('https://github.com/docker/buildx.git', 'refs/pull/648/head');
|
||||||
|
expect(ref).toEqual('f11797113e5a9b86bd976329c5dbb8a8bfdfadfa');
|
||||||
|
});
|
||||||
|
});
|
14
__tests__/github.test.ts
Normal file
14
__tests__/github.test.ts
Normal file
@ -0,0 +1,14 @@
|
|||||||
|
import {describe, expect, jest, it} from '@jest/globals';
|
||||||
|
import * as github from '../src/github';
|
||||||
|
|
||||||
|
import * as repoFixture from './fixtures/repo.json';
|
||||||
|
jest.spyOn(github, 'repo').mockImplementation((): Promise<github.ReposGetResponseData> => {
|
||||||
|
return <Promise<github.ReposGetResponseData>>(repoFixture as unknown);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('repo', () => {
|
||||||
|
it('returns GitHub repository', async () => {
|
||||||
|
const repo = await github.repo(process.env.GITHUB_TOKEN || '');
|
||||||
|
expect(repo.name).not.toBeNull();
|
||||||
|
});
|
||||||
|
});
|
197
__tests__/util.test.ts
Normal file
197
__tests__/util.test.ts
Normal file
@ -0,0 +1,197 @@
|
|||||||
|
import {describe, expect, it, jest, test} from '@jest/globals';
|
||||||
|
import * as fs from 'fs';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as util from '../src/util';
|
||||||
|
|
||||||
|
jest.spyOn(util, 'defaultContext').mockImplementation((): string => {
|
||||||
|
return 'https://github.com/docker/actions-toolkit.git#refs/heads/test-jest';
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(util, 'tmpDir').mockImplementation((): string => {
|
||||||
|
const tmpDir = path.join('/tmp/.docker-build-push-jest').split(path.sep).join(path.posix.sep);
|
||||||
|
if (!fs.existsSync(tmpDir)) {
|
||||||
|
fs.mkdirSync(tmpDir, {recursive: true});
|
||||||
|
}
|
||||||
|
return tmpDir;
|
||||||
|
});
|
||||||
|
|
||||||
|
jest.spyOn(util, 'tmpNameSync').mockImplementation((): string => {
|
||||||
|
return path.join('/tmp/.docker-build-push-jest', '.tmpname-jest').split(path.sep).join(path.posix.sep);
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('getInputList', () => {
|
||||||
|
it('single line correctly', async () => {
|
||||||
|
await setInput('foo', 'bar');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline correctly', async () => {
|
||||||
|
setInput('foo', 'bar\nbaz');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('empty lines correctly', async () => {
|
||||||
|
setInput('foo', 'bar\n\nbaz');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('comma correctly', async () => {
|
||||||
|
setInput('foo', 'bar,baz');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('empty result correctly', async () => {
|
||||||
|
setInput('foo', 'bar,baz,');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('different new lines correctly', async () => {
|
||||||
|
setInput('foo', 'bar\r\nbaz');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar', 'baz']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('different new lines and comma correctly', async () => {
|
||||||
|
setInput('foo', 'bar\r\nbaz,bat');
|
||||||
|
const res = util.getInputList('foo');
|
||||||
|
expect(res).toEqual(['bar', 'baz', 'bat']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline and ignoring comma correctly', async () => {
|
||||||
|
setInput('cache-from', 'user/app:cache\ntype=local,src=path/to/dir');
|
||||||
|
const res = util.getInputList('cache-from', true);
|
||||||
|
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('different new lines and ignoring comma correctly', async () => {
|
||||||
|
setInput('cache-from', 'user/app:cache\r\ntype=local,src=path/to/dir');
|
||||||
|
const res = util.getInputList('cache-from', true);
|
||||||
|
expect(res).toEqual(['user/app:cache', 'type=local,src=path/to/dir']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = util.getInputList('secrets', true);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
`MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc`,
|
||||||
|
'FOO=bar'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values with empty lines', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar
|
||||||
|
"EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc"`
|
||||||
|
);
|
||||||
|
const res = util.getInputList('secrets', true);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
`MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc`,
|
||||||
|
'FOO=bar',
|
||||||
|
`EMPTYLINE=aaaa
|
||||||
|
|
||||||
|
bbbb
|
||||||
|
ccc`
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values without quotes', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
MYSECRET=aaaaaaaa
|
||||||
|
bbbbbbb
|
||||||
|
ccccccccc
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = util.getInputList('secrets', true);
|
||||||
|
expect(res).toEqual(['GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789', 'MYSECRET=aaaaaaaa', 'bbbbbbb', 'ccccccccc', 'FOO=bar']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('large multiline values', async () => {
|
||||||
|
const pgp = fs.readFileSync(path.join(__dirname, 'fixtures', 'pgp.txt'), {encoding: 'utf-8'});
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`"GPG_KEY=${pgp}"
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = util.getInputList('secrets', true);
|
||||||
|
expect(res).toEqual([`GPG_KEY=${pgp}`, 'FOO=bar']);
|
||||||
|
});
|
||||||
|
|
||||||
|
it('multiline values escape quotes', async () => {
|
||||||
|
setInput(
|
||||||
|
'secrets',
|
||||||
|
`GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789
|
||||||
|
"MYSECRET=aaaaaaaa
|
||||||
|
bbbb""bbb
|
||||||
|
ccccccccc"
|
||||||
|
FOO=bar`
|
||||||
|
);
|
||||||
|
const res = util.getInputList('secrets', true);
|
||||||
|
expect(res).toEqual([
|
||||||
|
'GIT_AUTH_TOKEN=abcdefgh,ijklmno=0123456789',
|
||||||
|
`MYSECRET=aaaaaaaa
|
||||||
|
bbbb"bbb
|
||||||
|
ccccccccc`,
|
||||||
|
'FOO=bar'
|
||||||
|
]);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('asyncForEach', () => {
|
||||||
|
it('executes async tasks sequentially', async () => {
|
||||||
|
const testValues = [1, 2, 3, 4, 5];
|
||||||
|
const results: number[] = [];
|
||||||
|
|
||||||
|
await util.asyncForEach(testValues, async value => {
|
||||||
|
results.push(value);
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(results).toEqual(testValues);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
describe('isValidUrl', () => {
|
||||||
|
test.each([
|
||||||
|
['https://github.com/docker/buildx.git', true],
|
||||||
|
['https://github.com/docker/buildx.git#refs/pull/648/head', true],
|
||||||
|
['v0.4.1', false]
|
||||||
|
])('given %p', async (url, expected) => {
|
||||||
|
expect(util.isValidUrl(url)).toEqual(expected);
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
// See: https://github.com/actions/toolkit/blob/a1b068ec31a042ff1e10a522d8fdf0b8869d53ca/packages/core/src/core.ts#L89
|
||||||
|
function getInputName(name: string): string {
|
||||||
|
return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
function setInput(name: string, value: string): void {
|
||||||
|
process.env[getInputName(name)] = value;
|
||||||
|
}
|
21
jest.config.ts
Normal file
21
jest.config.ts
Normal file
@ -0,0 +1,21 @@
|
|||||||
|
process.env = Object.assign({}, process.env, {
|
||||||
|
RUNNER_TEMP: '/tmp/github_runner',
|
||||||
|
RUNNER_TOOL_CACHE: '/tmp/github_tool_cache'
|
||||||
|
}) as {
|
||||||
|
[key: string]: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
module.exports = {
|
||||||
|
clearMocks: true,
|
||||||
|
testEnvironment: 'node',
|
||||||
|
moduleFileExtensions: ['js', 'ts'],
|
||||||
|
setupFiles: ['dotenv/config'],
|
||||||
|
testMatch: ['**/*.test.ts'],
|
||||||
|
transform: {
|
||||||
|
'^.+\\.ts$': 'ts-jest'
|
||||||
|
},
|
||||||
|
moduleNameMapper: {
|
||||||
|
'^csv-parse/sync': '<rootDir>/node_modules/csv-parse/dist/cjs/sync.cjs'
|
||||||
|
},
|
||||||
|
verbose: true
|
||||||
|
};
|
57
package.json
Normal file
57
package.json
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
{
|
||||||
|
"name": "@docker/actions-toolkit",
|
||||||
|
"description": "Toolkit for Docker (GitHub) Actions",
|
||||||
|
"scripts": {
|
||||||
|
"build": "tsc --build",
|
||||||
|
"prepack": "rimraf ./dist && tsc --build",
|
||||||
|
"lint": "eslint src/**/*.ts __tests__/**/*.ts",
|
||||||
|
"format": "eslint --fix src/**/*.ts __tests__/**/*.ts",
|
||||||
|
"test": "jest --coverage"
|
||||||
|
},
|
||||||
|
"repository": {
|
||||||
|
"type": "git",
|
||||||
|
"url": "git+https://github.com/docker/actions-toolkit.git"
|
||||||
|
},
|
||||||
|
"keywords": [
|
||||||
|
"actions",
|
||||||
|
"docker",
|
||||||
|
"build",
|
||||||
|
"push"
|
||||||
|
],
|
||||||
|
"author": "Docker",
|
||||||
|
"contributors": [
|
||||||
|
{
|
||||||
|
"name": "CrazyMax",
|
||||||
|
"url": "https://crazymax.dev"
|
||||||
|
}
|
||||||
|
],
|
||||||
|
"license": "Apache-2.0",
|
||||||
|
"dependencies": {
|
||||||
|
"@actions/core": "^1.10.0",
|
||||||
|
"@actions/exec": "^1.1.1",
|
||||||
|
"@actions/github": "^5.1.1",
|
||||||
|
"csv-parse": "^5.3.3",
|
||||||
|
"jwt-decode": "^3.1.2",
|
||||||
|
"semver": "^7.3.7",
|
||||||
|
"tmp": "^0.2.1"
|
||||||
|
},
|
||||||
|
"devDependencies": {
|
||||||
|
"@types/csv-parse": "^1.2.2",
|
||||||
|
"@types/node": "^16.11.26",
|
||||||
|
"@types/semver": "^7.3.9",
|
||||||
|
"@types/tmp": "^0.2.3",
|
||||||
|
"@typescript-eslint/eslint-plugin": "^5.14.0",
|
||||||
|
"@typescript-eslint/parser": "^5.14.0",
|
||||||
|
"dotenv": "^16.0.0",
|
||||||
|
"eslint": "^8.11.0",
|
||||||
|
"eslint-config-prettier": "^8.5.0",
|
||||||
|
"eslint-plugin-jest": "^26.1.1",
|
||||||
|
"eslint-plugin-prettier": "^4.0.0",
|
||||||
|
"jest": "^27.2.5",
|
||||||
|
"prettier": "^2.3.1",
|
||||||
|
"rimraf": "^3.0.2",
|
||||||
|
"ts-jest": "^27.1.2",
|
||||||
|
"ts-node": "^10.7.0",
|
||||||
|
"typescript": "^4.4.4"
|
||||||
|
}
|
||||||
|
}
|
96
src/buildkit.ts
Normal file
96
src/buildkit.ts
Normal file
@ -0,0 +1,96 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as buildx from './buildx';
|
||||||
|
import * as util from './util';
|
||||||
|
|
||||||
|
export async function getConfigInline(s: string): Promise<string> {
|
||||||
|
return getConfig(s, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getConfigFile(s: string): Promise<string> {
|
||||||
|
return getConfig(s, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getConfig(s: string, file: boolean): Promise<string> {
|
||||||
|
if (file) {
|
||||||
|
if (!fs.existsSync(s)) {
|
||||||
|
throw new Error(`config file ${s} not found`);
|
||||||
|
}
|
||||||
|
s = fs.readFileSync(s, {encoding: 'utf-8'});
|
||||||
|
}
|
||||||
|
const configFile = util.tmpNameSync({
|
||||||
|
tmpdir: util.tmpDir()
|
||||||
|
});
|
||||||
|
fs.writeFileSync(configFile, s);
|
||||||
|
return configFile;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getVersion(builderName: string, standalone?: boolean): Promise<string | undefined> {
|
||||||
|
const builder = await buildx.inspect(builderName, standalone);
|
||||||
|
if (builder.nodes.length == 0) {
|
||||||
|
// a builder always have on node, should not happen.
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
// TODO: get version for all nodes
|
||||||
|
const node = builder.nodes[0];
|
||||||
|
if (!node.buildkit && node.name) {
|
||||||
|
try {
|
||||||
|
return await getVersionWithinImage(node.name);
|
||||||
|
} catch (e) {
|
||||||
|
core.warning(e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return node.buildkit;
|
||||||
|
}
|
||||||
|
|
||||||
|
async function getVersionWithinImage(nodeName: string): Promise<string> {
|
||||||
|
return exec
|
||||||
|
.getExecOutput(`docker`, ['inspect', '--format', '{{.Config.Image}}', `buildx_buildkit_${nodeName}`], {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(bkitimage => {
|
||||||
|
if (bkitimage.exitCode == 0 && bkitimage.stdout.length > 0) {
|
||||||
|
return exec
|
||||||
|
.getExecOutput(`docker`, ['run', '--rm', bkitimage.stdout.trim(), '--version'], {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(bkitversion => {
|
||||||
|
if (bkitversion.exitCode == 0 && bkitversion.stdout.length > 0) {
|
||||||
|
return `${bkitimage.stdout.trim()} => ${bkitversion.stdout.trim()}`;
|
||||||
|
} else if (bkitversion.stderr.length > 0) {
|
||||||
|
throw new Error(bkitimage.stderr.trim());
|
||||||
|
}
|
||||||
|
return bkitversion.stdout.trim();
|
||||||
|
});
|
||||||
|
} else if (bkitimage.stderr.length > 0) {
|
||||||
|
throw new Error(bkitimage.stderr.trim());
|
||||||
|
}
|
||||||
|
return bkitimage.stdout.trim();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function satisfies(builderName: string, range: string, standalone?: boolean): Promise<boolean> {
|
||||||
|
const builder = await buildx.inspect(builderName, standalone);
|
||||||
|
for (const node of builder.nodes) {
|
||||||
|
let bkversion = node.buildkit;
|
||||||
|
if (!bkversion) {
|
||||||
|
try {
|
||||||
|
bkversion = await getVersionWithinImage(node.name || '');
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// BuildKit version reported by moby is in the format of `v0.11.0-moby`
|
||||||
|
if (builder.driver == 'docker' && !bkversion.endsWith('-moby')) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
if (!semver.satisfies(bkversion.replace(/-moby$/, ''), range)) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
281
src/buildx.ts
Normal file
281
src/buildx.ts
Normal file
@ -0,0 +1,281 @@
|
|||||||
|
import {parse} from 'csv-parse/sync';
|
||||||
|
import fs from 'fs';
|
||||||
|
import path from 'path';
|
||||||
|
import * as semver from 'semver';
|
||||||
|
import * as exec from '@actions/exec';
|
||||||
|
import * as util from './util';
|
||||||
|
|
||||||
|
export type Builder = {
|
||||||
|
name?: string;
|
||||||
|
driver?: string;
|
||||||
|
'last-activity'?: Date;
|
||||||
|
nodes: Node[];
|
||||||
|
};
|
||||||
|
|
||||||
|
export type Node = {
|
||||||
|
name?: string;
|
||||||
|
endpoint?: string;
|
||||||
|
'driver-opts'?: Array<string>;
|
||||||
|
status?: string;
|
||||||
|
'buildkitd-flags'?: string;
|
||||||
|
buildkit?: string;
|
||||||
|
platforms?: string;
|
||||||
|
};
|
||||||
|
|
||||||
|
export async function getImageIDFile(): Promise<string> {
|
||||||
|
return path.join(util.tmpDir(), 'iidfile').split(path.sep).join(path.posix.sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getImageID(): Promise<string | undefined> {
|
||||||
|
const iidFile = await getImageIDFile();
|
||||||
|
if (!fs.existsSync(iidFile)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return fs.readFileSync(iidFile, {encoding: 'utf-8'}).trim();
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMetadataFile(): Promise<string> {
|
||||||
|
return path.join(util.tmpDir(), 'metadata-file').split(path.sep).join(path.posix.sep);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getMetadata(): Promise<string | undefined> {
|
||||||
|
const metadataFile = await getMetadataFile();
|
||||||
|
if (!fs.existsSync(metadataFile)) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const content = fs.readFileSync(metadataFile, {encoding: 'utf-8'}).trim();
|
||||||
|
if (content === 'null') {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
return content;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getDigest(metadata: string | undefined): Promise<string | undefined> {
|
||||||
|
if (metadata === undefined) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const metadataJSON = JSON.parse(metadata);
|
||||||
|
if (metadataJSON['containerimage.digest']) {
|
||||||
|
return metadataJSON['containerimage.digest'];
|
||||||
|
}
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSecretString(kvp: string): Promise<string> {
|
||||||
|
return getSecret(kvp, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSecretFile(kvp: string): Promise<string> {
|
||||||
|
return getSecret(kvp, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getSecret(kvp: string, file: boolean): Promise<string> {
|
||||||
|
const delimiterIndex = kvp.indexOf('=');
|
||||||
|
const key = kvp.substring(0, delimiterIndex);
|
||||||
|
let value = kvp.substring(delimiterIndex + 1);
|
||||||
|
if (key.length == 0 || value.length == 0) {
|
||||||
|
throw new Error(`${kvp} is not a valid secret`);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (file) {
|
||||||
|
if (!fs.existsSync(value)) {
|
||||||
|
throw new Error(`secret file ${value} not found`);
|
||||||
|
}
|
||||||
|
value = fs.readFileSync(value, {encoding: 'utf-8'});
|
||||||
|
}
|
||||||
|
|
||||||
|
const secretFile = util.tmpNameSync({
|
||||||
|
tmpdir: util.tmpDir()
|
||||||
|
});
|
||||||
|
fs.writeFileSync(secretFile, value);
|
||||||
|
|
||||||
|
return `id=${key},src=${secretFile}`;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasLocalExporter(outputs: string[]): boolean {
|
||||||
|
return hasExporterType('local', outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasTarExporter(outputs: string[]): boolean {
|
||||||
|
return hasExporterType('tar', outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasLocalOrTarExporter(outputs: string[]): boolean {
|
||||||
|
return hasLocalExporter(outputs) || hasTarExporter(outputs);
|
||||||
|
}
|
||||||
|
|
||||||
|
function hasExporterType(name: string, outputs: string[]): boolean {
|
||||||
|
const records = parse(outputs.join(`\n`), {
|
||||||
|
delimiter: ',',
|
||||||
|
trim: true,
|
||||||
|
columns: false,
|
||||||
|
relaxColumnCount: true
|
||||||
|
});
|
||||||
|
for (const record of records) {
|
||||||
|
if (record.length == 1 && !record[0].startsWith('type=')) {
|
||||||
|
// Local if no type is defined
|
||||||
|
// https://github.com/docker/buildx/blob/d2bf42f8b4784d83fde17acb3ed84703ddc2156b/build/output.go#L29-L43
|
||||||
|
return name == 'local';
|
||||||
|
}
|
||||||
|
for (const [key, value] of record.map(chunk => chunk.split('=').map(item => item.trim()))) {
|
||||||
|
if (key == 'type' && value == name) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function hasGitAuthToken(secrets: string[]): boolean {
|
||||||
|
for (const secret of secrets) {
|
||||||
|
if (secret.startsWith('GIT_AUTH_TOKEN=')) {
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function isAvailable(standalone?: boolean): Promise<boolean> {
|
||||||
|
const cmd = getCommand([], standalone);
|
||||||
|
return await exec
|
||||||
|
.getExecOutput(cmd.command, cmd.args, {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return res.exitCode == 0;
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.catch(error => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function inspect(name: string, standalone?: boolean): Promise<Builder> {
|
||||||
|
const cmd = getCommand(['inspect', name], standalone);
|
||||||
|
return await exec
|
||||||
|
.getExecOutput(cmd.command, cmd.args, {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
|
throw new Error(res.stderr.trim());
|
||||||
|
}
|
||||||
|
return parseInspect(res.stdout);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function parseInspect(data: string): Promise<Builder> {
|
||||||
|
const builder: Builder = {
|
||||||
|
nodes: []
|
||||||
|
};
|
||||||
|
let node: Node = {};
|
||||||
|
for (const line of data.trim().split(`\n`)) {
|
||||||
|
const [key, ...rest] = line.split(':');
|
||||||
|
const value = rest.map(v => v.trim()).join(':');
|
||||||
|
if (key.length == 0 || value.length == 0) {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
switch (key.toLowerCase()) {
|
||||||
|
case 'name': {
|
||||||
|
if (builder.name == undefined) {
|
||||||
|
builder.name = value;
|
||||||
|
} else {
|
||||||
|
if (Object.keys(node).length > 0) {
|
||||||
|
builder.nodes.push(node);
|
||||||
|
node = {};
|
||||||
|
}
|
||||||
|
node.name = value;
|
||||||
|
}
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'driver': {
|
||||||
|
builder.driver = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'last activity': {
|
||||||
|
builder['last-activity'] = new Date(value);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'endpoint': {
|
||||||
|
node.endpoint = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'driver options': {
|
||||||
|
node['driver-opts'] = (value.match(/(\w+)="([^"]*)"/g) || []).map(v => v.replace(/^(.*)="(.*)"$/g, '$1=$2'));
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'status': {
|
||||||
|
node.status = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'flags': {
|
||||||
|
node['buildkitd-flags'] = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'buildkit': {
|
||||||
|
node.buildkit = value;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case 'platforms': {
|
||||||
|
let platforms: Array<string> = [];
|
||||||
|
// if a preferred platform is being set then use only these
|
||||||
|
// https://docs.docker.com/engine/reference/commandline/buildx_inspect/#get-information-about-a-builder-instance
|
||||||
|
if (value.includes('*')) {
|
||||||
|
for (const platform of value.split(', ')) {
|
||||||
|
if (platform.includes('*')) {
|
||||||
|
platforms.push(platform.replace('*', ''));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
// otherwise set all platforms available
|
||||||
|
platforms = value.split(', ');
|
||||||
|
}
|
||||||
|
node.platforms = platforms.join(',');
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if (Object.keys(node).length > 0) {
|
||||||
|
builder.nodes.push(node);
|
||||||
|
}
|
||||||
|
return builder;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function getVersion(standalone?: boolean): Promise<string> {
|
||||||
|
const cmd = getCommand(['version'], standalone);
|
||||||
|
return await exec
|
||||||
|
.getExecOutput(cmd.command, cmd.args, {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
|
throw new Error(res.stderr.trim());
|
||||||
|
}
|
||||||
|
return parseVersion(res.stdout.trim());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
export function parseVersion(stdout: string): string {
|
||||||
|
const matches = /\sv?([0-9a-f]{7}|[0-9.]+)/.exec(stdout);
|
||||||
|
if (!matches) {
|
||||||
|
throw new Error(`Cannot parse buildx version`);
|
||||||
|
}
|
||||||
|
return matches[1];
|
||||||
|
}
|
||||||
|
|
||||||
|
export function satisfies(version: string, range: string): boolean {
|
||||||
|
return semver.satisfies(version, range) || /^[0-9a-f]{7}$/.exec(version) !== null;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getCommand(args: Array<string>, standalone?: boolean) {
|
||||||
|
return {
|
||||||
|
command: standalone ? 'buildx' : 'docker',
|
||||||
|
args: standalone ? args : ['buildx', ...args]
|
||||||
|
};
|
||||||
|
}
|
19
src/docker.ts
Normal file
19
src/docker.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as exec from '@actions/exec';
|
||||||
|
|
||||||
|
export async function isAvailable(): Promise<boolean> {
|
||||||
|
return await exec
|
||||||
|
.getExecOutput('docker', undefined, {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return res.exitCode == 0;
|
||||||
|
})
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||||
|
.catch(error => {
|
||||||
|
return false;
|
||||||
|
});
|
||||||
|
}
|
19
src/git.ts
Normal file
19
src/git.ts
Normal file
@ -0,0 +1,19 @@
|
|||||||
|
import * as exec from '@actions/exec';
|
||||||
|
|
||||||
|
export async function getRemoteSha(repo: string, ref: string): Promise<string> {
|
||||||
|
return await exec
|
||||||
|
.getExecOutput(`git`, ['ls-remote', repo, ref], {
|
||||||
|
ignoreReturnCode: true,
|
||||||
|
silent: true
|
||||||
|
})
|
||||||
|
.then(res => {
|
||||||
|
if (res.stderr.length > 0 && res.exitCode != 0) {
|
||||||
|
throw new Error(res.stderr);
|
||||||
|
}
|
||||||
|
const [rsha] = res.stdout.trim().split(/[\s\t]/);
|
||||||
|
if (rsha.length == 0) {
|
||||||
|
throw new Error(`Cannot find remote ref for ${repo}#${ref}`);
|
||||||
|
}
|
||||||
|
return rsha;
|
||||||
|
});
|
||||||
|
}
|
31
src/github.ts
Normal file
31
src/github.ts
Normal file
@ -0,0 +1,31 @@
|
|||||||
|
import jwt_decode, {JwtPayload} from 'jwt-decode';
|
||||||
|
import * as github from '@actions/github';
|
||||||
|
import {Context} from '@actions/github/lib/context';
|
||||||
|
import {components as OctoOpenApiTypes} from '@octokit/openapi-types';
|
||||||
|
import * as util from './util';
|
||||||
|
|
||||||
|
export type ReposGetResponseData = OctoOpenApiTypes['schemas']['repository'];
|
||||||
|
|
||||||
|
export function context(): Context {
|
||||||
|
return github.context;
|
||||||
|
}
|
||||||
|
|
||||||
|
export async function repo(token: string): Promise<ReposGetResponseData> {
|
||||||
|
return github
|
||||||
|
.getOctokit(token)
|
||||||
|
.rest.repos.get({...github.context.repo})
|
||||||
|
.then(response => response.data as ReposGetResponseData);
|
||||||
|
}
|
||||||
|
|
||||||
|
interface Jwt extends JwtPayload {
|
||||||
|
ac?: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const parseRuntimeToken = (token: string): Jwt => {
|
||||||
|
return jwt_decode<Jwt>(token);
|
||||||
|
};
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export function fromPayload(path: string): any {
|
||||||
|
return util.select(github.context.payload, path);
|
||||||
|
}
|
92
src/util.ts
Normal file
92
src/util.ts
Normal file
@ -0,0 +1,92 @@
|
|||||||
|
import * as fs from 'fs';
|
||||||
|
import * as os from 'os';
|
||||||
|
import * as path from 'path';
|
||||||
|
import * as tmp from 'tmp';
|
||||||
|
import * as core from '@actions/core';
|
||||||
|
import * as github from '@actions/github';
|
||||||
|
import {parse} from 'csv-parse/sync';
|
||||||
|
|
||||||
|
let _defaultContext, _tmpDir: string;
|
||||||
|
|
||||||
|
export function defaultContext(): string {
|
||||||
|
if (!_defaultContext) {
|
||||||
|
let ref = github.context.ref;
|
||||||
|
if (github.context.sha && ref && !ref.startsWith('refs/')) {
|
||||||
|
ref = `refs/heads/${github.context.ref}`;
|
||||||
|
}
|
||||||
|
if (github.context.sha && !ref.startsWith(`refs/pull/`)) {
|
||||||
|
ref = github.context.sha;
|
||||||
|
}
|
||||||
|
_defaultContext = `${process.env.GITHUB_SERVER_URL || 'https://github.com'}/${github.context.repo.owner}/${github.context.repo.repo}.git#${ref}`;
|
||||||
|
}
|
||||||
|
return _defaultContext;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tmpDir(): string {
|
||||||
|
if (!_tmpDir) {
|
||||||
|
_tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'docker-actions-toolkit-')).split(path.sep).join(path.posix.sep);
|
||||||
|
}
|
||||||
|
return _tmpDir;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function tmpNameSync(options?: tmp.TmpNameOptions): string {
|
||||||
|
return tmp.tmpNameSync(options);
|
||||||
|
}
|
||||||
|
|
||||||
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
||||||
|
export function select(obj: any, path: string): any {
|
||||||
|
if (!obj) {
|
||||||
|
return undefined;
|
||||||
|
}
|
||||||
|
const i = path.indexOf('.');
|
||||||
|
if (i < 0) {
|
||||||
|
return obj[path];
|
||||||
|
}
|
||||||
|
const key = path.slice(0, i);
|
||||||
|
return select(obj[key], path.slice(i + 1));
|
||||||
|
}
|
||||||
|
|
||||||
|
export function getInputList(name: string, ignoreComma?: boolean): string[] {
|
||||||
|
const res: Array<string> = [];
|
||||||
|
|
||||||
|
const items = core.getInput(name);
|
||||||
|
if (items == '') {
|
||||||
|
return res;
|
||||||
|
}
|
||||||
|
|
||||||
|
const records = parse(items, {
|
||||||
|
columns: false,
|
||||||
|
relaxQuotes: true,
|
||||||
|
comment: '#',
|
||||||
|
relaxColumnCount: true,
|
||||||
|
skipEmptyLines: true
|
||||||
|
});
|
||||||
|
|
||||||
|
for (const record of records as Array<string[]>) {
|
||||||
|
if (record.length == 1) {
|
||||||
|
res.push(record[0]);
|
||||||
|
continue;
|
||||||
|
} else if (!ignoreComma) {
|
||||||
|
res.push(...record);
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
res.push(record.join(','));
|
||||||
|
}
|
||||||
|
|
||||||
|
return res.filter(item => item).map(pat => pat.trim());
|
||||||
|
}
|
||||||
|
|
||||||
|
export const asyncForEach = async (array, callback) => {
|
||||||
|
for (let index = 0; index < array.length; index++) {
|
||||||
|
await callback(array[index], index, array);
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
export function isValidUrl(url: string): boolean {
|
||||||
|
try {
|
||||||
|
new URL(url);
|
||||||
|
} catch (e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
20
tsconfig.json
Normal file
20
tsconfig.json
Normal file
@ -0,0 +1,20 @@
|
|||||||
|
{
|
||||||
|
"compilerOptions": {
|
||||||
|
"target": "es6",
|
||||||
|
"module": "commonjs",
|
||||||
|
"newLine": "lf",
|
||||||
|
"outDir": "./lib",
|
||||||
|
"rootDir": "./src",
|
||||||
|
"esModuleInterop": true,
|
||||||
|
"forceConsistentCasingInFileNames": true,
|
||||||
|
"strict": true,
|
||||||
|
"noImplicitAny": false,
|
||||||
|
"resolveJsonModule": true,
|
||||||
|
"useUnknownInCatchVariables": false,
|
||||||
|
},
|
||||||
|
"exclude": [
|
||||||
|
"node_modules",
|
||||||
|
"**/*.test.ts",
|
||||||
|
"jest.config.ts"
|
||||||
|
]
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user