@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.
- package/LICENSE +21 -0
- package/README.md +201 -2
- package/dist/cli.js +545 -0
- package/dist/config.js +190 -0
- package/dist/index.js +23 -0
- package/dist/init.js +45 -0
- package/dist/inventory.js +212 -0
- package/dist/parser/pipeline-contract-parser.js +131 -0
- package/dist/parser/pipeline-contract-validator.js +448 -0
- package/dist/parser/validator.js +232 -0
- package/dist/parser/workflow-parser.js +122 -0
- package/dist/report.js +75 -0
- package/dist/runner/action-fetcher.js +431 -0
- package/dist/runner/actions.js +495 -0
- package/dist/runner/builtin-actions.js +484 -0
- package/dist/runner/checkout.js +142 -0
- package/dist/runner/container-runtime.js +374 -0
- package/dist/runner/docker-action.js +179 -0
- package/dist/runner/dry-run.js +142 -0
- package/dist/runner/executor.js +554 -0
- package/dist/runner/expression.js +676 -0
- package/dist/runner/github-context.js +235 -0
- package/dist/runner/hash-files.js +123 -0
- package/dist/runner/job-container.js +322 -0
- package/dist/runner/job-plan.js +138 -0
- package/dist/runner/job-runtime.js +338 -0
- package/dist/runner/node-action.js +278 -0
- package/dist/runner/runner-env.js +303 -0
- package/dist/runner/shell.js +61 -0
- package/dist/runner/steps.js +250 -0
- package/dist/types/index.js +10 -0
- package/dist/utils/key-value.js +33 -0
- package/dist/utils/logger.js +87 -0
- package/dist/utils/repository.js +51 -0
- package/package.json +77 -4
|
@@ -0,0 +1,138 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.sameValue = sameValue;
|
|
4
|
+
exports.matrixLabel = matrixLabel;
|
|
5
|
+
exports.isTruthySetting = isTruthySetting;
|
|
6
|
+
exports.numberSetting = numberSetting;
|
|
7
|
+
exports.matrixValue = matrixValue;
|
|
8
|
+
exports.expandMatrix = expandMatrix;
|
|
9
|
+
exports.topologicalJobOrder = topologicalJobOrder;
|
|
10
|
+
const logger_1 = require("../utils/logger");
|
|
11
|
+
const expression_1 = require("./expression");
|
|
12
|
+
/** Deep equality for matrix values (JSON-like data). */
|
|
13
|
+
function sameValue(a, b) {
|
|
14
|
+
if (a === b)
|
|
15
|
+
return true;
|
|
16
|
+
if (!a || !b || typeof a !== 'object' || typeof b !== 'object')
|
|
17
|
+
return false;
|
|
18
|
+
if (Array.isArray(a) !== Array.isArray(b))
|
|
19
|
+
return false;
|
|
20
|
+
const keysA = Object.keys(a);
|
|
21
|
+
const keysB = Object.keys(b);
|
|
22
|
+
return (keysA.length === keysB.length &&
|
|
23
|
+
keysA.every((key) => sameValue(a[key], b[key])));
|
|
24
|
+
}
|
|
25
|
+
/** `key: value, ...` for a matrix combination's job label. */
|
|
26
|
+
function matrixLabel(values) {
|
|
27
|
+
return Object.entries(values)
|
|
28
|
+
.map(([k, v]) => `${k}: ${v && typeof v === 'object' ? JSON.stringify(v) : v}`)
|
|
29
|
+
.join(', ');
|
|
30
|
+
}
|
|
31
|
+
/** A boolean setting that may be an expression (`continue-on-error`). */
|
|
32
|
+
function isTruthySetting(value, ctx) {
|
|
33
|
+
const resolved = matrixValue(value, ctx);
|
|
34
|
+
return resolved === true || resolved === 'true';
|
|
35
|
+
}
|
|
36
|
+
/** A positive number setting (e.g. timeout-minutes), possibly an expression. */
|
|
37
|
+
function numberSetting(value, ctx) {
|
|
38
|
+
if (value === undefined || value === null || value === '')
|
|
39
|
+
return undefined;
|
|
40
|
+
const n = Number(matrixValue(value, ctx));
|
|
41
|
+
return Number.isFinite(n) && n > 0 ? n : undefined;
|
|
42
|
+
}
|
|
43
|
+
/** A `${{ }}` string resolved to its value (e.g. via fromJSON), else as is. */
|
|
44
|
+
function matrixValue(value, ctx) {
|
|
45
|
+
if (typeof value !== 'string' || !/^\s*\$\{\{[\s\S]*\}\}\s*$/.test(value)) {
|
|
46
|
+
return value;
|
|
47
|
+
}
|
|
48
|
+
return (0, expression_1.resolveExpressionValue)(value, ctx);
|
|
49
|
+
}
|
|
50
|
+
/**
|
|
51
|
+
* Expand a job's `strategy.matrix` into its combinations, as GitHub does:
|
|
52
|
+
* the cartesian product of the axes, minus combinations matching an
|
|
53
|
+
* `exclude` entry (on all of its keys), then each `include` entry is
|
|
54
|
+
* merged into every combination whose original axis values it doesn't
|
|
55
|
+
* change (values added by an earlier include may be overwritten), or
|
|
56
|
+
* added as a new combination if it fits none. The matrix, or any axis,
|
|
57
|
+
* may be an expression such as `${{ fromJSON(needs.x.outputs.m) }}`.
|
|
58
|
+
*/
|
|
59
|
+
function expandMatrix(rawMatrix, ctx) {
|
|
60
|
+
const matrix = matrixValue(rawMatrix, ctx);
|
|
61
|
+
if (!matrix || typeof matrix !== 'object' || Array.isArray(matrix)) {
|
|
62
|
+
return [{}];
|
|
63
|
+
}
|
|
64
|
+
const entries = (key) => {
|
|
65
|
+
const list = matrixValue(matrix[key], ctx);
|
|
66
|
+
return Array.isArray(list)
|
|
67
|
+
? list.filter((e) => !!e && typeof e === 'object')
|
|
68
|
+
: [];
|
|
69
|
+
};
|
|
70
|
+
const include = entries('include');
|
|
71
|
+
const exclude = entries('exclude');
|
|
72
|
+
const axes = Object.entries(matrix)
|
|
73
|
+
.filter(([key]) => key !== 'include' && key !== 'exclude')
|
|
74
|
+
.map(([key, raw]) => {
|
|
75
|
+
const values = matrixValue(raw, ctx);
|
|
76
|
+
return [key, Array.isArray(values) ? values : [values]];
|
|
77
|
+
});
|
|
78
|
+
const axisKeys = new Set(axes.map(([key]) => key));
|
|
79
|
+
let combinations = axes.length ? [{}] : [];
|
|
80
|
+
for (const [key, values] of axes) {
|
|
81
|
+
combinations = combinations.flatMap((combo) => values.map((value) => ({ ...combo, [key]: value })));
|
|
82
|
+
}
|
|
83
|
+
combinations = combinations.filter((combo) => !exclude.some((entry) => Object.entries(entry).every(([key, value]) => sameValue(combo[key], value))));
|
|
84
|
+
const added = [];
|
|
85
|
+
for (const entry of include) {
|
|
86
|
+
let merged = false;
|
|
87
|
+
for (const combo of combinations) {
|
|
88
|
+
const fits = Object.entries(entry).every(([key, value]) => !axisKeys.has(key) || sameValue(combo[key], value));
|
|
89
|
+
if (fits) {
|
|
90
|
+
Object.assign(combo, entry);
|
|
91
|
+
merged = true;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
if (!merged)
|
|
95
|
+
added.push({ ...entry });
|
|
96
|
+
}
|
|
97
|
+
combinations.push(...added);
|
|
98
|
+
return combinations.length ? combinations : [{}];
|
|
99
|
+
}
|
|
100
|
+
/**
|
|
101
|
+
* Order job IDs so dependencies (via `needs`) run before dependents, using
|
|
102
|
+
* Kahn's algorithm. Falls back to declaration order (with a warning) if a
|
|
103
|
+
* cycle is detected, since that indicates an invalid workflow.
|
|
104
|
+
*/
|
|
105
|
+
function topologicalJobOrder(jobs) {
|
|
106
|
+
const jobIds = Object.keys(jobs);
|
|
107
|
+
const needsOf = (id) => {
|
|
108
|
+
const needs = jobs[id].needs;
|
|
109
|
+
if (!needs)
|
|
110
|
+
return [];
|
|
111
|
+
return Array.isArray(needs) ? needs : [needs];
|
|
112
|
+
};
|
|
113
|
+
const inDegree = new Map();
|
|
114
|
+
for (const id of jobIds) {
|
|
115
|
+
inDegree.set(id, needsOf(id).filter((dep) => jobIds.includes(dep)).length);
|
|
116
|
+
}
|
|
117
|
+
const queue = jobIds.filter((id) => inDegree.get(id) === 0);
|
|
118
|
+
const ordered = [];
|
|
119
|
+
while (queue.length > 0) {
|
|
120
|
+
const id = queue.shift();
|
|
121
|
+
ordered.push(id);
|
|
122
|
+
for (const other of jobIds) {
|
|
123
|
+
if (needsOf(other).includes(id)) {
|
|
124
|
+
const remaining = (inDegree.get(other) || 0) - 1;
|
|
125
|
+
inDegree.set(other, remaining);
|
|
126
|
+
if (remaining === 0) {
|
|
127
|
+
queue.push(other);
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
if (ordered.length !== jobIds.length) {
|
|
133
|
+
logger_1.logger.warn('Detected a cycle in job "needs" dependencies; falling back to declaration order.');
|
|
134
|
+
return jobIds;
|
|
135
|
+
}
|
|
136
|
+
return ordered;
|
|
137
|
+
}
|
|
138
|
+
//# sourceMappingURL=job-plan.js.map
|
|
@@ -0,0 +1,338 @@
|
|
|
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.createContainerStepFiles = createContainerStepFiles;
|
|
37
|
+
exports.killStepProcesses = killStepProcesses;
|
|
38
|
+
exports.containerPathPrefix = containerPathPrefix;
|
|
39
|
+
exports.createRunDir = createRunDir;
|
|
40
|
+
exports.runnerContext = runnerContext;
|
|
41
|
+
exports.hostHasBash = hostHasBash;
|
|
42
|
+
exports.createJobRuntime = createJobRuntime;
|
|
43
|
+
exports.runPostSteps = runPostSteps;
|
|
44
|
+
exports.fileCommandEnv = fileCommandEnv;
|
|
45
|
+
exports.containerRunnerEnv = containerRunnerEnv;
|
|
46
|
+
exports.finishStepOutput = finishStepOutput;
|
|
47
|
+
exports.stepActionEnv = stepActionEnv;
|
|
48
|
+
exports.actionDirInContainer = actionDirInContainer;
|
|
49
|
+
exports.effectiveTimeout = effectiveTimeout;
|
|
50
|
+
exports.stepDisplayName = stepDisplayName;
|
|
51
|
+
const crypto = __importStar(require("crypto"));
|
|
52
|
+
const fs = __importStar(require("fs"));
|
|
53
|
+
const os = __importStar(require("os"));
|
|
54
|
+
const path = __importStar(require("path"));
|
|
55
|
+
const logger_1 = require("../utils/logger");
|
|
56
|
+
const child_process_1 = require("child_process");
|
|
57
|
+
const expression_1 = require("./expression");
|
|
58
|
+
const container_runtime_1 = require("./container-runtime");
|
|
59
|
+
const runner_env_1 = require("./runner-env");
|
|
60
|
+
/** Step files inside the job container's exchange dir, writable from it. */
|
|
61
|
+
function createContainerStepFiles(container) {
|
|
62
|
+
const dir = fs.mkdtempSync(path.join(container.exchangeDir, 'step-'));
|
|
63
|
+
const files = (0, runner_env_1.createStepFiles)(dir);
|
|
64
|
+
fs.chmodSync(dir, 0o777);
|
|
65
|
+
for (const file of [
|
|
66
|
+
files.output,
|
|
67
|
+
files.env,
|
|
68
|
+
files.path,
|
|
69
|
+
files.state,
|
|
70
|
+
files.summary
|
|
71
|
+
]) {
|
|
72
|
+
fs.chmodSync(file, 0o666);
|
|
73
|
+
}
|
|
74
|
+
return {
|
|
75
|
+
files,
|
|
76
|
+
containerDir: `${container_runtime_1.CONTAINER_EXCHANGE_DIR}/${path.basename(dir)}`
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
/**
|
|
80
|
+
* Kill what a timed-out step left running in the job container: every
|
|
81
|
+
* process whose environment carries the step's SECURECI_STEP marker
|
|
82
|
+
* (children inherit it). Killing the container client alone leaves the
|
|
83
|
+
* command running until the container is removed.
|
|
84
|
+
*/
|
|
85
|
+
async function killStepProcesses(container, marker) {
|
|
86
|
+
await container.runtime.exec(container.id, {
|
|
87
|
+
command: `for p in /proc/[0-9]*; do tr '\\0' '\\n' < "$p/environ" 2>/dev/null | grep -qx 'SECURECI_STEP=${marker}' && kill -9 "\${p#/proc/}" 2>/dev/null; done; true`
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
/** `export PATH=...` prefix applying `$GITHUB_PATH` additions in a shell. */
|
|
91
|
+
function containerPathPrefix(runtime) {
|
|
92
|
+
return runtime.path.length
|
|
93
|
+
? `export PATH="${runtime.path.join(':')}:$PATH"\n`
|
|
94
|
+
: '';
|
|
95
|
+
}
|
|
96
|
+
/**
|
|
97
|
+
* Create the per-run scratch directory holding the event payload
|
|
98
|
+
* (`$GITHUB_EVENT_PATH`) and `$RUNNER_TEMP`.
|
|
99
|
+
*/
|
|
100
|
+
function createRunDir() {
|
|
101
|
+
const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'secureci-run-'));
|
|
102
|
+
fs.writeFileSync(path.join(dir, 'event.json'), '{}');
|
|
103
|
+
fs.mkdirSync(path.join(dir, 'tmp'));
|
|
104
|
+
return dir;
|
|
105
|
+
}
|
|
106
|
+
/** The `runner` context, derived from the runner env. */
|
|
107
|
+
function runnerContext(runtime) {
|
|
108
|
+
return {
|
|
109
|
+
name: 'secureci',
|
|
110
|
+
os: runtime.runnerEnv.RUNNER_OS,
|
|
111
|
+
arch: process.arch === 'arm64' ? 'ARM64' : 'X64',
|
|
112
|
+
temp: runtime.runnerEnv.RUNNER_TEMP,
|
|
113
|
+
tool_cache: runtime.runnerEnv.RUNNER_TOOL_CACHE
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
let hostBash;
|
|
117
|
+
/** Whether bash is available on the host (decides the default shell). */
|
|
118
|
+
function hostHasBash() {
|
|
119
|
+
if (hostBash === undefined) {
|
|
120
|
+
try {
|
|
121
|
+
(0, child_process_1.execSync)('command -v bash', { stdio: 'ignore' });
|
|
122
|
+
hostBash = true;
|
|
123
|
+
}
|
|
124
|
+
catch {
|
|
125
|
+
hostBash = false;
|
|
126
|
+
}
|
|
127
|
+
}
|
|
128
|
+
return hostBash;
|
|
129
|
+
}
|
|
130
|
+
function createJobRuntime(github, runDir, defaults = {}) {
|
|
131
|
+
const homeDir = process.env.HOME || process.env.USERPROFILE || os.tmpdir();
|
|
132
|
+
const eventPath = path.join(runDir, 'event.json');
|
|
133
|
+
return {
|
|
134
|
+
runnerEnv: (0, runner_env_1.buildRunnerEnv)(github, {
|
|
135
|
+
workspace: process.cwd(),
|
|
136
|
+
eventPath,
|
|
137
|
+
tempDir: path.join(runDir, 'tmp'),
|
|
138
|
+
toolCache: path.join(homeDir, '.secureci', 'toolcache')
|
|
139
|
+
}),
|
|
140
|
+
eventPath,
|
|
141
|
+
env: {},
|
|
142
|
+
path: [],
|
|
143
|
+
postSteps: [],
|
|
144
|
+
defaults,
|
|
145
|
+
summary: '',
|
|
146
|
+
actionEnv: {},
|
|
147
|
+
actionNames: new Map(),
|
|
148
|
+
preState: new Map(),
|
|
149
|
+
actionPaths: new Map()
|
|
150
|
+
};
|
|
151
|
+
}
|
|
152
|
+
/**
|
|
153
|
+
* Run the job's queued `post:` steps in reverse registration order, each
|
|
154
|
+
* gated on its `post-if` condition (default `always()`).
|
|
155
|
+
*/
|
|
156
|
+
async function runPostSteps(runtime, ctx, stepResults) {
|
|
157
|
+
for (const post of [...runtime.postSteps].reverse()) {
|
|
158
|
+
let run;
|
|
159
|
+
try {
|
|
160
|
+
run = (0, expression_1.evaluateCondition)(post.condition, ctx);
|
|
161
|
+
}
|
|
162
|
+
catch (error) {
|
|
163
|
+
const message = (0, expression_1.ifEvaluationError)(`step '${post.step.name}'`, error);
|
|
164
|
+
logger_1.logger.error(message);
|
|
165
|
+
stepResults.push({
|
|
166
|
+
step: post.step,
|
|
167
|
+
success: false,
|
|
168
|
+
error: message,
|
|
169
|
+
duration: 0
|
|
170
|
+
});
|
|
171
|
+
ctx.jobFailed = true;
|
|
172
|
+
continue;
|
|
173
|
+
}
|
|
174
|
+
if (!run) {
|
|
175
|
+
logger_1.logger.info(`Skipping step: ${post.step.name ?? 'post'} (condition not met)`);
|
|
176
|
+
continue;
|
|
177
|
+
}
|
|
178
|
+
logger_1.logger.info(`Running: ${post.step.name}`);
|
|
179
|
+
const result = await post.run();
|
|
180
|
+
stepResults.push(result);
|
|
181
|
+
if (!result.success) {
|
|
182
|
+
ctx.jobFailed = true;
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
runtime.postSteps = [];
|
|
186
|
+
}
|
|
187
|
+
/** `GITHUB_OUTPUT` / `GITHUB_ENV` / ... pointing at a step's files. */
|
|
188
|
+
function fileCommandEnv(files, containerDir) {
|
|
189
|
+
const at = (file) => containerDir ? `${containerDir}/${path.basename(file)}` : file;
|
|
190
|
+
return {
|
|
191
|
+
GITHUB_OUTPUT: at(files.output),
|
|
192
|
+
GITHUB_ENV: at(files.env),
|
|
193
|
+
GITHUB_PATH: at(files.path),
|
|
194
|
+
GITHUB_STATE: at(files.state),
|
|
195
|
+
GITHUB_STEP_SUMMARY: at(files.summary)
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
/**
|
|
199
|
+
* The runner env as seen from inside a container: workspace and event
|
|
200
|
+
* payload at their mounted paths (the event payload is copied into the
|
|
201
|
+
* exchange dir's root).
|
|
202
|
+
*/
|
|
203
|
+
function containerRunnerEnv(runtime) {
|
|
204
|
+
return {
|
|
205
|
+
...runtime.runnerEnv,
|
|
206
|
+
GITHUB_WORKSPACE: container_runtime_1.CONTAINER_WORKDIR,
|
|
207
|
+
GITHUB_EVENT_PATH: `${container_runtime_1.CONTAINER_EXCHANGE_DIR}/event.json`,
|
|
208
|
+
RUNNER_TEMP: '/tmp',
|
|
209
|
+
RUNNER_TOOL_CACHE: '/opt/hostedtoolcache'
|
|
210
|
+
};
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Handle a finished step's stdout and file commands: register
|
|
214
|
+
* `::add-mask::` values before anything is logged, log the remaining
|
|
215
|
+
* output, and fold `$GITHUB_ENV` / `$GITHUB_PATH` changes into the job
|
|
216
|
+
* runtime. Returns the step's outputs and saved state.
|
|
217
|
+
*/
|
|
218
|
+
function finishStepOutput(stdout, files, runtime) {
|
|
219
|
+
const commands = (0, runner_env_1.processWorkflowCommands)(stdout || '');
|
|
220
|
+
commands.masks.forEach((mask, i) => logger_1.logger.registerSecrets({ [`mask-${i}`]: mask }));
|
|
221
|
+
for (const annotation of commands.annotations) {
|
|
222
|
+
if (annotation.level === 'warning')
|
|
223
|
+
logger_1.logger.warn(annotation.message);
|
|
224
|
+
else if (annotation.level === 'error')
|
|
225
|
+
logger_1.logger.error(annotation.message);
|
|
226
|
+
else if (annotation.level === 'debug')
|
|
227
|
+
logger_1.logger.debug(annotation.message);
|
|
228
|
+
else if (annotation.level === 'group')
|
|
229
|
+
logger_1.logger.info(`▸ ${annotation.message}`);
|
|
230
|
+
else
|
|
231
|
+
logger_1.logger.info(annotation.message);
|
|
232
|
+
}
|
|
233
|
+
const text = commands.text.trim();
|
|
234
|
+
if (text) {
|
|
235
|
+
logger_1.logger.info(text);
|
|
236
|
+
}
|
|
237
|
+
const fileResults = (0, runner_env_1.readStepFiles)(files);
|
|
238
|
+
runtime.summary += fileResults.summary;
|
|
239
|
+
Object.assign(runtime.env, fileResults.env);
|
|
240
|
+
runtime.path.unshift(...[...fileResults.path].reverse());
|
|
241
|
+
return {
|
|
242
|
+
outputs: { ...fileResults.outputs, ...commands.outputs },
|
|
243
|
+
state: { ...fileResults.state, ...commands.state }
|
|
244
|
+
};
|
|
245
|
+
}
|
|
246
|
+
/**
|
|
247
|
+
* The runner's per-step GITHUB_ACTION variables: GITHUB_ACTION is the
|
|
248
|
+
* step's id, else `__run` / `__owner_repo` (numbered `_2`, `_3`... when
|
|
249
|
+
* repeated); a remote `uses:` sets GITHUB_ACTION_REPOSITORY / _REF. Inside
|
|
250
|
+
* a composite action, steps also get its GITHUB_ACTION_PATH, and run steps
|
|
251
|
+
* its repository and ref.
|
|
252
|
+
*/
|
|
253
|
+
function stepActionEnv(runtime, step, parent) {
|
|
254
|
+
let name = step.id;
|
|
255
|
+
if (!name) {
|
|
256
|
+
const base = step.uses
|
|
257
|
+
? `__${step.uses
|
|
258
|
+
.replace(/@.*$/, '')
|
|
259
|
+
.replace(/[^A-Za-z0-9]+/g, '_')
|
|
260
|
+
.replace(/^_+|_+$/g, '')}`
|
|
261
|
+
: '__run';
|
|
262
|
+
const count = (runtime.actionNames.get(base) ?? 0) + 1;
|
|
263
|
+
runtime.actionNames.set(base, count);
|
|
264
|
+
name = count === 1 ? base : `${base}_${count}`;
|
|
265
|
+
}
|
|
266
|
+
const remote = step.uses?.match(/^([^./][^/@]*\/[^/@]+)(?:\/[^@]*)?@(.+)$/);
|
|
267
|
+
const env = {
|
|
268
|
+
GITHUB_ACTION: name,
|
|
269
|
+
GITHUB_ACTION_REPOSITORY: remote
|
|
270
|
+
? remote[1]
|
|
271
|
+
: step.uses
|
|
272
|
+
? ''
|
|
273
|
+
: (parent?.repository ?? ''),
|
|
274
|
+
GITHUB_ACTION_REF: remote ? remote[2] : step.uses ? '' : (parent?.ref ?? '')
|
|
275
|
+
};
|
|
276
|
+
if (parent)
|
|
277
|
+
env.GITHUB_ACTION_PATH = parent.path;
|
|
278
|
+
return env;
|
|
279
|
+
}
|
|
280
|
+
/**
|
|
281
|
+
* An action directory's path inside the job container: copied once into
|
|
282
|
+
* the mounted exchange dir (the action cache isn't mounted).
|
|
283
|
+
*/
|
|
284
|
+
function actionDirInContainer(container, actionDir) {
|
|
285
|
+
let containerDir = container.actionDirs.get(actionDir);
|
|
286
|
+
if (!containerDir) {
|
|
287
|
+
const hash = crypto
|
|
288
|
+
.createHash('sha1')
|
|
289
|
+
.update(actionDir)
|
|
290
|
+
.digest('hex')
|
|
291
|
+
.slice(0, 12);
|
|
292
|
+
fs.cpSync(actionDir, path.join(container.exchangeDir, 'actions', hash), {
|
|
293
|
+
recursive: true
|
|
294
|
+
});
|
|
295
|
+
containerDir = `${container_runtime_1.CONTAINER_EXCHANGE_DIR}/actions/${hash}`;
|
|
296
|
+
container.actionDirs.set(actionDir, containerDir);
|
|
297
|
+
}
|
|
298
|
+
return containerDir;
|
|
299
|
+
}
|
|
300
|
+
/**
|
|
301
|
+
* The time limit for the running step: its (or its job's) timeout-minutes,
|
|
302
|
+
* or --container-timeout in container mode, whichever is shorter, with the
|
|
303
|
+
* message to fail it with.
|
|
304
|
+
*/
|
|
305
|
+
function effectiveTimeout(runtime, containerTimeoutMs, inContainer = !!runtime.container) {
|
|
306
|
+
const own = runtime.stepTimeout;
|
|
307
|
+
const flag = inContainer && containerTimeoutMs ? containerTimeoutMs : undefined;
|
|
308
|
+
if (own && (flag === undefined || own.ms <= flag))
|
|
309
|
+
return own;
|
|
310
|
+
if (flag !== undefined) {
|
|
311
|
+
return {
|
|
312
|
+
ms: flag,
|
|
313
|
+
message: `Timed out after ${flag}ms (--container-timeout)`
|
|
314
|
+
};
|
|
315
|
+
}
|
|
316
|
+
return { message: '' };
|
|
317
|
+
}
|
|
318
|
+
/**
|
|
319
|
+
* The label shown for a step, like GitHub: its `name` with `${{ }}`
|
|
320
|
+
* expressions resolved, else its `uses:` reference, else the first
|
|
321
|
+
* non-empty line of its `run:` script, else "step".
|
|
322
|
+
*/
|
|
323
|
+
function stepDisplayName(step, ctx) {
|
|
324
|
+
if (step.name)
|
|
325
|
+
return ctx ? (0, expression_1.substituteExpressions)(step.name, ctx) : step.name;
|
|
326
|
+
if (step.uses)
|
|
327
|
+
return step.uses;
|
|
328
|
+
if (step.run) {
|
|
329
|
+
const line = step.run
|
|
330
|
+
.split('\n')
|
|
331
|
+
.map((l) => l.trim())
|
|
332
|
+
.find(Boolean);
|
|
333
|
+
if (line)
|
|
334
|
+
return ctx ? (0, expression_1.substituteExpressions)(line, ctx) : line;
|
|
335
|
+
}
|
|
336
|
+
return 'step';
|
|
337
|
+
}
|
|
338
|
+
//# sourceMappingURL=job-runtime.js.map
|