@secureci/cli 0.0.0-stage → 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,278 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.executeNodeAction = executeNodeAction;
37
+ exports.inputEnvName = inputEnvName;
38
+ exports.runPreIf = runPreIf;
39
+ const child_process_1 = require("child_process");
40
+ const util_1 = require("util");
41
+ const path = __importStar(require("path"));
42
+ const logger_1 = require("../utils/logger");
43
+ const expression_1 = require("./expression");
44
+ const container_runtime_1 = require("./container-runtime");
45
+ const runner_env_1 = require("./runner-env");
46
+ const job_runtime_1 = require("./job-runtime");
47
+ const job_container_1 = require("./job-container");
48
+ const execFileAsync = (0, util_1.promisify)(child_process_1.execFile);
49
+ /**
50
+ * Execute a JavaScript action (`runs.using: node*`): `pre` (if any) and
51
+ * `main` now, with `post` queued on the job runtime to run at job end.
52
+ * Scripts run with the CLI's own Node binary, in the workspace, with the
53
+ * runner's default env, the caller's env, `INPUT_*`, `STATE_*` and the
54
+ * file-command variables. Note: GitHub runs every `pre` at job start;
55
+ * here it runs immediately before its own `main`.
56
+ */
57
+ async function executeNodeAction(action, actionPath, step, actionInputs, callerCtx, options, runtime, startTime, phase = 'main') {
58
+ if (phase === 'main') {
59
+ logger_1.logger.info(`Executing JavaScript action: ${action.name} (${action.runs.using})`);
60
+ }
61
+ const { main, pre, post } = action.runs;
62
+ if (!main) {
63
+ return {
64
+ step,
65
+ success: false,
66
+ error: 'No main script specified for JavaScript action',
67
+ duration: Date.now() - startTime
68
+ };
69
+ }
70
+ const actionDir = path.dirname(actionPath);
71
+ const inputEnv = {};
72
+ for (const [key, value] of Object.entries(actionInputs)) {
73
+ inputEnv[inputEnvName(key)] = value;
74
+ }
75
+ // In a job container, run the action there with the image's own `node`,
76
+ // from a copy of the action in the mounted exchange dir.
77
+ const container = runtime.container;
78
+ let containerActionDir;
79
+ if (container) {
80
+ if (container.node === undefined) {
81
+ const probe = await container.runtime.exec(container.id, {
82
+ command: `command -v node && printf '%s\\n' "$PATH"`
83
+ });
84
+ const [nodePath, basePath] = probe.stdout.trim().split('\n');
85
+ container.node =
86
+ probe.success && nodePath
87
+ ? { path: nodePath.trim(), basePath: (basePath || '').trim() }
88
+ : null;
89
+ }
90
+ if (!container.node) {
91
+ const error = `JavaScript action "${step.uses}" needs \`node\` in the job container image (${container.image}). Use an image that includes Node.js (e.g. --container-image node:20, a runner-images mapping, or ${job_container_1.DEFAULT_CONTAINER_IMAGE}), or run on the host with --container-runtime host.`;
92
+ logger_1.logger.error(error);
93
+ return { step, success: false, error, duration: Date.now() - startTime };
94
+ }
95
+ containerActionDir = (0, job_runtime_1.actionDirInContainer)(container, actionDir);
96
+ }
97
+ let state = {};
98
+ let output = '';
99
+ const limit = (0, job_runtime_1.effectiveTimeout)(runtime, options.containerTimeoutMs);
100
+ /** Run one script (pre/main/post) on the host or in the job container. */
101
+ const invokeScript = async (script, files, containerDir, stateEnv) => {
102
+ if (container?.node && containerActionDir && containerDir) {
103
+ // Exec node directly (no shell), so hyphenated INPUT_* names survive.
104
+ const scriptPath = path.posix.join(containerActionDir, script);
105
+ const result = await container.runtime.exec(container.id, {
106
+ args: [container.node.path, scriptPath],
107
+ workingDir: container_runtime_1.CONTAINER_WORKDIR,
108
+ env: {
109
+ ...(0, job_runtime_1.containerRunnerEnv)(runtime),
110
+ ...runtime.actionEnv,
111
+ ...callerCtx.env,
112
+ ...inputEnv,
113
+ ...stateEnv,
114
+ GITHUB_ACTION_PATH: containerActionDir,
115
+ PATH: [...runtime.path, container.node.basePath]
116
+ .filter(Boolean)
117
+ .join(':'),
118
+ ...(0, job_runtime_1.fileCommandEnv)(files, containerDir),
119
+ SECURECI_STEP: path.basename(files.dir)
120
+ },
121
+ timeoutMs: limit.ms
122
+ });
123
+ if (result.timedOut) {
124
+ await (0, job_runtime_1.killStepProcesses)(container, path.basename(files.dir));
125
+ }
126
+ return {
127
+ success: result.success,
128
+ stdout: result.stdout,
129
+ stderr: result.stderr,
130
+ error: result.success
131
+ ? undefined
132
+ : result.timedOut
133
+ ? limit.message
134
+ : `node exited with code ${result.exitCode}`
135
+ };
136
+ }
137
+ try {
138
+ const { stdout, stderr } = await execFileAsync(process.execPath, [path.join(actionDir, script)], {
139
+ cwd: process.cwd(),
140
+ maxBuffer: 64 * 1024 * 1024,
141
+ timeout: limit.ms,
142
+ killSignal: 'SIGKILL',
143
+ env: {
144
+ ...(0, runner_env_1.hostBaseEnv)(),
145
+ ...runtime.runnerEnv,
146
+ ...runtime.actionEnv,
147
+ ...callerCtx.env,
148
+ ...inputEnv,
149
+ ...stateEnv,
150
+ GITHUB_ACTION_PATH: actionDir,
151
+ PATH: (0, runner_env_1.prependPath)(runtime.path, callerCtx.env.PATH ?? process.env.PATH),
152
+ ...(0, job_runtime_1.fileCommandEnv)(files)
153
+ }
154
+ });
155
+ return { success: true, stdout, stderr };
156
+ }
157
+ catch (execError) {
158
+ const error = execError;
159
+ return {
160
+ success: false,
161
+ stdout: error.stdout || '',
162
+ stderr: error.stderr || '',
163
+ error: error.killed && limit.ms ? limit.message : error.message
164
+ };
165
+ }
166
+ };
167
+ const runScript = async (script) => {
168
+ const { files, containerDir } = container
169
+ ? (0, job_runtime_1.createContainerStepFiles)(container)
170
+ : { files: (0, runner_env_1.createStepFiles)(), containerDir: undefined };
171
+ const stateEnv = {};
172
+ for (const [key, value] of Object.entries(state)) {
173
+ stateEnv[`STATE_${key}`] = value;
174
+ }
175
+ try {
176
+ const result = await invokeScript(script, files, containerDir, stateEnv);
177
+ output += result.stdout;
178
+ const { outputs, state: savedState } = (0, job_runtime_1.finishStepOutput)(result.stdout, files, runtime);
179
+ state = { ...state, ...savedState };
180
+ if (result.success) {
181
+ if (result.stderr) {
182
+ logger_1.logger.warn(result.stderr.trim());
183
+ }
184
+ return { success: true, outputs };
185
+ }
186
+ logger_1.logger.error(`JavaScript action failed: ${result.error}`);
187
+ if (result.stderr) {
188
+ logger_1.logger.error(result.stderr.trim());
189
+ }
190
+ return { success: false, outputs, error: result.error };
191
+ }
192
+ finally {
193
+ (0, runner_env_1.cleanupStepFiles)(files);
194
+ }
195
+ };
196
+ // `pre` already ran at job start (see runPreSteps): continue from its state.
197
+ const preState = runtime.preState.get(step);
198
+ if (phase === 'pre' && pre) {
199
+ const preStep = { name: `Pre ${step.name || step.uses}` };
200
+ const preResult = await runScript(pre);
201
+ runtime.preState.set(step, state);
202
+ return {
203
+ step: preStep,
204
+ success: preResult.success,
205
+ error: preResult.error,
206
+ output,
207
+ duration: Date.now() - startTime
208
+ };
209
+ }
210
+ if (preState) {
211
+ state = { ...preState };
212
+ }
213
+ else if (pre && runPreIf(action, step, callerCtx)) {
214
+ const preResult = await runScript(pre);
215
+ if (!preResult.success) {
216
+ return {
217
+ step,
218
+ success: false,
219
+ error: preResult.error,
220
+ output,
221
+ duration: Date.now() - startTime
222
+ };
223
+ }
224
+ }
225
+ if (post) {
226
+ const postStep = {
227
+ name: `Post ${step.name || step.uses}`,
228
+ uses: step.uses
229
+ };
230
+ runtime.postSteps.push({
231
+ step: postStep,
232
+ condition: action.runs['post-if'] ?? 'always()',
233
+ run: async () => {
234
+ const postStartTime = Date.now();
235
+ output = '';
236
+ const postResult = await runScript(post);
237
+ return {
238
+ step: postStep,
239
+ success: postResult.success,
240
+ error: postResult.error,
241
+ output,
242
+ outputs: postResult.outputs,
243
+ duration: Date.now() - postStartTime
244
+ };
245
+ }
246
+ });
247
+ }
248
+ const mainResult = await runScript(main);
249
+ return {
250
+ step,
251
+ success: mainResult.success,
252
+ error: mainResult.error,
253
+ output,
254
+ outputs: mainResult.outputs,
255
+ duration: Date.now() - startTime
256
+ };
257
+ }
258
+ /**
259
+ * The env var an action input is exposed as. Matches the runner and
260
+ * `@actions/core`'s getInput(): spaces become `_`, hyphens are kept
261
+ * (`who-to-greet` -> `INPUT_WHO-TO-GREET`).
262
+ */
263
+ function inputEnvName(name) {
264
+ return `INPUT_${name.replace(/ /g, '_').toUpperCase()}`;
265
+ }
266
+ /**
267
+ * Whether a JavaScript action's `pre` should run (`pre-if`, default
268
+ * always()). Throws a step error if `pre-if` can't be evaluated.
269
+ */
270
+ function runPreIf(action, step, ctx) {
271
+ try {
272
+ return (0, expression_1.evaluateCondition)(action.runs['pre-if'] ?? 'always()', ctx);
273
+ }
274
+ catch (error) {
275
+ throw new Error((0, expression_1.ifEvaluationError)(`step 'Pre ${step.name || step.uses}'`, error));
276
+ }
277
+ }
278
+ //# sourceMappingURL=node-action.js.map
@@ -0,0 +1,303 @@
1
+ "use strict";
2
+ var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
3
+ if (k2 === undefined) k2 = k;
4
+ var desc = Object.getOwnPropertyDescriptor(m, k);
5
+ if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
6
+ desc = { enumerable: true, get: function() { return m[k]; } };
7
+ }
8
+ Object.defineProperty(o, k2, desc);
9
+ }) : (function(o, m, k, k2) {
10
+ if (k2 === undefined) k2 = k;
11
+ o[k2] = m[k];
12
+ }));
13
+ var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
14
+ Object.defineProperty(o, "default", { enumerable: true, value: v });
15
+ }) : function(o, v) {
16
+ o["default"] = v;
17
+ });
18
+ var __importStar = (this && this.__importStar) || (function () {
19
+ var ownKeys = function(o) {
20
+ ownKeys = Object.getOwnPropertyNames || function (o) {
21
+ var ar = [];
22
+ for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
23
+ return ar;
24
+ };
25
+ return ownKeys(o);
26
+ };
27
+ return function (mod) {
28
+ if (mod && mod.__esModule) return mod;
29
+ var result = {};
30
+ if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
31
+ __setModuleDefault(result, mod);
32
+ return result;
33
+ };
34
+ })();
35
+ Object.defineProperty(exports, "__esModule", { value: true });
36
+ exports.parseKeyValueFile = parseKeyValueFile;
37
+ exports.createStepFiles = createStepFiles;
38
+ exports.readStepFiles = readStepFiles;
39
+ exports.cleanupStepFiles = cleanupStepFiles;
40
+ exports.processWorkflowCommands = processWorkflowCommands;
41
+ exports.buildRunnerEnv = buildRunnerEnv;
42
+ exports.prependPath = prependPath;
43
+ exports.hostBaseEnv = hostBaseEnv;
44
+ const fs = __importStar(require("fs"));
45
+ const os = __importStar(require("os"));
46
+ const path = __importStar(require("path"));
47
+ /**
48
+ * Parse a runner "file command" file (`$GITHUB_OUTPUT`, `$GITHUB_ENV`,
49
+ * `$GITHUB_STATE`), supporting both the simple `name=value` form and the
50
+ * multiline heredoc form: `name<<DELIMITER` ... `DELIMITER`.
51
+ */
52
+ function parseKeyValueFile(filePath) {
53
+ const values = {};
54
+ let content;
55
+ try {
56
+ content = fs.readFileSync(filePath, 'utf-8');
57
+ }
58
+ catch {
59
+ return values;
60
+ }
61
+ const lines = content.split(/\r?\n/);
62
+ let i = 0;
63
+ while (i < lines.length) {
64
+ const line = lines[i];
65
+ if (!line) {
66
+ i++;
67
+ continue;
68
+ }
69
+ const heredocMatch = line.match(/^([^=<]+)<<(.+)$/);
70
+ if (heredocMatch) {
71
+ const [, name, delimiter] = heredocMatch;
72
+ const valueLines = [];
73
+ i++;
74
+ while (i < lines.length && lines[i] !== delimiter) {
75
+ valueLines.push(lines[i]);
76
+ i++;
77
+ }
78
+ values[name.trim()] = valueLines.join('\n');
79
+ i++; // Skip the closing delimiter line
80
+ continue;
81
+ }
82
+ const eqIndex = line.indexOf('=');
83
+ if (eqIndex > -1) {
84
+ values[line.slice(0, eqIndex).trim()] = line.slice(eqIndex + 1);
85
+ }
86
+ i++;
87
+ }
88
+ return values;
89
+ }
90
+ /**
91
+ * Create empty `GITHUB_OUTPUT` / `GITHUB_ENV` / `GITHUB_PATH` /
92
+ * `GITHUB_STATE` / `GITHUB_STEP_SUMMARY` files for one step, in `dir` (e.g.
93
+ * a container exchange dir) or a fresh temp directory.
94
+ */
95
+ function createStepFiles(dir) {
96
+ const baseDir = dir ?? fs.mkdtempSync(path.join(os.tmpdir(), 'secureci-step-'));
97
+ const files = {
98
+ dir: baseDir,
99
+ output: path.join(baseDir, 'output'),
100
+ env: path.join(baseDir, 'env'),
101
+ path: path.join(baseDir, 'path'),
102
+ state: path.join(baseDir, 'state'),
103
+ summary: path.join(baseDir, 'summary')
104
+ };
105
+ for (const file of [
106
+ files.output,
107
+ files.env,
108
+ files.path,
109
+ files.state,
110
+ files.summary
111
+ ]) {
112
+ fs.writeFileSync(file, '');
113
+ }
114
+ return files;
115
+ }
116
+ function readStepFiles(files) {
117
+ let pathLines = [];
118
+ try {
119
+ pathLines = fs
120
+ .readFileSync(files.path, 'utf-8')
121
+ .split(/\r?\n/)
122
+ .map((line) => line.trim())
123
+ .filter(Boolean);
124
+ }
125
+ catch {
126
+ // Missing file: no PATH additions
127
+ }
128
+ return {
129
+ outputs: parseKeyValueFile(files.output),
130
+ env: parseKeyValueFile(files.env),
131
+ path: pathLines,
132
+ state: parseKeyValueFile(files.state),
133
+ summary: (() => {
134
+ try {
135
+ return fs.readFileSync(files.summary, 'utf-8');
136
+ }
137
+ catch {
138
+ return '';
139
+ }
140
+ })()
141
+ };
142
+ }
143
+ function cleanupStepFiles(files) {
144
+ fs.rmSync(files.dir, { recursive: true, force: true });
145
+ }
146
+ function unescapeData(value) {
147
+ return value
148
+ .replace(/%0D/gi, '\r')
149
+ .replace(/%0A/gi, '\n')
150
+ .replace(/%25/g, '%');
151
+ }
152
+ function unescapeProperty(value) {
153
+ return unescapeData(value).replace(/%3A/gi, ':').replace(/%2C/gi, ',');
154
+ }
155
+ function parseProperties(raw) {
156
+ const props = {};
157
+ for (const pair of (raw || '').split(',')) {
158
+ const eq = pair.indexOf('=');
159
+ if (eq > 0) {
160
+ props[pair.slice(0, eq).trim()] = unescapeProperty(pair.slice(eq + 1));
161
+ }
162
+ }
163
+ return props;
164
+ }
165
+ /**
166
+ * Interpret GitHub Actions workflow commands (`::add-mask::`,
167
+ * `::set-output`, `::save-state`, `::warning::`, `::group::`, ...) written
168
+ * to a step's stdout, in both the `::cmd::` and legacy `##[cmd]` syntax.
169
+ * Problem matchers and `::echo::` have no local effect and are hidden;
170
+ * nothing is interpreted between `::stop-commands::<token>` and
171
+ * `::<token>::`. Unknown commands are left in the text.
172
+ * https://docs.github.com/en/actions/using-workflows/workflow-commands-for-github-actions
173
+ */
174
+ function processWorkflowCommands(stdout) {
175
+ const result = {
176
+ text: '',
177
+ masks: [],
178
+ outputs: {},
179
+ state: {},
180
+ annotations: []
181
+ };
182
+ const kept = [];
183
+ let stopToken;
184
+ for (const line of stdout.split(/\r?\n/)) {
185
+ if (stopToken !== undefined) {
186
+ if (line === `::${stopToken}::`)
187
+ stopToken = undefined;
188
+ else
189
+ kept.push(line);
190
+ continue;
191
+ }
192
+ const match = line.match(/^::([a-zA-Z-]+)(?:\s+([^:]*))?::(.*)$/) ??
193
+ line.match(/^##\[([a-zA-Z-]+)(?:\s+([^\]]*))?\](.*)$/);
194
+ if (!match) {
195
+ kept.push(line);
196
+ continue;
197
+ }
198
+ const [, command, rawProps, rawData] = match;
199
+ const data = unescapeData(rawData);
200
+ const props = parseProperties(rawProps);
201
+ switch (command) {
202
+ case 'add-mask':
203
+ if (data)
204
+ result.masks.push(data);
205
+ break;
206
+ case 'set-output':
207
+ if (props.name)
208
+ result.outputs[props.name] = data;
209
+ break;
210
+ case 'save-state':
211
+ if (props.name)
212
+ result.state[props.name] = data;
213
+ break;
214
+ case 'warning':
215
+ case 'error':
216
+ case 'notice':
217
+ case 'debug':
218
+ case 'group':
219
+ result.annotations.push({ level: command, message: data });
220
+ break;
221
+ case 'stop-commands':
222
+ if (data)
223
+ stopToken = data;
224
+ break;
225
+ case 'endgroup':
226
+ case 'add-matcher':
227
+ case 'remove-matcher':
228
+ case 'echo':
229
+ break;
230
+ default:
231
+ kept.push(line);
232
+ }
233
+ }
234
+ result.text = kept.join('\n');
235
+ return result;
236
+ }
237
+ /** A context value as an env var string ('' when unset). */
238
+ function text(value) {
239
+ return value === undefined || value === null ? '' : String(value);
240
+ }
241
+ function runnerOs() {
242
+ switch (process.platform) {
243
+ case 'darwin':
244
+ return 'macOS';
245
+ case 'win32':
246
+ return 'Windows';
247
+ default:
248
+ return 'Linux';
249
+ }
250
+ }
251
+ /**
252
+ * The default environment variables GitHub's runner exports to every step,
253
+ * derived from the (local, best-effort) `github` context.
254
+ * https://docs.github.com/en/actions/learn-github-actions/variables#default-environment-variables
255
+ */
256
+ function buildRunnerEnv(github, options) {
257
+ const env = {
258
+ CI: 'true',
259
+ GITHUB_ACTIONS: 'true',
260
+ GITHUB_WORKFLOW: text(github.workflow),
261
+ GITHUB_JOB: text(github.job),
262
+ GITHUB_SHA: text(github.sha),
263
+ GITHUB_REF: text(github.ref),
264
+ GITHUB_REF_NAME: text(github.ref_name),
265
+ GITHUB_EVENT_NAME: text(github.event_name),
266
+ GITHUB_ACTOR: text(github.actor),
267
+ GITHUB_REPOSITORY: text(github.repository),
268
+ GITHUB_REPOSITORY_OWNER: text(github.repository_owner),
269
+ GITHUB_RUN_ID: text(github.run_id),
270
+ GITHUB_RUN_NUMBER: text(github.run_number),
271
+ GITHUB_RUN_ATTEMPT: text(github.run_attempt),
272
+ GITHUB_SERVER_URL: text(github.server_url),
273
+ GITHUB_API_URL: text(github.api_url),
274
+ GITHUB_WORKSPACE: options.workspace,
275
+ GITHUB_EVENT_PATH: options.eventPath,
276
+ RUNNER_OS: runnerOs(),
277
+ RUNNER_TEMP: options.tempDir,
278
+ RUNNER_TOOL_CACHE: options.toolCache
279
+ };
280
+ return env;
281
+ }
282
+ /** Prepend `$GITHUB_PATH` additions (most recent first) to a PATH value. */
283
+ function prependPath(additions, basePath) {
284
+ return [...additions, ...(basePath ? [basePath] : [])].join(path.delimiter);
285
+ }
286
+ /**
287
+ * Host variables that `run:` steps and actions on the host still get: what
288
+ * tools need to work (PATH, HOME, locale, temp dirs, proxy and CA settings,
289
+ * the container client's config, and Windows system variables). Everything
290
+ * else in the user's shell (tokens, cloud credentials...) stays out; pass
291
+ * what a workflow needs with `--env`.
292
+ */
293
+ const HOST_ENV_ALLOWLIST = /^(PATH|HOME|USER|LOGNAME|SHELL|TERM|TZ|LANG|LANGUAGE|LC_[A-Z_]+|TMPDIR|TEMP|TMP|(HTTPS?|FTP|ALL|NO)_PROXY|SSL_CERT_(FILE|DIR)|NODE_EXTRA_CA_CERTS|REQUESTS_CA_BUNDLE|CURL_CA_BUNDLE|DOCKER_(HOST|CONTEXT|CONFIG|CERT_PATH|TLS_VERIFY)|XDG_RUNTIME_DIR|SYSTEMROOT|SYSTEMDRIVE|WINDIR|COMSPEC|PATHEXT|USERPROFILE|APPDATA|LOCALAPPDATA|PROGRAMDATA|PROGRAMFILES(\(X86\))?|COMMONPROGRAMFILES(\(X86\))?)$/i;
294
+ /** The allowlisted part of the host environment (see HOST_ENV_ALLOWLIST). */
295
+ function hostBaseEnv(env = process.env) {
296
+ const base = {};
297
+ for (const [key, value] of Object.entries(env)) {
298
+ if (value !== undefined && HOST_ENV_ALLOWLIST.test(key))
299
+ base[key] = value;
300
+ }
301
+ return base;
302
+ }
303
+ //# sourceMappingURL=runner-env.js.map
@@ -0,0 +1,61 @@
1
+ "use strict";
2
+ /**
3
+ * `shell:` handling for `run:` steps, mirroring the GitHub runner: the
4
+ * script is written to a file and passed as `{0}` to a command template.
5
+ * https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsshell
6
+ */
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ exports.resolveShell = resolveShell;
9
+ exports.shellCommandLine = shellCommandLine;
10
+ exports.splitArgs = splitArgs;
11
+ const BASH = 'bash --noprofile --norc -eo pipefail {0}';
12
+ const SH = 'sh -e {0}';
13
+ const KNOWN_SHELLS = {
14
+ bash: { template: BASH, extension: '.sh' },
15
+ sh: { template: SH, extension: '.sh' },
16
+ python: { template: 'python {0}', extension: '.py' },
17
+ pwsh: { template: `pwsh -command ". '{0}'"`, extension: '.ps1' },
18
+ powershell: {
19
+ template: `powershell -command ". '{0}'"`,
20
+ extension: '.ps1'
21
+ },
22
+ cmd: {
23
+ template: '%ComSpec% /D /E:ON /V:OFF /S /C "CALL "{0}""',
24
+ extension: '.cmd'
25
+ }
26
+ };
27
+ /**
28
+ * Resolve a step's `shell:` (after `defaults.run.shell`) to a command
29
+ * template. With no shell, GitHub uses bash with `-eo pipefail`, or
30
+ * `sh -e` where bash isn't installed.
31
+ */
32
+ function resolveShell(shell, options) {
33
+ if (!shell) {
34
+ return options.bashAvailable ? KNOWN_SHELLS.bash : KNOWN_SHELLS.sh;
35
+ }
36
+ const known = KNOWN_SHELLS[shell];
37
+ if (known)
38
+ return known;
39
+ if (shell.includes('{0}'))
40
+ return { template: shell, extension: '' };
41
+ return { template: `${shell} {0}`, extension: '' };
42
+ }
43
+ /**
44
+ * Build the command line for a template and script path. `{0}` is replaced
45
+ * with a single-quoted path, unless the template already quotes it.
46
+ */
47
+ function shellCommandLine(template, scriptPath) {
48
+ if (/'\{0\}'|"\{0\}"/.test(template)) {
49
+ return template.replace('{0}', scriptPath);
50
+ }
51
+ const quoted = `'${scriptPath.replace(/'/g, "'\\''")}'`;
52
+ return template.replace('{0}', quoted);
53
+ }
54
+ /**
55
+ * Split an argument string (`with.args`, `services.<id>.options`) into
56
+ * argv, honouring simple quoting.
57
+ */
58
+ function splitArgs(args) {
59
+ return (args.match(/"[^"]*"|'[^']*'|\S+/g) || []).map((arg) => /^(["']).*\1$/.test(arg) ? arg.slice(1, -1) : arg);
60
+ }
61
+ //# sourceMappingURL=shell.js.map