mirror of
https://github.com/julia-actions/setup-julia.git
synced 2026-02-12 02:56:54 +08:00
Verify checksums (#49)
Co-authored-by: Derk-Jan Karrenbeld <derk-jan+github@karrenbeld.info>
This commit is contained in:
@@ -34,35 +34,35 @@ const toolDir = path.join(__dirname, 'runner', 'tools')
|
|||||||
const tempDir = path.join(__dirname, 'runner', 'temp')
|
const tempDir = path.join(__dirname, 'runner', 'temp')
|
||||||
const fixtureDir = path.join(__dirname, 'fixtures')
|
const fixtureDir = path.join(__dirname, 'fixtures')
|
||||||
|
|
||||||
process.env['RUNNER_TOOL_CACHE'] = toolDir;
|
process.env['RUNNER_TOOL_CACHE'] = toolDir
|
||||||
process.env['RUNNER_TEMP'] = tempDir;
|
process.env['RUNNER_TEMP'] = tempDir
|
||||||
|
|
||||||
import * as installer from '../src/installer'
|
import * as installer from '../src/installer'
|
||||||
|
|
||||||
describe('version matching tests', () => {
|
describe('version matching tests', () => {
|
||||||
describe('specific versions', () => {
|
describe('specific versions', () => {
|
||||||
it('Doesn\'t change the version when given a valid semver version', async () => {
|
it('Doesn\'t change the version when given a valid semver version', () => {
|
||||||
expect(await installer.getJuliaVersion([], '1.0.5')).toEqual('1.0.5')
|
expect(installer.getJuliaVersion([], '1.0.5')).toEqual('1.0.5')
|
||||||
expect(await installer.getJuliaVersion(['v1.0.5', 'v1.0.6'], '1.0.5')).toEqual('1.0.5')
|
expect(installer.getJuliaVersion(['v1.0.5', 'v1.0.6'], '1.0.5')).toEqual('1.0.5')
|
||||||
expect(await installer.getJuliaVersion(['v1.0.4', 'v1.0.5'], '1.0.5')).toEqual('1.0.5')
|
expect(installer.getJuliaVersion(['v1.0.4', 'v1.0.5'], '1.0.5')).toEqual('1.0.5')
|
||||||
expect(await installer.getJuliaVersion(['v1.0.4'], '1.0.5')).toEqual('1.0.5')
|
expect(installer.getJuliaVersion(['v1.0.4'], '1.0.5')).toEqual('1.0.5')
|
||||||
expect(await installer.getJuliaVersion([], '1.3.0-alpha')).toEqual('1.3.0-alpha')
|
expect(installer.getJuliaVersion([], '1.3.0-alpha')).toEqual('1.3.0-alpha')
|
||||||
expect(await installer.getJuliaVersion(['v1.2.0', 'v1.3.0-alpha', 'v1.3.0-rc1', 'v1.3.0'], '1.3.0-alpha')).toEqual('1.3.0-alpha')
|
expect(installer.getJuliaVersion(['v1.2.0', 'v1.3.0-alpha', 'v1.3.0-rc1', 'v1.3.0'], '1.3.0-alpha')).toEqual('1.3.0-alpha')
|
||||||
expect(await installer.getJuliaVersion([], '1.3.0-rc2')).toEqual('1.3.0-rc2')
|
expect(installer.getJuliaVersion([], '1.3.0-rc2')).toEqual('1.3.0-rc2')
|
||||||
})
|
})
|
||||||
|
|
||||||
it('Doesn\'t change the version when given `nightly`', async () => {
|
it('Doesn\'t change the version when given `nightly`', () => {
|
||||||
expect(await installer.getJuliaVersion([], 'nightly')).toEqual('nightly')
|
expect(installer.getJuliaVersion([], 'nightly')).toEqual('nightly')
|
||||||
expect(await installer.getJuliaVersion(testVersions, 'nightly')).toEqual('nightly')
|
expect(installer.getJuliaVersion(testVersions, 'nightly')).toEqual('nightly')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
describe('version ranges', () => {
|
describe('version ranges', () => {
|
||||||
it('Chooses the highest available version that matches the input', async () => {
|
it('Chooses the highest available version that matches the input', () => {
|
||||||
expect(await installer.getJuliaVersion(testVersions, '1')).toEqual('1.2.0')
|
expect(installer.getJuliaVersion(testVersions, '1')).toEqual('1.2.0')
|
||||||
expect(await installer.getJuliaVersion(testVersions, '1.0')).toEqual('1.0.5')
|
expect(installer.getJuliaVersion(testVersions, '1.0')).toEqual('1.0.5')
|
||||||
expect(await installer.getJuliaVersion(testVersions, '^1.3.0-rc1')).toEqual('1.3.0-rc4')
|
expect(installer.getJuliaVersion(testVersions, '^1.3.0-rc1')).toEqual('1.3.0-rc4')
|
||||||
expect(await installer.getJuliaVersion(testVersions, '^1.2.0-rc1')).toEqual('1.2.0')
|
expect(installer.getJuliaVersion(testVersions, '^1.2.0-rc1')).toEqual('1.2.0')
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
|
||||||
@@ -96,7 +96,7 @@ describe('installer tests', () => {
|
|||||||
describe('versions.json parsing', () => {
|
describe('versions.json parsing', () => {
|
||||||
// Instead of downloading versions.json, use fixtures/versions.json
|
// Instead of downloading versions.json, use fixtures/versions.json
|
||||||
beforeEach(() => {
|
beforeEach(() => {
|
||||||
nock('https://julialang-s3.julialang.org')
|
nock('https://julialang-s3.julialang.org').persist()
|
||||||
.get('/bin/versions.json')
|
.get('/bin/versions.json')
|
||||||
.replyWithFile(200, path.join(fixtureDir, 'versions.json'))
|
.replyWithFile(200, path.join(fixtureDir, 'versions.json'))
|
||||||
})
|
})
|
||||||
|
|||||||
@@ -1,50 +0,0 @@
|
|||||||
import * as installer from '../src/installer'
|
|
||||||
|
|
||||||
import * as semver from 'semver'
|
|
||||||
|
|
||||||
const testVersions = ['v1.3.0-rc4', 'v1.3.0-rc3', 'v1.3.0-rc2', 'v1.0.5', 'v1.2.0', 'v1.3.0-rc1', 'v1.2.0-rc3', 'v1.3.0-alpha', 'v1.2.0-rc2', 'v1.2.0-rc1', 'v1.1.1', 'v1.0.4', 'v1.1.0', 'v1.1.0-rc2', 'v1.1.0-rc1', 'v1.0.3', 'v1.0.2', 'v1.0.1', 'v1.0.0']
|
|
||||||
|
|
||||||
describe('installer tests', () => {
|
|
||||||
describe('version matching', () => {
|
|
||||||
describe('specific versions', () => {
|
|
||||||
it('Doesn\'t change the version when given a valid semver version', async () => {
|
|
||||||
expect(await installer.getJuliaVersion([], '1.0.5')).toEqual('1.0.5')
|
|
||||||
expect(await installer.getJuliaVersion(['v1.0.5', 'v1.0.6'], '1.0.5')).toEqual('1.0.5')
|
|
||||||
expect(await installer.getJuliaVersion(['v1.0.4', 'v1.0.5'], '1.0.5')).toEqual('1.0.5')
|
|
||||||
expect(await installer.getJuliaVersion(['v1.0.4'], '1.0.5')).toEqual('1.0.5')
|
|
||||||
expect(await installer.getJuliaVersion([], '1.3.0-alpha')).toEqual('1.3.0-alpha')
|
|
||||||
expect(await installer.getJuliaVersion(['v1.2.0', 'v1.3.0-alpha', 'v1.3.0-rc1', 'v1.3.0'], '1.3.0-alpha')).toEqual('1.3.0-alpha')
|
|
||||||
expect(await installer.getJuliaVersion([], '1.3.0-rc2')).toEqual('1.3.0-rc2')
|
|
||||||
})
|
|
||||||
it('Doesn\'t change the version when given `nightly`', async () => {
|
|
||||||
expect(await installer.getJuliaVersion([], 'nightly')).toEqual('nightly')
|
|
||||||
expect(await installer.getJuliaVersion(testVersions, 'nightly')).toEqual('nightly')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('version ranges', () => {
|
|
||||||
it('Chooses the highest available version that matches the input', async () => {
|
|
||||||
expect(await installer.getJuliaVersion(testVersions, '1')).toEqual('1.2.0')
|
|
||||||
expect(await installer.getJuliaVersion(testVersions, '1.0')).toEqual('1.0.5')
|
|
||||||
expect(await installer.getJuliaVersion(testVersions, '^1.3.0-rc1')).toEqual('1.3.0-rc4')
|
|
||||||
expect(await installer.getJuliaVersion(testVersions, '^1.2.0-rc1')).toEqual('1.2.0')
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('invalid version range (#38)', () => {
|
|
||||||
it('Throws an error if a version range does not match any available version', () => {
|
|
||||||
expect(() => {
|
|
||||||
installer.getJuliaVersion(['v1.5.0-rc1', 'v1.5.0-beta1', 'v1.4.2', 'v1.4.1', 'v1.4.0', 'v1.4.0-rc2', 'v1.4.0-rc1'], '1.6')
|
|
||||||
}).toThrowError()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
describe('node-semver behaviour', () => {
|
|
||||||
describe('Windows installer change', () => {
|
|
||||||
it('Correctly understands >1.4.0', () => {
|
|
||||||
expect(semver.gtr('1.4.0-rc1', '1.3', {includePrerelease: true})).toBeTruthy()
|
|
||||||
expect(semver.gtr('1.4.0-DEV', '1.3', {includePrerelease: true})).toBeTruthy()
|
|
||||||
expect(semver.gtr('1.3.1', '1.3', {includePrerelease: true})).toBeFalsy()
|
|
||||||
expect(semver.gtr('1.3.2-rc1', '1.3', {includePrerelease: true})).toBeFalsy()
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
})
|
|
||||||
55
lib/installer.js
generated
55
lib/installer.js
generated
@@ -18,6 +18,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
|
|||||||
const core = __importStar(require("@actions/core"));
|
const core = __importStar(require("@actions/core"));
|
||||||
const exec = __importStar(require("@actions/exec"));
|
const exec = __importStar(require("@actions/exec"));
|
||||||
const tc = __importStar(require("@actions/tool-cache"));
|
const tc = __importStar(require("@actions/tool-cache"));
|
||||||
|
const crypto = __importStar(require("crypto"));
|
||||||
const fs = __importStar(require("fs"));
|
const fs = __importStar(require("fs"));
|
||||||
const os = __importStar(require("os"));
|
const os = __importStar(require("os"));
|
||||||
const path = __importStar(require("path"));
|
const path = __importStar(require("path"));
|
||||||
@@ -35,6 +36,24 @@ const archMap = {
|
|||||||
// Store information about the environment
|
// Store information about the environment
|
||||||
const osPlat = os.platform(); // possible values: win32 (Windows), linux (Linux), darwin (macOS)
|
const osPlat = os.platform(); // possible values: win32 (Windows), linux (Linux), darwin (macOS)
|
||||||
core.debug(`platform: ${osPlat}`);
|
core.debug(`platform: ${osPlat}`);
|
||||||
|
/**
|
||||||
|
* @returns The SHA256 checksum of a given file.
|
||||||
|
*/
|
||||||
|
function calculateChecksum(file) {
|
||||||
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
|
const hash = crypto.createHash('sha256');
|
||||||
|
const input = fs.createReadStream(file);
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
input.on('data', (chunk) => {
|
||||||
|
hash.update(chunk);
|
||||||
|
});
|
||||||
|
input.on('end', () => {
|
||||||
|
const digest = hash.digest('hex');
|
||||||
|
digest ? resolve(digest) : reject(new Error(`Could not calculate checksum of file ${file}: digest was empty.`));
|
||||||
|
});
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
/**
|
/**
|
||||||
* @returns The content of the downloaded versions.json file as object.
|
* @returns The content of the downloaded versions.json file as object.
|
||||||
*/
|
*/
|
||||||
@@ -99,27 +118,45 @@ function getNightlyFileName(arch) {
|
|||||||
}
|
}
|
||||||
return `julia-latest${versionExt}.${ext}`;
|
return `julia-latest${versionExt}.${ext}`;
|
||||||
}
|
}
|
||||||
function getDownloadURL(versionInfo, version, arch) {
|
function getFileInfo(versionInfo, version, arch) {
|
||||||
|
if (version == 'nightly') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
for (let file of versionInfo[version].files) {
|
||||||
|
if (file.os == osMap[osPlat] && file.arch == archMap[arch]) {
|
||||||
|
return file;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
throw `Could not find ${archMap[arch]}/${version} binaries`;
|
||||||
|
}
|
||||||
|
exports.getFileInfo = getFileInfo;
|
||||||
|
function getDownloadURL(fileInfo, version, arch) {
|
||||||
// nightlies
|
// nightlies
|
||||||
if (version == 'nightly') {
|
if (version == 'nightly') {
|
||||||
const baseURL = 'https://julialangnightlies-s3.julialang.org/bin';
|
const baseURL = 'https://julialangnightlies-s3.julialang.org/bin';
|
||||||
return `${baseURL}/${osMap[osPlat]}/${arch}/${getNightlyFileName(arch)}`;
|
return `${baseURL}/${osMap[osPlat]}/${arch}/${getNightlyFileName(arch)}`;
|
||||||
}
|
}
|
||||||
for (let file of versionInfo[version].files) {
|
return fileInfo.url;
|
||||||
if (file.os == osMap[osPlat] && file.arch == archMap[arch]) {
|
|
||||||
core.debug(file);
|
|
||||||
return file.url;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
throw `Could not find ${archMap[arch]}/${version} binaries`;
|
|
||||||
}
|
}
|
||||||
exports.getDownloadURL = getDownloadURL;
|
exports.getDownloadURL = getDownloadURL;
|
||||||
function installJulia(versionInfo, version, arch) {
|
function installJulia(versionInfo, version, arch) {
|
||||||
return __awaiter(this, void 0, void 0, function* () {
|
return __awaiter(this, void 0, void 0, function* () {
|
||||||
// Download Julia
|
// Download Julia
|
||||||
const downloadURL = getDownloadURL(versionInfo, version, arch);
|
const fileInfo = getFileInfo(versionInfo, version, arch);
|
||||||
|
const downloadURL = getDownloadURL(fileInfo, version, arch);
|
||||||
core.debug(`downloading Julia from ${downloadURL}`);
|
core.debug(`downloading Julia from ${downloadURL}`);
|
||||||
const juliaDownloadPath = yield tc.downloadTool(downloadURL);
|
const juliaDownloadPath = yield tc.downloadTool(downloadURL);
|
||||||
|
// Verify checksum
|
||||||
|
if (version != 'nightly') {
|
||||||
|
const checkSum = yield calculateChecksum(juliaDownloadPath);
|
||||||
|
if (fileInfo.sha256 != checkSum) {
|
||||||
|
throw new Error(`Checksum of downloaded file does not match the expected checksum from versions.json.\nExpected: ${fileInfo.sha256}\nGot: ${checkSum}`);
|
||||||
|
}
|
||||||
|
core.debug(`Checksum of downloaded file matches expected checksum: ${checkSum}`);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
core.debug('Skipping checksum check for nightly binaries.');
|
||||||
|
}
|
||||||
const tempInstallDir = fs.mkdtempSync(`julia-${arch}-${version}-`);
|
const tempInstallDir = fs.mkdtempSync(`julia-${arch}-${version}-`);
|
||||||
// Install it
|
// Install it
|
||||||
switch (osPlat) {
|
switch (osPlat) {
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ import * as core from '@actions/core'
|
|||||||
import * as exec from '@actions/exec'
|
import * as exec from '@actions/exec'
|
||||||
import * as tc from '@actions/tool-cache'
|
import * as tc from '@actions/tool-cache'
|
||||||
|
|
||||||
|
import * as crypto from 'crypto'
|
||||||
import * as fs from 'fs'
|
import * as fs from 'fs'
|
||||||
import * as os from 'os'
|
import * as os from 'os'
|
||||||
import * as path from 'path'
|
import * as path from 'path'
|
||||||
@@ -23,6 +24,25 @@ const archMap = {
|
|||||||
const osPlat = os.platform() // possible values: win32 (Windows), linux (Linux), darwin (macOS)
|
const osPlat = os.platform() // possible values: win32 (Windows), linux (Linux), darwin (macOS)
|
||||||
core.debug(`platform: ${osPlat}`)
|
core.debug(`platform: ${osPlat}`)
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @returns The SHA256 checksum of a given file.
|
||||||
|
*/
|
||||||
|
async function calculateChecksum(file: string): Promise<string> {
|
||||||
|
const hash = crypto.createHash('sha256')
|
||||||
|
const input = fs.createReadStream(file)
|
||||||
|
|
||||||
|
return new Promise((resolve, reject) => {
|
||||||
|
input.on('data', (chunk) => {
|
||||||
|
hash.update(chunk)
|
||||||
|
})
|
||||||
|
|
||||||
|
input.on('end', () => {
|
||||||
|
const digest = hash.digest('hex')
|
||||||
|
digest ? resolve(digest) : reject(new Error(`Could not calculate checksum of file ${file}: digest was empty.`))
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @returns The content of the downloaded versions.json file as object.
|
* @returns The content of the downloaded versions.json file as object.
|
||||||
*/
|
*/
|
||||||
@@ -90,29 +110,48 @@ function getNightlyFileName(arch: string): string {
|
|||||||
return `julia-latest${versionExt}.${ext}`
|
return `julia-latest${versionExt}.${ext}`
|
||||||
}
|
}
|
||||||
|
|
||||||
export function getDownloadURL(versionInfo, version: string, arch: string): string {
|
export function getFileInfo(versionInfo, version: string, arch: string) {
|
||||||
// nightlies
|
|
||||||
if (version == 'nightly') {
|
if (version == 'nightly') {
|
||||||
const baseURL = 'https://julialangnightlies-s3.julialang.org/bin'
|
return null
|
||||||
return `${baseURL}/${osMap[osPlat]}/${arch}/${getNightlyFileName(arch)}`
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (let file of versionInfo[version].files) {
|
for (let file of versionInfo[version].files) {
|
||||||
if (file.os == osMap[osPlat] && file.arch == archMap[arch]) {
|
if (file.os == osMap[osPlat] && file.arch == archMap[arch]) {
|
||||||
core.debug(file)
|
return file
|
||||||
return file.url
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw `Could not find ${archMap[arch]}/${version} binaries`
|
throw `Could not find ${archMap[arch]}/${version} binaries`
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export function getDownloadURL(fileInfo, version: string, arch: string): string {
|
||||||
|
// nightlies
|
||||||
|
if (version == 'nightly') {
|
||||||
|
const baseURL = 'https://julialangnightlies-s3.julialang.org/bin'
|
||||||
|
return `${baseURL}/${osMap[osPlat]}/${arch}/${getNightlyFileName(arch)}`
|
||||||
|
}
|
||||||
|
|
||||||
|
return fileInfo.url
|
||||||
|
}
|
||||||
|
|
||||||
export async function installJulia(versionInfo, version: string, arch: string): Promise<string> {
|
export async function installJulia(versionInfo, version: string, arch: string): Promise<string> {
|
||||||
// Download Julia
|
// Download Julia
|
||||||
const downloadURL = getDownloadURL(versionInfo, version, arch)
|
const fileInfo = getFileInfo(versionInfo, version, arch)
|
||||||
|
const downloadURL = getDownloadURL(fileInfo, version, arch)
|
||||||
core.debug(`downloading Julia from ${downloadURL}`)
|
core.debug(`downloading Julia from ${downloadURL}`)
|
||||||
const juliaDownloadPath = await tc.downloadTool(downloadURL)
|
const juliaDownloadPath = await tc.downloadTool(downloadURL)
|
||||||
|
|
||||||
|
// Verify checksum
|
||||||
|
if (version != 'nightly') {
|
||||||
|
const checkSum = await calculateChecksum(juliaDownloadPath)
|
||||||
|
if (fileInfo.sha256 != checkSum) {
|
||||||
|
throw new Error(`Checksum of downloaded file does not match the expected checksum from versions.json.\nExpected: ${fileInfo.sha256}\nGot: ${checkSum}`)
|
||||||
|
}
|
||||||
|
core.debug(`Checksum of downloaded file matches expected checksum: ${checkSum}`)
|
||||||
|
} else {
|
||||||
|
core.debug('Skipping checksum check for nightly binaries.')
|
||||||
|
}
|
||||||
|
|
||||||
const tempInstallDir = fs.mkdtempSync(`julia-${arch}-${version}-`)
|
const tempInstallDir = fs.mkdtempSync(`julia-${arch}-${version}-`)
|
||||||
|
|
||||||
// Install it
|
// Install it
|
||||||
|
|||||||
Reference in New Issue
Block a user