@docker/actions-toolkit
Version:
Toolkit for Docker (GitHub) Actions
199 lines • 6.81 kB
JavaScript
/**
* Copyright 2023 actions-toolkit authors
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
import fs from 'fs';
import os from 'os';
import path from 'path';
import * as core from '@actions/core';
import * as io from '@actions/io';
import { Context } from '../context.js';
import { Cache } from '../cache.js';
import { Exec } from '../exec.js';
import { Util } from '../util.js';
export class Docker {
static get configDir() {
return process.env.DOCKER_CONFIG || path.join(os.homedir(), '.docker');
}
static configFile() {
const f = path.join(Docker.configDir, 'config.json');
if (!fs.existsSync(f)) {
return undefined;
}
return JSON.parse(fs.readFileSync(f, { encoding: 'utf-8' }));
}
static async isAvailable() {
return await io
.which('docker', true)
.then(res => {
core.debug(`Docker.isAvailable ok: ${res}`);
return true;
})
.catch(error => {
core.debug(`Docker.isAvailable error: ${error}`);
return false;
});
}
static async isDaemonRunning() {
try {
await Docker.getExecOutput([`version`], {
silent: true
});
return true;
}
catch {
return false;
}
}
static async exec(args, options) {
return Exec.exec('docker', args, Docker.execOptions(options));
}
static async getExecOutput(args, options) {
return Exec.getExecOutput('docker', args, Docker.execOptions(options));
}
static execOptions(options) {
if (!options) {
options = {};
}
if (!options.env) {
options.env = Object.assign({}, process.env, {
DOCKER_CONTENT_TRUST: 'false'
});
}
else {
options.env.DOCKER_CONTENT_TRUST = 'false';
}
return options;
}
static async context(name) {
const args = ['context', 'inspect', '--format', '{{.Name}}'];
if (name) {
args.push(name);
}
return await Docker.getExecOutput(args, {
ignoreReturnCode: true,
silent: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr);
}
return res.stdout.trim();
});
}
static async contextInspect(name) {
const args = ['context', 'inspect', '--format=json'];
if (name) {
args.push(name);
}
return await Docker.getExecOutput(args, {
ignoreReturnCode: true,
silent: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
throw new Error(res.stderr.trim());
}
return JSON.parse(res.stdout.trim())[0];
});
}
static async printVersion() {
await Docker.exec(['version']);
}
static async printInfo() {
await Docker.exec(['info']);
}
static parseRepoTag(image) {
let sepPos;
const digestPos = image.indexOf('@');
const colonPos = image.lastIndexOf(':');
if (digestPos >= 0) {
// priority on digest
sepPos = digestPos;
}
else if (colonPos >= 0) {
sepPos = colonPos;
}
else {
return {
repository: image,
tag: 'latest'
};
}
const tag = image.slice(sepPos + 1);
if (tag.indexOf('/') === -1) {
return {
repository: image.slice(0, sepPos),
tag: tag
};
}
return {
repository: image,
tag: 'latest'
};
}
static async pull(image, cache) {
const parsedImage = Docker.parseRepoTag(image);
const repoSanitized = parsedImage.repository.replace(/[^a-zA-Z0-9.]+/g, '--');
const tagSanitized = parsedImage.tag.replace(/[^a-zA-Z0-9.]+/g, '--');
const imageCache = new Cache({
htcName: repoSanitized,
htcVersion: tagSanitized,
baseCacheDir: path.join(Docker.configDir, '.cache', 'images', repoSanitized),
cacheFile: 'image.tar'
});
let cacheFoundPath;
if (cache) {
cacheFoundPath = await imageCache.find();
if (cacheFoundPath) {
core.info(`Image found from cache in ${cacheFoundPath}`);
await Docker.getExecOutput(['load', '-i', cacheFoundPath], {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
core.warning(`Failed to load image from cache: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
}
});
}
}
let pulled = true;
await Docker.getExecOutput(['pull', image], {
ignoreReturnCode: true
}).then(res => {
if (res.stderr.length > 0 && res.exitCode != 0) {
pulled = false;
const err = res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error';
if (cacheFoundPath) {
core.warning(`Failed to pull image, using one from cache: ${err}`);
}
else {
throw new Error(err);
}
}
});
if (cache && pulled) {
const imageTarPath = path.join(Context.tmpDir(), `${Util.hash(image)}.tar`);
await Docker.getExecOutput(['save', '-o', imageTarPath, image], {
ignoreReturnCode: true
}).then(async (res) => {
if (res.stderr.length > 0 && res.exitCode != 0) {
core.warning(`Failed to save image: ${res.stderr.match(/(.*)\s*$/)?.[0]?.trim() ?? 'unknown error'}`);
}
else {
const cachePath = await imageCache.save(imageTarPath);
core.info(`Image cached to ${cachePath}`);
}
});
}
}
}
//# sourceMappingURL=docker.js.map