Files
actions-toolkit/src/docker/install.ts
2023-03-02 15:39:24 +01:00

222 lines
6.9 KiB
TypeScript

/**
* 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 handlebars from 'handlebars';
import * as util from 'util';
import * as core from '@actions/core';
import * as io from '@actions/io';
import * as tc from '@actions/tool-cache';
import * as scripts from '../scripts';
import {Context} from '../context';
import {Exec} from '../exec';
import {Util} from '../util';
export class Install {
public async download(version: string, channel?: string): Promise<string> {
channel = channel || 'stable';
const downloadURL = this.downloadURL(version, channel);
core.info(`Downloading ${downloadURL}`);
const downloadPath = await tc.downloadTool(downloadURL);
core.debug(`docker.Install.download downloadPath: ${downloadPath}`);
let extractFolder: string;
if (os.platform() == 'win32') {
extractFolder = await tc.extractZip(downloadPath);
} else {
extractFolder = await tc.extractTar(downloadPath, path.join(Context.tmpDir(), 'docker'));
}
if (Util.isDirectory(path.join(extractFolder, 'docker'))) {
extractFolder = path.join(extractFolder, 'docker');
}
core.debug(`docker.Install.download extractFolder: ${extractFolder}`);
core.info('Fixing perms');
fs.readdir(path.join(extractFolder), function (err, files) {
if (err) {
throw err;
}
// eslint-disable-next-line @typescript-eslint/no-unused-vars
files.forEach(function (file, index) {
fs.chmodSync(path.join(extractFolder, file), '0755');
});
});
const tooldir = await tc.cacheDir(extractFolder, `docker-${channel}`, version.replace(/(0+)([1-9]+)/, '$2'));
core.addPath(tooldir);
core.info('Added Docker to PATH');
return tooldir;
}
public async install(toolDir: string, version: string, channel?: string): Promise<void> {
channel = channel || 'stable';
switch (os.platform()) {
case 'darwin': {
await this.installDarwin(toolDir, version, channel);
break;
}
case 'linux': {
await this.installLinux(toolDir);
break;
}
case 'win32': {
await this.installWindows(toolDir);
break;
}
default: {
throw new Error(`Unsupported platform: ${os.platform()}`);
}
}
}
private async installDarwin(toolDir: string, version: string, channel?: string): Promise<void> {
const colimaDir = path.join(os.homedir(), '.colima', 'default');
await io.mkdirP(colimaDir);
const dockerHost = `unix://${colimaDir}/docker.sock`;
if (!(await Install.colimaInstalled())) {
await core.group('Installing colima', async () => {
await Exec.exec('brew', ['install', 'colima']);
});
}
await core.group('Creating colima config', async () => {
const colimaCfg = handlebars.compile(
fs.readFileSync(scripts.colimaConfig, {
encoding: 'utf8',
flag: 'r'
})
)({
hostArch: Install.platformArch(),
dockerVersion: version,
dockerChannel: channel
});
core.info(`Writing colima config to ${path.join(colimaDir, 'colima.yaml')}`);
fs.writeFileSync(path.join(colimaDir, 'colima.yaml'), colimaCfg);
core.info(colimaCfg);
});
// colima is already started on the runner so env var added in download
// method is not expanded to the running process.
const envs = Object.assign({}, process.env, {
PATH: `${toolDir}:${process.env.PATH}`
}) as {
[key: string]: string;
};
await core.group('Starting colima', async () => {
await Exec.exec('colima', ['start', '--very-verbose'], {env: envs});
});
await core.group('Create Docker context', async () => {
await Exec.exec('docker', ['context', 'create', 'setup-docker-action', '--docker', `host=${dockerHost}`]);
await Exec.exec('docker', ['context', 'use', 'setup-docker-action']);
});
}
private async installLinux(toolDir: string): Promise<void> {
core.addPath(toolDir);
core.info('Added Docker to PATH');
}
private async installWindows(toolDir: string): Promise<void> {
const dockerHost = 'npipe:////./pipe/setup_docker_action';
const setupCmd = await Util.powershellCommand(scripts.setupDockerPowershell, {
ToolDir: toolDir,
TmpDir: Context.tmpDir(),
DockerHost: dockerHost
});
await core.group('Install Docker daemon service', async () => {
await Exec.exec(setupCmd.command, setupCmd.args);
});
await core.group('Create Docker context', async () => {
await Exec.exec('docker', ['context', 'create', 'setup-docker-action', '--docker', `host=${dockerHost}`]);
await Exec.exec('docker', ['context', 'use', 'setup-docker-action']);
});
}
private downloadURL(version: string, channel: string): string {
const platformOS = Install.platformOS();
const platformArch = Install.platformArch();
const ext = platformOS === 'win' ? '.zip' : '.tgz';
return util.format('https://download.docker.com/%s/static/%s/%s/docker-%s%s', platformOS, channel, platformArch, version, ext);
}
private static platformOS(): string {
switch (os.platform()) {
case 'darwin': {
return 'mac';
}
case 'linux': {
return 'linux';
}
case 'win32': {
return 'win';
}
default: {
return os.platform();
}
}
}
private static platformArch(): string {
switch (os.arch()) {
case 'x64': {
return 'x86_64';
}
case 'ppc64': {
return 'ppc64le';
}
case 'arm': {
// eslint-disable-next-line @typescript-eslint/no-explicit-any
const arm_version = (process.config.variables as any).arm_version;
switch (arm_version) {
case 6: {
return 'armel';
}
case 7: {
return 'armhf';
}
default: {
return `v${arm_version}`;
}
}
}
default: {
return os.arch();
}
}
}
private static async colimaInstalled(): Promise<boolean> {
return await io
.which('colima', true)
.then(res => {
core.debug(`docker.Install.colimaAvailable ok: ${res}`);
return true;
})
.catch(error => {
core.debug(`docker.Install.colimaAvailable error: ${error}`);
return false;
});
}
}