-
Notifications
You must be signed in to change notification settings - Fork 9
/
main.js
123 lines (100 loc) · 5.11 KB
/
main.js
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
const os = require('os');
const path = require('path');
const fs = require('fs').promises;
const core = require('@actions/core');
const tc = require('@actions/tool-cache');
const cache = require('@actions/cache');
const common = require('./common');
const minisign = require('./minisign');
// Upstream's minisign key, from https://ziglang.org/download
const MINISIGN_KEY = 'RWSGOq2NVecA2UPNdBUZykf1CCb147pkmdtYxgb3Ti+JO/wCYvhbAb/U';
// The base URL of the official builds of Zig. This is only used as a fallback, if all mirrors fail.
const CANONICAL = 'https://ziglang.org/builds';
// The list of mirrors we attempt to fetch from. These need not be trusted, as
// we always verify the minisign signature.
// This is an array of URLs.
const MIRRORS = require('./mirrors.json').map((x) => x[0]);
async function downloadFromMirror(mirror, tarball_name, tarball_ext) {
const tarball_path = await tc.downloadTool(`${mirror}/${tarball_name}${tarball_ext}?source=github-actions`);
const signature_response = await fetch(`${mirror}/${tarball_name}${tarball_ext}.minisig?source=github-actions`);
const signature_data = Buffer.from(await signature_response.arrayBuffer());
const tarball_data = await fs.readFile(tarball_path);
const key = minisign.parseKey(MINISIGN_KEY);
const signature = minisign.parseSignature(signature_data);
if (!minisign.verifySignature(key, signature, tarball_data)) {
throw new Error(`signature verification failed for '${mirror}/${tarball_name}${tarball_ext}'`);
}
return tarball_path;
}
async function downloadTarball(tarball_name, tarball_ext) {
const preferred_mirror = core.getInput('mirror');
if (preferred_mirror.includes("://ziglang.org/") || preferred_mirror.startsWith("ziglang.org/")) {
throw new Error("'https://ziglang.org' cannot be used as mirror override; for more information see README.md");
}
if (preferred_mirror) {
core.info(`Using mirror: ${preferred_mirror}`);
return await downloadFromMirror(preferred_mirror, tarball_name, tarball_ext);
}
// We will attempt all mirrors before making a last-ditch attempt to the official download.
// To avoid hammering a single mirror, we first randomize the array.
const shuffled_mirrors = MIRRORS.map((m) => [m, Math.random()]).sort((a, b) => a[1] - b[1]).map((a) => a[0]);
for (const mirror of shuffled_mirrors) {
core.info(`Attempting mirror: ${mirror}`);
try {
return await downloadFromMirror(mirror, tarball_name, tarball_ext);
} catch (e) {
core.info(`Mirror failed with error: ${e}`);
// continue loop to next mirror
}
}
core.info(`Attempting official: ${CANONICAL}`);
return await downloadFromMirror(CANONICAL, tarball_name, tarball_ext);
}
async function retrieveTarball(tarball_name, tarball_ext) {
const cache_key = `setup-zig-tarball-${tarball_name}`;
const tarball_cache_path = await common.getTarballCachePath();
if (await cache.restoreCache([tarball_cache_path], cache_key)) {
return tarball_cache_path;
}
core.info(`Cache miss. Fetching Zig ${await common.getVersion()}`);
const downloaded_path = await downloadTarball(tarball_name, tarball_ext);
await fs.copyFile(downloaded_path, tarball_cache_path)
await cache.saveCache([tarball_cache_path], cache_key);
return tarball_cache_path;
}
async function main() {
try {
// We will check whether Zig is stored in the cache. We use two separate caches.
// * 'tool-cache' caches the final extracted directory if the same Zig build is used multiple
// times by one job. We have this dependency anyway for archive extraction.
// * 'cache' only caches the unextracted archive, but it does so across runs. It's a little
// less efficient, but still much preferable to fetching Zig from a mirror. We have this
// dependency anyway for caching the global Zig cache.
let zig_dir = tc.find('zig', await common.getVersion());
if (!zig_dir) {
const tarball_name = await common.getTarballName();
const tarball_ext = await common.getTarballExt();
core.info(`Fetching ${tarball_name}${tarball_ext}`);
const fetch_start = Date.now();
const tarball_path = await retrieveTarball(tarball_name, tarball_ext);
core.info(`fetch took ${Date.now() - fetch_start} ms`);
core.info(`Extracting tarball ${tarball_name}${tarball_ext}`);
const extract_start = Date.now();
const zig_parent_dir = tarball_ext === '.zip' ?
await tc.extractZip(tarball_path) :
await tc.extractTar(tarball_path, null, 'xJ'); // J for xz
core.info(`extract took ${Date.now() - extract_start} ms`);
const zig_inner_dir = path.join(zig_parent_dir, tarball_name);
zig_dir = await tc.cacheDir(zig_inner_dir, 'zig', await common.getVersion());
}
core.addPath(zig_dir);
// Direct Zig to use the global cache as every local cache, so that we get maximum benefit from the caching below.
core.exportVariable('ZIG_LOCAL_CACHE_DIR', await common.getZigCachePath());
if (core.getBooleanInput('use-cache')) {
await cache.restoreCache([await common.getZigCachePath()], await common.getCachePrefix());
}
} catch (err) {
core.setFailed(err.message);
}
}
main();