-
-
Notifications
You must be signed in to change notification settings - Fork 7
/
setup-lando.js
executable file
·222 lines (183 loc) · 9.38 KB
/
setup-lando.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
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
'use strict';
let SCRIPT_VERSION;
const core = require('@actions/core');
const exec = require('@actions/exec');
const fs = require('fs');
const get = require('lodash.get');
const io = require('@actions/io');
const os = require('os');
const path = require('path');
const tc = require('@actions/tool-cache');
const yaml = require('js-yaml');
const {execSync} = require('child_process');
const {GitHub, getOctokitOptions} = require('@actions/github/lib/utils');
const {paginateRest} = require('@octokit/plugin-paginate-rest');
const canBeSlim = require('./utils/can-be-slim');
const getConfigFile = require('./utils/get-config-file');
const getDownloadUrl = require('./utils/get-download-url');
const getGCFPath = require('./utils/get-gcf-path');
const getInputs = require('./utils/get-inputs');
const getFileVersion = require('./utils/get-file-version');
const getObjectKeys = require('./utils/get-object-keys');
const getSetupCommand = require('./utils/get-setup-command');
const mergeConfig = require('./utils/merge-config');
const parseSetupCommand = require('./utils/parse-setup-command');
const resolveVersionSpec = require('./utils/resolve-version-spec');
// if there is no script version lets get it from git
if (!SCRIPT_VERSION) {
const output = execSync(`git describe --tags --always --abbrev=1`, {
maxBuffer: 1024 * 1024 * 10,
encoding: 'utf-8',
env: {...process.env, LANDO_DEBUG: 0},
});
SCRIPT_VERSION = typeof output === 'string' ? output.trim() : 'unknown';
}
const main = async () => {
// ensure needed RUNNER_ vars are set
// @NOTE: this is just to ensure we can run this locally
if (!get(process, 'env.GITHUB_WORKSPACE', false)) process.env.GITHUB_WORKSPACE = process.cwd();
if (!get(process, 'env.RUNNER_DEBUG', false)) process.env.RUNNER_DEBUG = core.isDebug();
if (!get(process, 'env.RUNNER_TEMP', false)) process.env.RUNNER_TEMP = os.tmpdir();
if (!get(process, 'env.RUNNER_TOOL_CACHE', false)) process.env.RUNNER_TOOL_CACHE = os.tmpdir();
// start by getting the inputs and stuff
const inputs = getInputs();
// debug the script version
core.debug(`running setup-lando.js script version: ${SCRIPT_VERSION}`);
// immediately try to determine our slim status
inputs.slim = inputs.landoVersion.endsWith('-slim');
// show a warning if both version inputs are set
if (inputs.landoVersion && inputs.landoVersionFile) {
core.warning('Both lando-version and lando-version-file inputs are specified, only lando-version will be used');
}
// prefer autoSetup to setup because setup is DEPRECATED
inputs.setup = inputs.autoSetup ?? inputs.setup;
// determine lando version spec to install
const spec = inputs.landoVersion || getFileVersion(inputs.landoVersionFile) || get(process, 'env.LANDO_VERSION') || 'stable'; // eslint-disable-line max-len
core.debug(`rolling with "${spec}" as version spec`);
// get a pagination vibed octokit so we can get ALL release data
const Octokit = GitHub.plugin(paginateRest);
const octokit = new Octokit(getOctokitOptions(inputs.token));
// try/catch
try {
const releases = await Promise.all([
await octokit.paginate('GET /repos/{owner}/{repo}/releases', {owner: 'lando', repo: 'core', per_page: 100}),
await octokit.paginate('GET /repos/{owner}/{repo}/releases', {owner: 'lando', repo: 'core-next', per_page: 100}),
].flat(Number.POSITIVE_INFINITY));
core.debug(`found ${releases.length} valid releases`);
// attempt to resolve the spec
let version = resolveVersionSpec(spec, releases, 3, inputs);
// throw error if we cannot resolve a version
if (!version) throw new Error(`Could not resolve "${spec}" into an installable version of Lando`);
// now that we have a version lets try to reevaluate slim since it should only be true in the >3.21 <4 range
if (inputs.slim) inputs.slim = inputs.slim && canBeSlim(version);
// start by assuming that version is just the path to some locally installed version of lando
let landoPath = version;
core.startGroup('Version information');
core.info(`spec: ${spec}`);
core.info(`version: ${version}`);
core.info(`slim: ${inputs.slim}`);
// if that assumption is wrong then we need to attempt a download
if (!fs.existsSync(landoPath)) {
// determine url of lando version to install
const downloadUrl = getDownloadUrl(version, inputs);
core.debug(`going to download version ${version} from ${downloadUrl}`);
core.info(`url: ${downloadUrl}`);
// download lando
try {
landoPath = await tc.downloadTool(downloadUrl);
} catch (error) {
throw new Error(`Unable to download Lando ${version} from ${downloadUrl}. ${error.message}`);
}
}
// if on windows we need to move and rename so it ends in exe if it doesnt already
if (inputs.os === 'Windows' && path.extname(landoPath) === '') {
await io.cp(landoPath, `${landoPath}.exe`, {force: true});
landoPath = `${landoPath}.exe`;
}
core.info(`path: ${landoPath}`);
core.endGroup();
// if on windows remove docker for windows eg windows containers
if (inputs.os === 'Windows') {
await exec.exec('powershell', ['-Command', 'Stop-Service -Name docker -Force'], {ignoreReturnCode: true});
await exec.exec('powershell', ['-Command', 'Remove-Item -Path (Get-Command docker).Source -Force'], {ignoreReturnCode: true}); // eslint-disable-line max-len
await exec.exec('powershell', ['-Command', 'Get-Service -Name docker'], {ignoreReturnCode: true});
await exec.exec('powershell', ['-Command', 'docker info'], {ignoreReturnCode: true});
}
// reset version information, we do this to get the source of truth on what we've downloaded
fs.chmodSync(landoPath, '755');
const output = execSync(`${landoPath} version`, {
maxBuffer: 1024 * 1024 * 10,
encoding: 'utf-8',
env: {...process.env, LANDO_DEBUG: 0},
});
// parse output into version
version = output.split(' ').length === 2 ? output.split(' ')[1].trim() : output.split(' ')[0].trim();
const lmv = version.split('.')[0];
core.debug(`using lando version ${version}, major version ${lmv}`);
// move into the tool cache and compute path
const targetFile = inputs.os === 'Windows' ? 'lando.exe' : 'lando';
const toolDir = await tc.cacheFile(landoPath, targetFile, 'lando', version);
landoPath = path.join(toolDir, targetFile);
// set the path and outputs
core.addPath(toolDir);
core.setOutput('lando-path', landoPath);
core.debug(`lando installed at ${landoPath}`);
// start with either the config file or an empty object
let config = getConfigFile(inputs.configFile) || {};
// if we have config then loop through that and set
if (inputs.config) config = mergeConfig(config, inputs.config);
// if telemetry is off on v3 then add in more config
if (!inputs.telemetry && lmv === 'v3') {
config = mergeConfig(config, [['stats[0].report', false], 'stats[0].url=https://metrics.lando.dev']);
} else if (!inputs.telemetry && lmv === 'v4') {
// or if telemetry is off on v4 then add in more config
config = mergeConfig(config, [['core.telemetry', false]]);
}
// set config info
core.startGroup('Configuration information');
getObjectKeys(config).forEach(key => core.info(`${key}: ${get(config, key)}`));
core.endGroup();
// write the config file to disk
const gcf = getGCFPath(lmv);
await io.mkdirP(path.dirname(gcf));
fs.writeFileSync(gcf, yaml.dump(config));
// get version
await exec.exec('lando', ['version']);
// cat config
await exec.exec('cat', [gcf]);
// v3 needs a special thing for reporting error value comes from telemetry
if (lmv === 'v3') {
const reportFile = path.join(path.dirname(gcf), 'cache', 'report_errors');
await io.mkdirP(path.dirname(reportFile));
fs.writeFileSync(reportFile, inputs.telemetry ? 'true' : 'false');
await exec.exec('cat', [reportFile]);
}
// if setup is non-false then we want to try to run it if we can
if (lmv === 'v3' && getSetupCommand(inputs.setup) !== false) {
// print warning if setup command does not exist and leave
if (await exec.exec(landoPath, ['setup', '--help'], {ignoreReturnCode: true}) !== 0) {
core.warning('lando setup is only available in lando >=3.21 <4! Skipping!');
// if we get here then we should be G2G
} else {
const commands = parseSetupCommand(getSetupCommand(inputs.setup), landoPath);
const opts = {env: {...process.env, LANDO_DEBUG: core.isDebug() || inputs.debug}};
for (const command of commands) {
await exec.exec(command, [], opts);
}
}
}
// if debug then print the entire lando config
if (core.isDebug() || inputs.debug) await exec.exec(landoPath, ['config']);
// if core debugging or user debug is on then lets set "LANDO_DEBUG=1"
// @NOTE: we use core.exportVariable because we want any GHA workflow that uses @lando/setup-lando to not need
// to handle their own downstream lando debugging. Of course they can if they want since they migth want something
// more targeted or wide than LANDO_DEBUG=1 eg LANDO_DEBUG="*" or LANDO_DEBUG="lando/core*"
// we set this at the end so we can more selectively control the debug output above
if (core.isDebug() || inputs.debug) core.exportVariable('LANDO_DEBUG', 1);
// catch unexpected
} catch (error) {
core.setFailed(error.message);
}
};
// main logix
main();