mirror of
https://github.com/docker/actions-toolkit.git
synced 2024-11-23 03:16:09 +08:00
docker: install and download methods for macos and windows
Signed-off-by: CrazyMax <crazy-max@users.noreply.github.com>
This commit is contained in:
parent
964381b7e9
commit
4d66b2fa08
39
.github/workflows/e2e.yml
vendored
Normal file
39
.github/workflows/e2e.yml
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
name: e2e
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
push:
|
||||
branches:
|
||||
- 'main'
|
||||
pull_request:
|
||||
paths-ignore:
|
||||
- '.github/buildx-releases.json'
|
||||
|
||||
jobs:
|
||||
test:
|
||||
runs-on: ${{ matrix.os }}
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
os:
|
||||
#- ubuntu-latest
|
||||
- macos-latest
|
||||
- windows-latest
|
||||
steps:
|
||||
-
|
||||
name: Checkout
|
||||
uses: actions/checkout@v3
|
||||
-
|
||||
name: Setup Node
|
||||
uses: actions/setup-node@v3
|
||||
with:
|
||||
node-version: 16
|
||||
cache: 'yarn'
|
||||
-
|
||||
name: Install
|
||||
run: yarn install
|
||||
-
|
||||
name: Test
|
||||
run: yarn test:e2e
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
@ -1,6 +1,7 @@
|
||||
[![Version](https://img.shields.io/npm/v/@docker/actions-toolkit?label=version&logo=npm&style=flat-square)](https://www.npmjs.com/package/@docker/actions-toolkit)
|
||||
[![Downloads](https://img.shields.io/npm/dw/@docker/actions-toolkit?logo=npm&style=flat-square)](https://www.npmjs.com/package/@docker/actions-toolkit)
|
||||
[![Test workflow](https://img.shields.io/github/actions/workflow/status/docker/actions-toolkit/test.yml?label=test&logo=github&style=flat-square)](https://github.com/docker/actions-toolkit/actions?workflow=test)
|
||||
[![E2E workflow](https://img.shields.io/github/actions/workflow/status/docker/actions-toolkit/e2e.yml?label=e2e&logo=github&style=flat-square)](https://github.com/docker/actions-toolkit/actions?workflow=e2e)
|
||||
[![Codecov](https://img.shields.io/codecov/c/github/docker/actions-toolkit?logo=codecov&style=flat-square)](https://codecov.io/gh/docker/actions-toolkit)
|
||||
|
||||
# Actions Toolkit
|
||||
|
34
__tests__/docker/install.test.e2e.ts
Normal file
34
__tests__/docker/install.test.e2e.ts
Normal file
@ -0,0 +1,34 @@
|
||||
/**
|
||||
* 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 {describe, expect, test} from '@jest/globals';
|
||||
|
||||
import {Install} from '../../src/docker/install';
|
||||
import {Docker} from '../../src/docker/docker';
|
||||
|
||||
describe('install', () => {
|
||||
// prettier-ignore
|
||||
test.each(['23.0.0'])(
|
||||
'install docker %s', async (version) => {
|
||||
await expect((async () => {
|
||||
const install = new Install();
|
||||
const toolPath = await install.download(version);
|
||||
await install.install(toolPath);
|
||||
await Docker.printVersion();
|
||||
await Docker.printInfo();
|
||||
})()).resolves.not.toThrow();
|
||||
});
|
||||
});
|
50
__tests__/docker/install.test.ts
Normal file
50
__tests__/docker/install.test.ts
Normal file
@ -0,0 +1,50 @@
|
||||
/**
|
||||
* 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 {describe, expect, jest, test, beforeEach, afterEach} from '@jest/globals';
|
||||
import * as fs from 'fs';
|
||||
import * as path from 'path';
|
||||
import * as rimraf from 'rimraf';
|
||||
import osm = require('os');
|
||||
|
||||
import {Install} from '../../src/docker/install';
|
||||
|
||||
// prettier-ignore
|
||||
const tmpDir = path.join(process.env.TEMP || '/tmp', 'buildx-jest');
|
||||
|
||||
beforeEach(() => {
|
||||
jest.clearAllMocks();
|
||||
});
|
||||
|
||||
afterEach(function () {
|
||||
rimraf.sync(tmpDir);
|
||||
});
|
||||
|
||||
describe('download', () => {
|
||||
// prettier-ignore
|
||||
test.each([
|
||||
['19.03.6', 'linux'],
|
||||
['20.10.22', 'linux'],
|
||||
['20.10.22', 'darwin'],
|
||||
['20.10.22', 'win32'],
|
||||
])(
|
||||
'acquires %p of docker (%s)', async (version, platformOS) => {
|
||||
jest.spyOn(osm, 'platform').mockImplementation(() => platformOS);
|
||||
const install = new Install();
|
||||
const toolPath = await install.download(version);
|
||||
expect(fs.existsSync(toolPath)).toBe(true);
|
||||
}, 100000);
|
||||
});
|
31
jest.config.e2e.ts
Normal file
31
jest.config.e2e.ts
Normal file
@ -0,0 +1,31 @@
|
||||
/**
|
||||
* 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.
|
||||
*/
|
||||
|
||||
module.exports = {
|
||||
clearMocks: true,
|
||||
testEnvironment: 'node',
|
||||
moduleFileExtensions: ['js', 'ts'],
|
||||
setupFiles: ['dotenv/config'],
|
||||
testMatch: ['**/*.test.e2e.ts'],
|
||||
testTimeout: 1800000, // 30 minutes
|
||||
transform: {
|
||||
'^.+\\.ts$': 'ts-jest'
|
||||
},
|
||||
moduleNameMapper: {
|
||||
'^csv-parse/sync': '<rootDir>/node_modules/csv-parse/dist/cjs/sync.cjs'
|
||||
},
|
||||
verbose: false
|
||||
};
|
@ -11,7 +11,8 @@
|
||||
"prettier": "prettier --check \"./**/*.ts\"",
|
||||
"prettier:fix": "prettier --write \"./**/*.ts\"",
|
||||
"test": "jest",
|
||||
"test-coverage": "jest --coverage"
|
||||
"test-coverage": "jest --coverage",
|
||||
"test:e2e": "jest -c jest.config.e2e.ts"
|
||||
},
|
||||
"repository": {
|
||||
"type": "git",
|
||||
@ -35,7 +36,8 @@
|
||||
"test": "__tests__"
|
||||
},
|
||||
"files": [
|
||||
"lib"
|
||||
"lib",
|
||||
"scripts"
|
||||
],
|
||||
"publishConfig": {
|
||||
"access": "public",
|
||||
|
69
scripts/setup-docker.ps1
Normal file
69
scripts/setup-docker.ps1
Normal file
@ -0,0 +1,69 @@
|
||||
[CmdletBinding()]
|
||||
param(
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$ToolDir,
|
||||
|
||||
[Parameter(Mandatory = $false)]
|
||||
[string]$TmpDir,
|
||||
|
||||
[Parameter(Mandatory = $true)]
|
||||
[string]$DockerHost)
|
||||
|
||||
$pwver = (Get-ItemProperty -Path HKLM:\SOFTWARE\Microsoft\PowerShell\3\PowerShellEngine -Name 'PowerShellVersion').PowerShellVersion
|
||||
Write-Host "PowerShell version: $pwver"
|
||||
|
||||
# Create temp directory
|
||||
if (!$TmpDir) { $TmpDir = $env:TEMP }
|
||||
New-Item -ItemType Directory "$TmpDir" -ErrorAction SilentlyContinue | Out-Null
|
||||
|
||||
# Remove existing service
|
||||
if (Get-Service docker -ErrorAction SilentlyContinue) {
|
||||
$dockerVersion = (docker version -f "{{.Server.Version}}")
|
||||
Write-Host "Current installed Docker version: $dockerVersion"
|
||||
# stop service
|
||||
Stop-Service -Force -Name docker
|
||||
Write-Host "Service stopped"
|
||||
# remove service
|
||||
sc.exe delete "docker"
|
||||
# removes event log entry. we could use "Remove-EventLog -LogName -Source docker"
|
||||
# but this cmd is not available atm
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
& reg delete "HKLM\SYSTEM\CurrentControlSet\Services\EventLog\Application\docker" /f 2>&1 | Out-Null
|
||||
$ErrorActionPreference = "Stop"
|
||||
Write-Host "Service removed"
|
||||
}
|
||||
|
||||
$env:DOCKER_HOST = $DockerHost
|
||||
Write-Host "DOCKER_HOST: $env:DOCKER_HOST"
|
||||
|
||||
Write-Host "Creating service"
|
||||
New-Item -ItemType Directory "$TmpDir\moby-root" -ErrorAction SilentlyContinue | Out-Null
|
||||
New-Item -ItemType Directory "$TmpDir\moby-exec" -ErrorAction SilentlyContinue | Out-Null
|
||||
Start-Process -Wait -NoNewWindow "$ToolDir\dockerd" `
|
||||
-ArgumentList `
|
||||
"--host=$DockerHost", `
|
||||
"--data-root=$TmpDir\moby-root", `
|
||||
"--exec-root=$TmpDir\moby-exec", `
|
||||
"--pidfile=$TmpDir\docker.pid", `
|
||||
"--register-service"
|
||||
Write-Host "Starting service"
|
||||
Start-Service -Name docker
|
||||
Write-Host "Service started successfully!"
|
||||
|
||||
$tries=20
|
||||
Write-Host "Waiting for Docker daemon to start..."
|
||||
While ($true) {
|
||||
$ErrorActionPreference = "SilentlyContinue"
|
||||
& "$ToolDir\docker" version | Out-Null
|
||||
$ErrorActionPreference = "Stop"
|
||||
If ($LastExitCode -eq 0) {
|
||||
break
|
||||
}
|
||||
$tries--
|
||||
If ($tries -le 0) {
|
||||
Throw "Failed to get a response from Docker daemon"
|
||||
}
|
||||
Write-Host -NoNewline "."
|
||||
Start-Sleep -Seconds 1
|
||||
}
|
||||
Write-Host "Docker daemon started successfully!"
|
195
src/docker/install.ts
Normal file
195
src/docker/install.ts
Normal file
@ -0,0 +1,195 @@
|
||||
/**
|
||||
* 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 * as tc from '@actions/tool-cache';
|
||||
import * as util from 'util';
|
||||
|
||||
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): Promise<void> {
|
||||
switch (os.platform()) {
|
||||
case 'darwin': {
|
||||
await this.installDarwin(toolDir);
|
||||
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): Promise<void> {
|
||||
if (!(await Install.colimaInstalled())) {
|
||||
await core.group('Installing colima', async () => {
|
||||
await Exec.exec('brew', ['install', 'colima']);
|
||||
});
|
||||
}
|
||||
// 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', '--runtime', 'docker', '--mount-type', '9p'], {env: envs});
|
||||
});
|
||||
}
|
||||
|
||||
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 {
|
||||
let platformOS, platformArch: string;
|
||||
switch (os.platform()) {
|
||||
case 'darwin': {
|
||||
platformOS = 'mac';
|
||||
break;
|
||||
}
|
||||
case 'linux': {
|
||||
platformOS = 'linux';
|
||||
break;
|
||||
}
|
||||
case 'win32': {
|
||||
platformOS = 'win';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
platformOS = os.platform();
|
||||
break;
|
||||
}
|
||||
}
|
||||
switch (os.arch()) {
|
||||
case 'x64': {
|
||||
platformArch = 'x86_64';
|
||||
break;
|
||||
}
|
||||
case 'ppc64': {
|
||||
platformArch = 'ppc64le';
|
||||
break;
|
||||
}
|
||||
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: {
|
||||
platformArch = 'armel';
|
||||
break;
|
||||
}
|
||||
case 7: {
|
||||
platformArch = 'armhf';
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
platformArch = `v${arm_version}`;
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
default: {
|
||||
platformArch = os.arch();
|
||||
break;
|
||||
}
|
||||
}
|
||||
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 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;
|
||||
});
|
||||
}
|
||||
}
|
21
src/scripts.ts
Normal file
21
src/scripts.ts
Normal file
@ -0,0 +1,21 @@
|
||||
/**
|
||||
* 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 path from 'path';
|
||||
|
||||
const scriptsPath = path.join(__dirname, '..', 'scripts');
|
||||
|
||||
export const setupDockerPowershell = path.join(scriptsPath, 'setup-docker.ps1');
|
24
src/util.ts
24
src/util.ts
@ -14,7 +14,9 @@
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
import fs from 'fs';
|
||||
import * as core from '@actions/core';
|
||||
import * as io from '@actions/io';
|
||||
import {parse} from 'csv-parse/sync';
|
||||
|
||||
export interface InputListOpts {
|
||||
@ -71,4 +73,26 @@ export class Util {
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
public static async powershellCommand(script: string, params: Record<string, string>) {
|
||||
const powershellPath: string = await io.which('powershell', true);
|
||||
const escapedScript = script.replace(/'/g, "''").replace(/"|\n|\r/g, '');
|
||||
const escapedParams: string[] = [];
|
||||
for (const key in params) {
|
||||
escapedParams.push(`-${key} '${params[key].replace(/'/g, "''").replace(/"|\n|\r/g, '')}'`);
|
||||
}
|
||||
return {
|
||||
command: `"${powershellPath}"`,
|
||||
args: ['-NoLogo', '-Sta', '-NoProfile', '-NonInteractive', '-ExecutionPolicy', 'Unrestricted', '-Command', `& '${escapedScript}' ${escapedParams.join(' ')}`]
|
||||
};
|
||||
}
|
||||
|
||||
public static isDirectory(p) {
|
||||
try {
|
||||
return fs.lstatSync(p).isDirectory();
|
||||
} catch (_) {
|
||||
// noop
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -19,6 +19,6 @@
|
||||
"./__tests__/**/*",
|
||||
"./lib/**/*",
|
||||
"node_modules",
|
||||
"jest.config.ts"
|
||||
"jest.config*.ts"
|
||||
]
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user