starting v2 and proxy support

This commit is contained in:
Bryan MacFarlane
2020-02-09 00:21:39 -05:00
parent 9fbc767707
commit 4282769cc0
191 changed files with 5351 additions and 14572 deletions

View File

@ -1,223 +1,91 @@
// Load tempDirectory before it gets wiped by tool-cache
let tempDirectory = process.env['RUNNER_TEMPDIRECTORY'] || '';
import * as core from '@actions/core';
import * as tc from '@actions/tool-cache';
import * as os from 'os';
import * as path from 'path';
import * as util from 'util';
import * as semver from 'semver';
import * as restm from 'typed-rest-client/RestClient';
import * as httpm from '@actions/http-client'
import * as sys from './system'
let osPlat: string = os.platform();
let osArch: string = os.arch();
export async function downloadGo(versionSpec: string, stable: boolean): Promise<string | undefined> {
let toolPath: string | undefined;
if (!tempDirectory) {
let baseLocation;
if (process.platform === 'win32') {
// On windows use the USERPROFILE env variable
baseLocation = process.env['USERPROFILE'] || 'C:\\';
} else {
if (process.platform === 'darwin') {
baseLocation = '/Users';
} else {
baseLocation = '/home';
}
}
tempDirectory = path.join(baseLocation, 'actions', 'temp');
}
export async function getGo(version: string) {
const selected = await determineVersion(version);
if (selected) {
version = selected;
}
// check cache
let toolPath: string;
toolPath = tc.find('go', normalizeVersion(version));
if (!toolPath) {
// download, extract, cache
toolPath = await acquireGo(version);
core.debug('Go tool is cached under ' + toolPath);
}
setGoEnvironmentVariables(toolPath);
toolPath = path.join(toolPath, 'bin');
//
// prepend the tools path. instructs the agent to prepend for future tasks
//
core.addPath(toolPath);
}
async function acquireGo(version: string): Promise<string> {
//
// Download - a tool installer intimately knows how to get the tool (and construct urls)
//
let fileName: string = getFileName(version);
let downloadUrl: string = getDownloadUrl(fileName);
core.debug('Downloading Go from: ' + downloadUrl);
let downloadPath: string | null = null;
try {
downloadPath = await tc.downloadTool(downloadUrl);
let match: IGoVersion | undefined = await findMatch(versionSpec, stable);
if (match) {
// download
let downloadUrl: string = `https://storage.googleapis.com/golang/${match.files[0]}`;
let downloadPath: string = await tc.downloadTool(downloadUrl);
// extract
let extPath: string = sys.getPlatform() == 'windows'?
await tc.extractZip(downloadPath): await tc.extractTar(downloadPath);
// extracts with a root folder that matches the fileName downloaded
const toolRoot = path.join(extPath, 'go');
toolPath = await tc.cacheDir(toolRoot, 'go', versionSpec);
}
} catch (error) {
core.debug(error);
throw `Failed to download version ${version}: ${error}`;
throw `Failed to download version ${versionSpec}: ${error}`;
}
//
// Extract
//
let extPath: string = tempDirectory;
if (!extPath) {
throw new Error('Temp directory not set');
}
if (osPlat == 'win32') {
extPath = await tc.extractZip(downloadPath);
} else {
extPath = await tc.extractTar(downloadPath);
}
//
// Install into the local tool cache - node extracts with a root folder that matches the fileName downloaded
//
const toolRoot = path.join(extPath, 'go');
version = normalizeVersion(version);
return await tc.cacheDir(toolRoot, 'go', version);
return toolPath;
}
function getFileName(version: string): string {
const arches: {[arch: string]: string} = {
x64: 'amd64',
arm: 'armv6l',
arm64: 'arm64',
default: '386'
export interface IGoVersionFile {
filename: string,
// darwin, linux, windows
os: string,
arch: string
}
export interface IGoVersion {
version: string;
stable: boolean;
files: IGoVersionFile[];
}
export async function findMatch(versionSpec: string, stable: boolean): Promise<IGoVersion | undefined> {
let archFilter = sys.getArch();
let platFilter = sys.getPlatform();
let match: IGoVersion| undefined;
const dlUrl: string = 'https://golang.org/dl/?mode=json&include=all';
// this returns versions descending so latest is first
let http: httpm.HttpClient = new httpm.HttpClient('setup-go');
let candidates: IGoVersion[] | null = (await http.getJson<IGoVersion[]>(dlUrl)).result;
if (!candidates) {
throw new Error(`golang download url did not return results: ${dlUrl}`);
}
let goFile: IGoVersionFile | undefined;
for (let i=0; i < candidates.length; i++) {
let candidate: IGoVersion = candidates[i];
let version = candidate.version.replace('go', '');
// 1.13.0 is advertised as 1.13 preventing being able to match exactly 1.13.0
// since a semver of 1.13 would match latest 1.13
let parts: string[] = version.split('.');
if (parts.length == 2) {
version = version + '.0';
}
//console.log(version, versionSpec);
if (semver.satisfies(version, versionSpec) && candidate.stable == stable) {
goFile = candidate.files.find(file => {
return file.arch === archFilter && file.os === platFilter;
});
if (goFile) {
match = candidate;
break;
}
}
};
const platform: string = osPlat == 'win32' ? 'windows' : osPlat;
const arch: string = arches[osArch] || arches['default'];
const ext: string = osPlat == 'win32' ? 'zip' : 'tar.gz';
const filename: string = util.format(
'go%s.%s-%s.%s',
version,
platform,
arch,
ext
);
return filename;
}
function getDownloadUrl(filename: string): string {
return util.format('https://storage.googleapis.com/golang/%s', filename);
}
function setGoEnvironmentVariables(goRoot: string) {
core.exportVariable('GOROOT', goRoot);
const goPath: string = process.env['GOPATH'] || '';
const goBin: string = process.env['GOBIN'] || '';
// set GOPATH and GOBIN as user value
if (goPath) {
core.exportVariable('GOPATH', goPath);
}
if (goBin) {
core.exportVariable('GOBIN', goBin);
}
}
// This function is required to convert the version 1.10 to 1.10.0.
// Because caching utility accept only sementic version,
// which have patch number as well.
function normalizeVersion(version: string): string {
const versionPart = version.split('.');
if (versionPart[1] == null) {
//append minor and patch version if not available
return version.concat('.0.0');
} else {
// handle beta and rc: 1.10beta1 => 1.10.0-beta1, 1.10rc1 => 1.10.0-rc1
if (versionPart[1].includes('beta') || versionPart[1].includes('rc')) {
versionPart[1] = versionPart[1]
.replace('beta', '.0-beta')
.replace('rc', '.0-rc');
return versionPart.join('.');
}
if (match && goFile) {
match.files = [ goFile ];
}
if (versionPart[2] == null) {
//append patch version if not available
return version.concat('.0');
} else {
// handle beta and rc: 1.8.5beta1 => 1.8.5-beta1, 1.8.5rc1 => 1.8.5-rc1
if (versionPart[2].includes('beta') || versionPart[2].includes('rc')) {
versionPart[2] = versionPart[2]
.replace('beta', '-beta')
.replace('rc', '-rc');
return versionPart.join('.');
}
}
return version;
}
async function determineVersion(version: string): Promise<string> {
if (!version.endsWith('.x')) {
const versionPart = version.split('.');
if (versionPart[1] == null || versionPart[2] == null) {
return await getLatestVersion(version.concat('.x'));
} else {
return version;
}
}
return await getLatestVersion(version);
}
async function getLatestVersion(version: string): Promise<string> {
// clean .x syntax: 1.10.x -> 1.10
const trimmedVersion = version.slice(0, version.length - 2);
const versions = await getPossibleVersions(trimmedVersion);
core.debug(`evaluating ${versions.length} versions`);
if (version.length === 0) {
throw new Error('unable to get latest version');
}
core.debug(`matched: ${versions[0]}`);
return versions[0];
}
interface IGoRef {
version: string;
}
async function getAvailableVersions(): Promise<string[]> {
let rest: restm.RestClient = new restm.RestClient('setup-go');
let tags: IGoRef[] =
(await rest.get<IGoRef[]>('https://golang.org/dl/?mode=json&include=all'))
.result || [];
return tags.map(tag => tag.version.replace('go', ''));
}
async function getPossibleVersions(version: string): Promise<string[]> {
const versions = await getAvailableVersions();
const possibleVersions = versions.filter(v => v.startsWith(version));
const versionMap = new Map();
possibleVersions.forEach(v => versionMap.set(normalizeVersion(v), v));
return Array.from(versionMap.keys())
.sort(semver.rcompare)
.map(v => versionMap.get(v));
return match;
}