-
Notifications
You must be signed in to change notification settings - Fork 19
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Signed-off-by: CrazyMax <1951866+crazy-max@users.noreply.github.com>
- Loading branch information
Showing
5 changed files
with
355 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
/** | ||
* Copyright 2024 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 * as fs from 'fs'; | ||
|
||
import {Install} from '../../src/undock/install'; | ||
|
||
describe('download', () => { | ||
// prettier-ignore | ||
test.each(['latest'])( | ||
'install undock %s', async (version) => { | ||
await expect((async () => { | ||
const install = new Install(); | ||
const toolPath = await install.download(version); | ||
if (!fs.existsSync(toolPath)) { | ||
throw new Error('toolPath does not exist'); | ||
} | ||
const binPath = await install.install(toolPath); | ||
if (!fs.existsSync(binPath)) { | ||
throw new Error('binPath does not exist'); | ||
} | ||
})()).resolves.not.toThrow(); | ||
}, 60000); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,129 @@ | ||
/** | ||
* Copyright 2024 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, it, jest, test, afterEach} from '@jest/globals'; | ||
import fs from 'fs'; | ||
import os from 'os'; | ||
import path from 'path'; | ||
import * as rimraf from 'rimraf'; | ||
import osm = require('os'); | ||
|
||
import {Install} from '../../src/undock/install'; | ||
|
||
const tmpDir = fs.mkdtempSync(path.join(process.env.TEMP || os.tmpdir(), 'undock-install-')); | ||
|
||
afterEach(function () { | ||
rimraf.sync(tmpDir); | ||
}); | ||
|
||
describe('download', () => { | ||
// prettier-ignore | ||
test.each([ | ||
['v0.4.0'], | ||
['v0.7.0'], | ||
['latest'] | ||
])( | ||
'acquires %p of undock (standalone: %p)', async (version) => { | ||
const install = new Install(); | ||
const toolPath = await install.download(version); | ||
expect(fs.existsSync(toolPath)).toBe(true); | ||
const undockBin = await install.install(toolPath, tmpDir); | ||
expect(fs.existsSync(undockBin)).toBe(true); | ||
}, | ||
100000 | ||
); | ||
|
||
// prettier-ignore | ||
test.each([ | ||
// following versions are already cached to htc from previous test cases | ||
['v0.4.0'], | ||
['v0.7.0'], | ||
])( | ||
'acquires %p of undock with cache', async (version) => { | ||
const install = new Install(); | ||
const toolPath = await install.download(version); | ||
expect(fs.existsSync(toolPath)).toBe(true); | ||
}); | ||
|
||
// prettier-ignore | ||
test.each([ | ||
['v0.5.0'], | ||
['v0.6.0'], | ||
])( | ||
'acquires %p of undock without cache', async (version) => { | ||
const install = new Install(); | ||
const toolPath = await install.download(version, true); | ||
expect(fs.existsSync(toolPath)).toBe(true); | ||
}); | ||
|
||
// TODO: add tests for arm | ||
// prettier-ignore | ||
test.each([ | ||
['win32', 'x64'], | ||
['win32', 'arm64'], | ||
['darwin', 'x64'], | ||
['darwin', 'arm64'], | ||
['linux', 'x64'], | ||
['linux', 'arm64'], | ||
['linux', 'ppc64'], | ||
['linux', 's390x'], | ||
])( | ||
'acquires undock for %s/%s', async (os, arch) => { | ||
jest.spyOn(osm, 'platform').mockImplementation(() => os as NodeJS.Platform); | ||
jest.spyOn(osm, 'arch').mockImplementation(() => arch); | ||
const install = new Install(); | ||
const undockBin = await install.download('latest'); | ||
expect(fs.existsSync(undockBin)).toBe(true); | ||
}, | ||
100000 | ||
); | ||
}); | ||
|
||
describe('getDownloadVersion', () => { | ||
it('returns latest download version', async () => { | ||
const version = await Install.getDownloadVersion('latest'); | ||
expect(version.version).toEqual('latest'); | ||
expect(version.downloadURL).toEqual('https://github.com/crazy-max/undock/releases/download/v%s/%s'); | ||
expect(version.releasesURL).toEqual('https://raw.githubusercontent.com/docker/actions-toolkit/main/.github/undock-releases.json'); | ||
}); | ||
it('returns v0.6.0 download version', async () => { | ||
const version = await Install.getDownloadVersion('v0.6.0'); | ||
expect(version.version).toEqual('v0.6.0'); | ||
expect(version.downloadURL).toEqual('https://github.com/crazy-max/undock/releases/download/v%s/%s'); | ||
expect(version.releasesURL).toEqual('https://raw.githubusercontent.com/docker/actions-toolkit/main/.github/undock-releases.json'); | ||
}); | ||
}); | ||
|
||
describe('getRelease', () => { | ||
it('returns latest GitHub release', async () => { | ||
const version = await Install.getDownloadVersion('latest'); | ||
const release = await Install.getRelease(version); | ||
expect(release).not.toBeNull(); | ||
expect(release?.tag_name).not.toEqual(''); | ||
}); | ||
it('returns v0.6.0 GitHub release', async () => { | ||
const version = await Install.getDownloadVersion('v0.6.0'); | ||
const release = await Install.getRelease(version); | ||
expect(release).not.toBeNull(); | ||
expect(release?.id).toEqual(121362767); | ||
expect(release?.tag_name).toEqual('v0.6.0'); | ||
expect(release?.html_url).toEqual('https://github.com/crazy-max/undock/releases/tag/v0.6.0'); | ||
}); | ||
it('unknown release', async () => { | ||
const version = await Install.getDownloadVersion('foo'); | ||
await expect(Install.getRelease(version)).rejects.toThrow(new Error('Cannot find Undock release foo in https://raw.githubusercontent.com/docker/actions-toolkit/main/.github/undock-releases.json')); | ||
}); | ||
}); |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
/** | ||
* Copyright 2024 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. | ||
*/ | ||
|
||
export interface DownloadVersion { | ||
version: string; | ||
downloadURL: string; | ||
releasesURL: string; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,166 @@ | ||
/** | ||
* Copyright 2024 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 httpm from '@actions/http-client'; | ||
import * as tc from '@actions/tool-cache'; | ||
import * as semver from 'semver'; | ||
import * as util from 'util'; | ||
|
||
import {Cache} from '../cache'; | ||
import {Context} from '../context'; | ||
|
||
import {GitHubRelease} from '../types/github'; | ||
import {DownloadVersion} from '../types/undock/undock'; | ||
|
||
export class Install { | ||
/* | ||
* Download undock binary from GitHub release | ||
* @param v: version semver version or latest | ||
* @param ghaNoCache: disable binary caching in GitHub Actions cache backend | ||
* @returns path to the undock binary | ||
*/ | ||
public async download(v: string, ghaNoCache?: boolean): Promise<string> { | ||
const version: DownloadVersion = await Install.getDownloadVersion(v); | ||
core.debug(`Install.download version: ${version.version}`); | ||
|
||
const release: GitHubRelease = await Install.getRelease(version); | ||
core.debug(`Install.download release tag name: ${release.tag_name}`); | ||
|
||
const vspec = await this.vspec(release.tag_name); | ||
core.debug(`Install.download vspec: ${vspec}`); | ||
|
||
const c = semver.clean(vspec) || ''; | ||
if (!semver.valid(c)) { | ||
throw new Error(`Invalid Undock version "${vspec}".`); | ||
} | ||
|
||
const installCache = new Cache({ | ||
htcName: 'undock-dl-bin', | ||
htcVersion: vspec, | ||
baseCacheDir: path.join(os.homedir(), '.bin'), | ||
cacheFile: os.platform() == 'win32' ? 'undock.exe' : 'undock', | ||
ghaNoCache: ghaNoCache | ||
}); | ||
|
||
const cacheFoundPath = await installCache.find(); | ||
if (cacheFoundPath) { | ||
core.info(`Unodck binary found in ${cacheFoundPath}`); | ||
return cacheFoundPath; | ||
} | ||
|
||
const downloadURL = util.format(version.downloadURL, vspec, this.filename(vspec)); | ||
core.info(`Downloading ${downloadURL}`); | ||
|
||
const htcDownloadPath = await tc.downloadTool(downloadURL); | ||
core.debug(`Install.download htcDownloadPath: ${htcDownloadPath}`); | ||
|
||
let htcExtPath: string; | ||
if (os.platform() == 'win32') { | ||
htcExtPath = await tc.extractZip(htcDownloadPath); | ||
} else { | ||
htcExtPath = await tc.extractTar(htcDownloadPath); | ||
} | ||
core.info(`Extracted to ${htcExtPath}`); | ||
|
||
const exePath: string = path.join(htcExtPath, os.platform() == 'win32' ? 'undock.exe' : 'undock'); | ||
core.debug(`Install.download exePath: ${exePath}`); | ||
|
||
const cacheSavePath = await installCache.save(exePath); | ||
core.info(`Cached to ${cacheSavePath}`); | ||
return cacheSavePath; | ||
} | ||
|
||
public async install(binPath: string, dest?: string): Promise<string> { | ||
dest = dest || Context.tmpDir(); | ||
|
||
const binDir = path.join(dest, 'undock-bin'); | ||
if (!fs.existsSync(binDir)) { | ||
fs.mkdirSync(binDir, {recursive: true}); | ||
} | ||
const binName: string = os.platform() == 'win32' ? 'undock.exe' : 'undock'; | ||
const undockPath: string = path.join(binDir, binName); | ||
fs.copyFileSync(binPath, undockPath); | ||
|
||
core.info('Fixing perms'); | ||
fs.chmodSync(undockPath, '0755'); | ||
|
||
core.addPath(binDir); | ||
core.info('Added Unodck to PATH'); | ||
|
||
core.info(`Binary path: ${undockPath}`); | ||
return undockPath; | ||
} | ||
|
||
private filename(version: string): string { | ||
let arch: string; | ||
switch (os.arch()) { | ||
case 'x64': { | ||
arch = 'amd64'; | ||
break; | ||
} | ||
case 'ppc64': { | ||
arch = 'ppc64le'; | ||
break; | ||
} | ||
case 'arm': { | ||
// eslint-disable-next-line @typescript-eslint/no-explicit-any | ||
const arm_version = (process.config.variables as any).arm_version; | ||
arch = arm_version ? 'armv' + arm_version : 'arm'; | ||
break; | ||
} | ||
default: { | ||
arch = os.arch(); | ||
break; | ||
} | ||
} | ||
const platform: string = os.platform() == 'win32' ? 'windows' : os.platform(); | ||
const ext: string = os.platform() == 'win32' ? '.zip' : '.tar.gz'; | ||
return util.format('undock_%s_%s_%s%s', version, platform, arch, ext); | ||
} | ||
|
||
private async vspec(version: string): Promise<string> { | ||
const v = version.replace(/^v+|v+$/g, ''); | ||
core.info(`Use ${v} version spec cache key for ${version}`); | ||
return v; | ||
} | ||
|
||
public static async getDownloadVersion(v: string): Promise<DownloadVersion> { | ||
return { | ||
version: v, | ||
downloadURL: 'https://github.com/crazy-max/undock/releases/download/v%s/%s', | ||
releasesURL: 'https://raw.githubusercontent.com/docker/actions-toolkit/main/.github/undock-releases.json' | ||
}; | ||
} | ||
|
||
public static async getRelease(version: DownloadVersion): Promise<GitHubRelease> { | ||
const http: httpm.HttpClient = new httpm.HttpClient('docker-actions-toolkit'); | ||
const resp: httpm.HttpClientResponse = await http.get(version.releasesURL); | ||
const body = await resp.readBody(); | ||
const statusCode = resp.message.statusCode || 500; | ||
if (statusCode >= 400) { | ||
throw new Error(`Failed to get Undock releases from ${version.releasesURL} with status code ${statusCode}: ${body}`); | ||
} | ||
const releases = <Record<string, GitHubRelease>>JSON.parse(body); | ||
if (!releases[version.version]) { | ||
throw new Error(`Cannot find Undock release ${version.version} in ${version.releasesURL}`); | ||
} | ||
return releases[version.version]; | ||
} | ||
} |