@secureci/cli 0.0.0-stage → 0.1.1
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 +230 -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/ansi.js +47 -0
- package/dist/utils/key-value.js +33 -0
- package/dist/utils/logger.js +84 -0
- package/dist/utils/repository.js +51 -0
- package/package.json +76 -4
|
@@ -0,0 +1,495 @@
|
|
|
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.resolveActionInputs = resolveActionInputs;
|
|
37
|
+
exports.warnUnsandboxedActions = warnUnsandboxedActions;
|
|
38
|
+
exports.stepFileSystem = stepFileSystem;
|
|
39
|
+
exports.runBuiltinAction = runBuiltinAction;
|
|
40
|
+
exports.fetchActionOnce = fetchActionOnce;
|
|
41
|
+
exports.runPreSteps = runPreSteps;
|
|
42
|
+
exports.isLocalActionRef = isLocalActionRef;
|
|
43
|
+
exports.resolveLocalAction = resolveLocalAction;
|
|
44
|
+
exports.executeAction = executeAction;
|
|
45
|
+
const fs = __importStar(require("fs"));
|
|
46
|
+
const path = __importStar(require("path"));
|
|
47
|
+
const workflow_parser_1 = require("../parser/workflow-parser");
|
|
48
|
+
const logger_1 = require("../utils/logger");
|
|
49
|
+
const action_fetcher_1 = require("./action-fetcher");
|
|
50
|
+
const shell_1 = require("./shell");
|
|
51
|
+
const builtin_actions_1 = require("./builtin-actions");
|
|
52
|
+
const expression_1 = require("./expression");
|
|
53
|
+
const container_runtime_1 = require("./container-runtime");
|
|
54
|
+
const job_runtime_1 = require("./job-runtime");
|
|
55
|
+
const checkout_1 = require("./checkout");
|
|
56
|
+
const steps_1 = require("./steps");
|
|
57
|
+
const docker_action_1 = require("./docker-action");
|
|
58
|
+
const node_action_1 = require("./node-action");
|
|
59
|
+
const validator_1 = require("../parser/validator");
|
|
60
|
+
/**
|
|
61
|
+
* Build an action's `inputs.*`: the step's `with:` values (resolved against
|
|
62
|
+
* the calling step's context), falling back to each declared input's
|
|
63
|
+
* `default` (which may itself contain expressions, e.g. `${{ github.token }}`).
|
|
64
|
+
*/
|
|
65
|
+
function resolveActionInputs(action, withValues, callerCtx) {
|
|
66
|
+
const inputs = {};
|
|
67
|
+
for (const [name, def] of Object.entries(action.inputs || {})) {
|
|
68
|
+
if (def?.default !== undefined) {
|
|
69
|
+
inputs[name] = (0, expression_1.substituteExpressions)(String(def.default), callerCtx);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
return { ...inputs, ...(0, expression_1.substituteRecord)(withValues, callerCtx) };
|
|
73
|
+
}
|
|
74
|
+
/**
|
|
75
|
+
* Warn that a job's remote actions are about to run on the host, with the
|
|
76
|
+
* user's privileges (container mode off, or no runtime available). Local
|
|
77
|
+
* actions, `docker://` steps and a local checkout are left out.
|
|
78
|
+
*/
|
|
79
|
+
function warnUnsandboxedActions(steps, ctx, options) {
|
|
80
|
+
const remote = steps
|
|
81
|
+
.filter((step) => step.uses &&
|
|
82
|
+
!isLocalActionRef(step.uses) &&
|
|
83
|
+
!step.uses.startsWith('docker://') &&
|
|
84
|
+
!(0, builtin_actions_1.builtinActionFor)(step.uses) &&
|
|
85
|
+
!(options.localCheckout !== false &&
|
|
86
|
+
(0, checkout_1.isLocalCheckout)(step.uses, step.with, ctx)))
|
|
87
|
+
.map((step) => step.uses);
|
|
88
|
+
if (remote.length === 0)
|
|
89
|
+
return;
|
|
90
|
+
logger_1.logger.warn(`Running remote actions on the host without container isolation: ${[...new Set(remote)].join(', ')}. They run with your user's privileges; use a container runtime (--container-runtime docker|podman|containerd) to sandbox them.`);
|
|
91
|
+
}
|
|
92
|
+
/** The file system a step sees: the host, or the job container. */
|
|
93
|
+
async function stepFileSystem(runtime) {
|
|
94
|
+
const container = runtime.container;
|
|
95
|
+
if (!container)
|
|
96
|
+
return (0, builtin_actions_1.hostFileSystem)(process.cwd());
|
|
97
|
+
if (container.home === undefined) {
|
|
98
|
+
const probe = await container.runtime.exec(container.id, {
|
|
99
|
+
command: 'printf %s "$HOME"'
|
|
100
|
+
});
|
|
101
|
+
container.home = (probe.success && probe.stdout.trim()) || '/root';
|
|
102
|
+
}
|
|
103
|
+
return (0, builtin_actions_1.containerFileSystem)({
|
|
104
|
+
workspace: container_runtime_1.CONTAINER_WORKDIR,
|
|
105
|
+
hostWorkspace: process.cwd(),
|
|
106
|
+
home: container.home,
|
|
107
|
+
copyFrom: (from, to) => container.runtime.copyFrom(container.id, from, to),
|
|
108
|
+
copyTo: (from, to) => container.runtime.copyTo(container.id, from, to),
|
|
109
|
+
mkdir: async (dir) => {
|
|
110
|
+
await container.runtime.exec(container.id, {
|
|
111
|
+
args: ['mkdir', '-p', dir]
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/**
|
|
117
|
+
* Run actions/upload-artifact, download-artifact or cache locally (see
|
|
118
|
+
* builtin-actions.ts); actions/cache's save is queued as a post step.
|
|
119
|
+
*/
|
|
120
|
+
async function runBuiltinAction(kind, step, ctx, options, runtime, startTime) {
|
|
121
|
+
const inputs = (0, expression_1.substituteRecord)(step.with, ctx);
|
|
122
|
+
const runId = String(ctx.github.run_id ?? options.runId ?? 'local');
|
|
123
|
+
const repository = String(ctx.github.repository ?? '');
|
|
124
|
+
const fsys = await stepFileSystem(runtime);
|
|
125
|
+
try {
|
|
126
|
+
const result = kind === 'upload-artifact'
|
|
127
|
+
? await (0, builtin_actions_1.uploadArtifact)(fsys, inputs, runId)
|
|
128
|
+
: kind === 'download-artifact'
|
|
129
|
+
? await (0, builtin_actions_1.downloadArtifact)(fsys, inputs, runId)
|
|
130
|
+
: await (0, builtin_actions_1.runCacheAction)(kind, fsys, inputs, repository);
|
|
131
|
+
if (result.warning)
|
|
132
|
+
logger_1.logger.warn(result.warning);
|
|
133
|
+
if (result.output)
|
|
134
|
+
logger_1.logger.info(result.output);
|
|
135
|
+
if (!result.success)
|
|
136
|
+
logger_1.logger.error(result.error ?? 'failed');
|
|
137
|
+
const post = result.post;
|
|
138
|
+
if (post) {
|
|
139
|
+
const postStep = { name: `Post ${step.name || step.uses}` };
|
|
140
|
+
runtime.postSteps.push({
|
|
141
|
+
step: postStep,
|
|
142
|
+
condition: 'success()',
|
|
143
|
+
run: async () => {
|
|
144
|
+
const postFs = await stepFileSystem(runtime);
|
|
145
|
+
try {
|
|
146
|
+
const saved = await post(postFs);
|
|
147
|
+
if (saved.warning)
|
|
148
|
+
logger_1.logger.warn(saved.warning);
|
|
149
|
+
if (saved.output)
|
|
150
|
+
logger_1.logger.info(saved.output);
|
|
151
|
+
return {
|
|
152
|
+
step: postStep,
|
|
153
|
+
success: saved.success,
|
|
154
|
+
output: saved.output,
|
|
155
|
+
error: saved.error,
|
|
156
|
+
duration: 0
|
|
157
|
+
};
|
|
158
|
+
}
|
|
159
|
+
finally {
|
|
160
|
+
postFs.cleanup();
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
return {
|
|
166
|
+
step,
|
|
167
|
+
success: result.success,
|
|
168
|
+
output: result.output,
|
|
169
|
+
outputs: result.outputs,
|
|
170
|
+
error: result.error,
|
|
171
|
+
duration: Date.now() - startTime
|
|
172
|
+
};
|
|
173
|
+
}
|
|
174
|
+
finally {
|
|
175
|
+
fsys.cleanup();
|
|
176
|
+
}
|
|
177
|
+
}
|
|
178
|
+
/** fetchAction, once per remote action per job. */
|
|
179
|
+
async function fetchActionOnce(uses, options, runtime) {
|
|
180
|
+
if (!runtime.actionPaths.has(uses)) {
|
|
181
|
+
runtime.actionPaths.set(uses, await (0, action_fetcher_1.fetchAction)(uses, { offline: options.offline }));
|
|
182
|
+
}
|
|
183
|
+
return runtime.actionPaths.get(uses) ?? null;
|
|
184
|
+
}
|
|
185
|
+
/**
|
|
186
|
+
* Run the `pre` scripts of the job's remote JavaScript actions at job
|
|
187
|
+
* start, before any step, as GitHub does (gated by `pre-if`, default
|
|
188
|
+
* `always()`). Their `main` later continues from the state they saved.
|
|
189
|
+
* Local actions usually come from the checkout, so their `pre` still runs
|
|
190
|
+
* right before their `main`. A failed `pre` fails the job.
|
|
191
|
+
*/
|
|
192
|
+
async function runPreSteps(steps, options, runtime, jobCtx, stepResults) {
|
|
193
|
+
for (const step of steps) {
|
|
194
|
+
if (!step.uses ||
|
|
195
|
+
isLocalActionRef(step.uses) ||
|
|
196
|
+
step.uses.startsWith('docker://') ||
|
|
197
|
+
(0, builtin_actions_1.builtinActionFor)(step.uses)) {
|
|
198
|
+
continue;
|
|
199
|
+
}
|
|
200
|
+
const envCtx = {
|
|
201
|
+
...jobCtx,
|
|
202
|
+
env: { ...jobCtx.env, ...runtime.env }
|
|
203
|
+
};
|
|
204
|
+
const stepCtx = {
|
|
205
|
+
...envCtx,
|
|
206
|
+
env: { ...envCtx.env, ...(0, expression_1.substituteRecord)(step.env, envCtx) }
|
|
207
|
+
};
|
|
208
|
+
if (options.localCheckout !== false &&
|
|
209
|
+
(0, checkout_1.isLocalCheckout)(step.uses, step.with, stepCtx)) {
|
|
210
|
+
continue;
|
|
211
|
+
}
|
|
212
|
+
// Unresolvable actions are reported when their step runs.
|
|
213
|
+
const actionPath = await fetchActionOnce(step.uses, options, runtime);
|
|
214
|
+
if (!actionPath)
|
|
215
|
+
continue;
|
|
216
|
+
let action;
|
|
217
|
+
try {
|
|
218
|
+
const parsed = (0, workflow_parser_1.parseFile)(actionPath);
|
|
219
|
+
if (parsed.type !== 'action')
|
|
220
|
+
continue;
|
|
221
|
+
action = parsed.file;
|
|
222
|
+
}
|
|
223
|
+
catch {
|
|
224
|
+
continue;
|
|
225
|
+
}
|
|
226
|
+
if (!/^node\d+$/.test(action.runs.using) || !action.runs.pre)
|
|
227
|
+
continue;
|
|
228
|
+
let runPre;
|
|
229
|
+
try {
|
|
230
|
+
runPre = (0, node_action_1.runPreIf)(action, step, stepCtx);
|
|
231
|
+
}
|
|
232
|
+
catch (error) {
|
|
233
|
+
const message = error instanceof Error ? error.message : String(error);
|
|
234
|
+
logger_1.logger.error(message);
|
|
235
|
+
stepResults.push({
|
|
236
|
+
step: { name: `Pre ${step.name || step.uses}` },
|
|
237
|
+
success: false,
|
|
238
|
+
error: message,
|
|
239
|
+
duration: 0
|
|
240
|
+
});
|
|
241
|
+
jobCtx.jobFailed = true;
|
|
242
|
+
continue;
|
|
243
|
+
}
|
|
244
|
+
if (!runPre)
|
|
245
|
+
continue;
|
|
246
|
+
logger_1.logger.info(`Running: Pre ${step.name || step.uses}`);
|
|
247
|
+
const result = await (0, node_action_1.executeNodeAction)(action, actionPath, step, resolveActionInputs(action, step.with, stepCtx), stepCtx, options, runtime, Date.now(), 'pre');
|
|
248
|
+
stepResults.push(result);
|
|
249
|
+
if (!result.success) {
|
|
250
|
+
jobCtx.jobFailed = true;
|
|
251
|
+
logger_1.logger.error(`Step failed: Pre ${step.name || step.uses}`);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
function isLocalActionRef(uses) {
|
|
256
|
+
return uses.startsWith('./') || uses.startsWith('../');
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Resolve a local action (`uses: ./path/to/action`) relative to the
|
|
260
|
+
* workspace, like the runner does, to its action.yml / action.yaml.
|
|
261
|
+
*/
|
|
262
|
+
function resolveLocalAction(uses) {
|
|
263
|
+
const dir = path.resolve(process.cwd(), uses);
|
|
264
|
+
for (const name of ['action.yml', 'action.yaml']) {
|
|
265
|
+
const candidate = path.join(dir, name);
|
|
266
|
+
if (fs.existsSync(candidate))
|
|
267
|
+
return candidate;
|
|
268
|
+
}
|
|
269
|
+
return null;
|
|
270
|
+
}
|
|
271
|
+
/**
|
|
272
|
+
* Execute an action step: local (`./`), remote (`owner/repo@ref`) or
|
|
273
|
+
* `docker://image`.
|
|
274
|
+
*/
|
|
275
|
+
async function executeAction(step, options, startTime, runtime, ctx) {
|
|
276
|
+
if (!step.uses) {
|
|
277
|
+
return {
|
|
278
|
+
step,
|
|
279
|
+
success: false,
|
|
280
|
+
error: 'No action specified',
|
|
281
|
+
duration: Date.now() - startTime
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
try {
|
|
285
|
+
const callerCtx = ctx ?? (0, expression_1.createContext)({}, {}, options.secrets);
|
|
286
|
+
// `uses: docker://image` runs the image directly, with `with.args` /
|
|
287
|
+
// `with.entrypoint` as its argv and entrypoint.
|
|
288
|
+
if (step.uses.startsWith('docker://')) {
|
|
289
|
+
const withValues = (0, expression_1.substituteRecord)(step.with, callerCtx);
|
|
290
|
+
const dockerAction = {
|
|
291
|
+
name: step.uses,
|
|
292
|
+
description: '',
|
|
293
|
+
runs: {
|
|
294
|
+
using: 'docker',
|
|
295
|
+
image: step.uses,
|
|
296
|
+
entrypoint: withValues.entrypoint,
|
|
297
|
+
args: withValues.args ? (0, shell_1.splitArgs)(withValues.args) : []
|
|
298
|
+
}
|
|
299
|
+
};
|
|
300
|
+
return await (0, docker_action_1.executeDockerAction)(dockerAction, path.join(process.cwd(), 'action.yml'), step, {}, callerCtx.env, options, runtime, startTime);
|
|
301
|
+
}
|
|
302
|
+
if (options.localCheckout !== false &&
|
|
303
|
+
(0, checkout_1.isLocalCheckout)(step.uses, step.with, callerCtx)) {
|
|
304
|
+
return (0, checkout_1.localCheckout)(step, callerCtx, runtime, startTime);
|
|
305
|
+
}
|
|
306
|
+
const builtin = (0, builtin_actions_1.builtinActionFor)(step.uses);
|
|
307
|
+
if (builtin) {
|
|
308
|
+
return await runBuiltinAction(builtin, step, callerCtx, options, runtime, startTime);
|
|
309
|
+
}
|
|
310
|
+
const actionPath = isLocalActionRef(step.uses)
|
|
311
|
+
? resolveLocalAction(step.uses)
|
|
312
|
+
: await fetchActionOnce(step.uses, options, runtime);
|
|
313
|
+
if (!actionPath) {
|
|
314
|
+
const error = `Could not resolve action "${step.uses}"${isLocalActionRef(step.uses)
|
|
315
|
+
? ` (no action.yml or action.yaml in ${path.resolve(process.cwd(), step.uses)})`
|
|
316
|
+
: ''}`;
|
|
317
|
+
logger_1.logger.error(error);
|
|
318
|
+
return {
|
|
319
|
+
step,
|
|
320
|
+
success: false,
|
|
321
|
+
error,
|
|
322
|
+
duration: Date.now() - startTime
|
|
323
|
+
};
|
|
324
|
+
}
|
|
325
|
+
// Parse the action file
|
|
326
|
+
const parsed = (0, workflow_parser_1.parseFile)(actionPath);
|
|
327
|
+
if (parsed.type !== 'action') {
|
|
328
|
+
logger_1.logger.error(`Invalid action file: ${actionPath}`);
|
|
329
|
+
return {
|
|
330
|
+
step,
|
|
331
|
+
success: false,
|
|
332
|
+
error: 'Invalid action file',
|
|
333
|
+
duration: Date.now() - startTime
|
|
334
|
+
};
|
|
335
|
+
}
|
|
336
|
+
const action = parsed.file;
|
|
337
|
+
// Resolve `with:` values against the calling step's context before they
|
|
338
|
+
// become the action's inputs (INPUT_* env vars / `inputs.*`).
|
|
339
|
+
const actionInputs = resolveActionInputs(action, step.with, callerCtx);
|
|
340
|
+
// Handle different action types
|
|
341
|
+
if (action.runs.using === 'composite') {
|
|
342
|
+
const invalid = (0, validator_1.actionExpressionErrors)(action);
|
|
343
|
+
if (invalid.length > 0) {
|
|
344
|
+
const error = `Invalid action file for ${step.uses}: ${invalid
|
|
345
|
+
.map((e) => `${e.path}: ${e.message}`)
|
|
346
|
+
.join('; ')}`;
|
|
347
|
+
logger_1.logger.error(error);
|
|
348
|
+
return {
|
|
349
|
+
step,
|
|
350
|
+
success: false,
|
|
351
|
+
error,
|
|
352
|
+
duration: Date.now() - startTime
|
|
353
|
+
};
|
|
354
|
+
}
|
|
355
|
+
// Execute composite action steps
|
|
356
|
+
logger_1.logger.info(`Executing composite action: ${action.name}`);
|
|
357
|
+
const compositeSteps = action.runs.steps || [];
|
|
358
|
+
const outputs = {};
|
|
359
|
+
// Composite steps see only the action's own inputs, plus the
|
|
360
|
+
// caller's env and github context - never the workflow's inputs.
|
|
361
|
+
// The action's files, as its steps see them (copied into the job
|
|
362
|
+
// container in container mode).
|
|
363
|
+
const actionDir = runtime.container
|
|
364
|
+
? (0, job_runtime_1.actionDirInContainer)(runtime.container, path.dirname(actionPath))
|
|
365
|
+
: path.dirname(actionPath);
|
|
366
|
+
const callerActionEnv = runtime.actionEnv;
|
|
367
|
+
const parent = {
|
|
368
|
+
repository: callerActionEnv.GITHUB_ACTION_REPOSITORY ?? '',
|
|
369
|
+
ref: callerActionEnv.GITHUB_ACTION_REF ?? '',
|
|
370
|
+
path: actionDir
|
|
371
|
+
};
|
|
372
|
+
const actionCtx = {
|
|
373
|
+
...(0, expression_1.createContext)({ ...callerCtx.env }, actionInputs, {}, { ...callerCtx.github, action_path: actionDir }),
|
|
374
|
+
secretsUnavailable: true,
|
|
375
|
+
vars: callerCtx.vars,
|
|
376
|
+
job: callerCtx.job,
|
|
377
|
+
runner: callerCtx.runner,
|
|
378
|
+
strategy: callerCtx.strategy
|
|
379
|
+
};
|
|
380
|
+
// `defaults.run` doesn't apply inside composite actions.
|
|
381
|
+
const callerDefaults = runtime.defaults;
|
|
382
|
+
runtime.defaults = {};
|
|
383
|
+
try {
|
|
384
|
+
for (const compositeStep of compositeSteps) {
|
|
385
|
+
runtime.actionEnv = (0, job_runtime_1.stepActionEnv)(runtime, compositeStep, parent);
|
|
386
|
+
const envCtx = {
|
|
387
|
+
...actionCtx,
|
|
388
|
+
github: {
|
|
389
|
+
...actionCtx.github,
|
|
390
|
+
action: runtime.actionEnv.GITHUB_ACTION
|
|
391
|
+
},
|
|
392
|
+
env: { ...actionCtx.env, ...runtime.env }
|
|
393
|
+
};
|
|
394
|
+
const stepCtx = {
|
|
395
|
+
...envCtx,
|
|
396
|
+
env: {
|
|
397
|
+
...envCtx.env,
|
|
398
|
+
...(0, expression_1.substituteRecord)(compositeStep.env, envCtx)
|
|
399
|
+
}
|
|
400
|
+
};
|
|
401
|
+
let runCompositeStep;
|
|
402
|
+
try {
|
|
403
|
+
runCompositeStep = (0, expression_1.evaluateCondition)(compositeStep.if, stepCtx);
|
|
404
|
+
}
|
|
405
|
+
catch (error) {
|
|
406
|
+
const message = (0, expression_1.ifEvaluationError)(`step '${(0, job_runtime_1.stepDisplayName)(compositeStep, stepCtx)}' of ${step.uses}`, error);
|
|
407
|
+
logger_1.logger.error(message);
|
|
408
|
+
return {
|
|
409
|
+
step,
|
|
410
|
+
success: false,
|
|
411
|
+
error: message,
|
|
412
|
+
duration: Date.now() - startTime
|
|
413
|
+
};
|
|
414
|
+
}
|
|
415
|
+
if (!runCompositeStep) {
|
|
416
|
+
logger_1.logger.info(`Skipping composite step: ${(0, job_runtime_1.stepDisplayName)(compositeStep, stepCtx)} (condition not met)`);
|
|
417
|
+
if (compositeStep.id) {
|
|
418
|
+
actionCtx.steps[compositeStep.id] = {
|
|
419
|
+
outcome: 'skipped',
|
|
420
|
+
outputs: {}
|
|
421
|
+
};
|
|
422
|
+
}
|
|
423
|
+
continue;
|
|
424
|
+
}
|
|
425
|
+
const stepOptions = {
|
|
426
|
+
...options,
|
|
427
|
+
inputs: actionInputs
|
|
428
|
+
};
|
|
429
|
+
const stepResult = await (0, steps_1.executeStep)(compositeStep, stepOptions, runtime, stepCtx);
|
|
430
|
+
if (compositeStep.id) {
|
|
431
|
+
actionCtx.steps[compositeStep.id] = {
|
|
432
|
+
outcome: stepResult.success ? 'success' : 'failure',
|
|
433
|
+
outputs: stepResult.outputs || {}
|
|
434
|
+
};
|
|
435
|
+
}
|
|
436
|
+
if (!stepResult.success) {
|
|
437
|
+
actionCtx.jobFailed = true;
|
|
438
|
+
return {
|
|
439
|
+
step,
|
|
440
|
+
success: false,
|
|
441
|
+
error: `Composite action step failed: ${(0, job_runtime_1.stepDisplayName)(compositeStep, stepCtx)}`,
|
|
442
|
+
duration: Date.now() - startTime
|
|
443
|
+
};
|
|
444
|
+
}
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
finally {
|
|
448
|
+
runtime.defaults = callerDefaults;
|
|
449
|
+
runtime.actionEnv = callerActionEnv;
|
|
450
|
+
}
|
|
451
|
+
// Resolve the action's declared outputs (e.g.
|
|
452
|
+
// `outputs.greeting.value: ${{ steps.greet.outputs.greeting }}`)
|
|
453
|
+
// against the accumulated step outcomes/outputs.
|
|
454
|
+
for (const [outputName, outputDef] of Object.entries(action.outputs || {})) {
|
|
455
|
+
const resolved = (0, expression_1.resolveExpressionValue)(outputDef.value, actionCtx);
|
|
456
|
+
if (resolved !== undefined) {
|
|
457
|
+
outputs[outputName] = String(resolved);
|
|
458
|
+
}
|
|
459
|
+
}
|
|
460
|
+
return {
|
|
461
|
+
step,
|
|
462
|
+
success: true,
|
|
463
|
+
output: JSON.stringify(outputs),
|
|
464
|
+
outputs,
|
|
465
|
+
duration: Date.now() - startTime
|
|
466
|
+
};
|
|
467
|
+
}
|
|
468
|
+
else if (/^node\d+$/.test(action.runs.using)) {
|
|
469
|
+
return await (0, node_action_1.executeNodeAction)(action, actionPath, step, actionInputs, callerCtx, options, runtime, startTime);
|
|
470
|
+
}
|
|
471
|
+
else if (action.runs.using === 'docker') {
|
|
472
|
+
return await (0, docker_action_1.executeDockerAction)(action, actionPath, step, actionInputs, callerCtx.env, options, runtime, startTime);
|
|
473
|
+
}
|
|
474
|
+
else {
|
|
475
|
+
const error = `Unsupported action type "${action.runs.using}" in ${step.uses}`;
|
|
476
|
+
logger_1.logger.error(error);
|
|
477
|
+
return {
|
|
478
|
+
step,
|
|
479
|
+
success: false,
|
|
480
|
+
error,
|
|
481
|
+
duration: Date.now() - startTime
|
|
482
|
+
};
|
|
483
|
+
}
|
|
484
|
+
}
|
|
485
|
+
catch (error) {
|
|
486
|
+
logger_1.logger.error(`Action execution failed: ${error instanceof Error ? error.message : 'Unknown error'}`);
|
|
487
|
+
return {
|
|
488
|
+
step,
|
|
489
|
+
success: false,
|
|
490
|
+
error: error instanceof Error ? error.message : 'Unknown error',
|
|
491
|
+
duration: Date.now() - startTime
|
|
492
|
+
};
|
|
493
|
+
}
|
|
494
|
+
}
|
|
495
|
+
//# sourceMappingURL=actions.js.map
|