@async/pipeline 0.9.4 → 0.9.6
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/API_SURFACE.md +3 -1
- package/api-contract.json +20 -0
- package/dist/internal/core/index.d.ts +10 -0
- package/dist/internal/core/index.d.ts.map +1 -1
- package/dist/internal/core/index.js +35 -4
- package/dist/internal/core/index.js.map +1 -1
- package/dist/internal/node/cli.d.ts.map +1 -1
- package/dist/internal/node/cli.js +44 -4
- package/dist/internal/node/cli.js.map +1 -1
- package/dist/internal/node/github.d.ts +10 -1
- package/dist/internal/node/github.d.ts.map +1 -1
- package/dist/internal/node/github.js +171 -66
- package/dist/internal/node/github.js.map +1 -1
- package/dist/internal/node/runner.d.ts +28 -0
- package/dist/internal/node/runner.d.ts.map +1 -1
- package/dist/internal/node/runner.js +50 -1
- package/dist/internal/node/runner.js.map +1 -1
- package/dist/internal/node/sources.d.ts +25 -1
- package/dist/internal/node/sources.d.ts.map +1 -1
- package/dist/internal/node/sources.js +72 -2
- package/dist/internal/node/sources.js.map +1 -1
- package/package.json +1 -1
|
@@ -3,9 +3,10 @@ import { existsSync } from "node:fs";
|
|
|
3
3
|
import { mkdir, readFile, readdir, writeFile } from "node:fs/promises";
|
|
4
4
|
import { dirname, join, relative, resolve } from "node:path";
|
|
5
5
|
import { githubConfigForJob, pipelineError } from "../core/index.js";
|
|
6
|
+
import { sourceImpactPlanForJob } from "./sources.js";
|
|
6
7
|
export const GITHUB_WORKFLOW_PATH = ".github/workflows/async-pipeline.yml";
|
|
7
8
|
export const GITHUB_LOCK_PATH = ".github/async-pipeline.lock.json";
|
|
8
|
-
const GENERATOR_VERSION =
|
|
9
|
+
const GENERATOR_VERSION = 17;
|
|
9
10
|
const DEFAULT_NODE_VERSION = "24";
|
|
10
11
|
const DEFAULT_DENO_VERSION = "2";
|
|
11
12
|
const DEFAULT_PNPM_VERSION = "11.1.0";
|
|
@@ -19,8 +20,8 @@ function defineActionRef(id, uses, sha, label) {
|
|
|
19
20
|
ref: `${uses}@${sha} # ${label}`
|
|
20
21
|
};
|
|
21
22
|
}
|
|
22
|
-
const ASYNC_ACTIONS_SHA = "
|
|
23
|
-
const ASYNC_ACTIONS_LABEL = "v0.1.
|
|
23
|
+
const ASYNC_ACTIONS_SHA = "1b1a167072f242ed200f8f5ec7cdf10c8a9ae241";
|
|
24
|
+
const ASYNC_ACTIONS_LABEL = "v0.1.8";
|
|
24
25
|
const GENERATED_ACTIONS = [
|
|
25
26
|
defineActionRef("async.actions.setup", "async/actions/setup", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
26
27
|
defineActionRef("async.actions.run", "async/actions/run", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
@@ -29,8 +30,9 @@ const GENERATED_ACTIONS = [
|
|
|
29
30
|
defineActionRef("async.actions.publish", "async/actions/publish", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
30
31
|
defineActionRef("async.actions.dependabot-merge", "async/actions/dependabot-merge", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
31
32
|
defineActionRef("async.actions.evidence", "async/actions/evidence", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
33
|
+
defineActionRef("async.actions.source-impact", "async/actions/source-impact", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
34
|
+
defineActionRef("async.actions.cache", "async/actions/cache", ASYNC_ACTIONS_SHA, ASYNC_ACTIONS_LABEL),
|
|
32
35
|
defineActionRef("actions.checkout", "actions/checkout", "de0fac2e4500dabe0009e67214ff5f5447ce83dd", "v6.0.2"),
|
|
33
|
-
defineActionRef("actions.cache", "actions/cache", "0057852bfaa89a56745cba8c7296529d2fc39830", "v4"),
|
|
34
36
|
defineActionRef("pnpm.setup", "pnpm/setup", "cf03a9b516e09bc5a90f041fc26fc930c9dc631b", "v1.0.0"),
|
|
35
37
|
defineActionRef("deno.setup", "denoland/setup-deno", "667a34cdef165d8d2b2e98dde39547c9daac7282", "v2.0.4"),
|
|
36
38
|
defineActionRef("actions.setup-node", "actions/setup-node", "48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e", "v6"),
|
|
@@ -46,8 +48,9 @@ const ASYNC_PREVIEW_ACTION = actionRef("async.actions.preview");
|
|
|
46
48
|
const ASYNC_PUBLISH_ACTION = actionRef("async.actions.publish");
|
|
47
49
|
const ASYNC_DEPENDABOT_MERGE_ACTION = actionRef("async.actions.dependabot-merge");
|
|
48
50
|
const ASYNC_EVIDENCE_ACTION = actionRef("async.actions.evidence");
|
|
51
|
+
const ASYNC_SOURCE_IMPACT_ACTION = actionRef("async.actions.source-impact");
|
|
52
|
+
const ASYNC_CACHE_ACTION = actionRef("async.actions.cache");
|
|
49
53
|
const CHECKOUT_ACTION = actionRef("actions.checkout");
|
|
50
|
-
const CACHE_ACTION = actionRef("actions.cache");
|
|
51
54
|
const PNPM_SETUP_ACTION = actionRef("pnpm.setup");
|
|
52
55
|
const DENO_SETUP_ACTION = actionRef("deno.setup");
|
|
53
56
|
const SETUP_NODE_ACTION = actionRef("actions.setup-node");
|
|
@@ -65,6 +68,7 @@ export async function renderGitHubWorkflow(pipeline, options) {
|
|
|
65
68
|
const packageInfo = await readPackageInfo(options.cwd);
|
|
66
69
|
const renderModel = buildRenderModel(pipeline, {
|
|
67
70
|
...packageInfo,
|
|
71
|
+
cwd: options.cwd,
|
|
68
72
|
configPath: relativePath(options.cwd, options.configPath),
|
|
69
73
|
workflowPath
|
|
70
74
|
});
|
|
@@ -88,6 +92,7 @@ export async function renderGitHubWorkflow(pipeline, options) {
|
|
|
88
92
|
dependabotAutoMerge: renderModel.dependabotAutoMerge,
|
|
89
93
|
packagePreviews: renderModel.packagePreviews,
|
|
90
94
|
evidence: renderModel.evidence,
|
|
95
|
+
sourceImpact: renderModel.sourceImpact,
|
|
91
96
|
bridge: renderModel.bridge,
|
|
92
97
|
pages: renderModel.pages,
|
|
93
98
|
manualDispatchJobs: renderModel.manualDispatchJobs,
|
|
@@ -116,6 +121,7 @@ export async function renderGitHubWorkflow(pipeline, options) {
|
|
|
116
121
|
dependabotAutoMerge: renderModel.dependabotAutoMerge,
|
|
117
122
|
packagePreviews: renderModel.packagePreviews,
|
|
118
123
|
evidence: renderModel.evidence,
|
|
124
|
+
sourceImpact: renderModel.sourceImpact,
|
|
119
125
|
bridge: renderModel.bridge,
|
|
120
126
|
pages: renderModel.pages,
|
|
121
127
|
manualDispatchJobs: renderModel.manualDispatchJobs
|
|
@@ -264,25 +270,38 @@ function buildRenderModel(pipeline, options) {
|
|
|
264
270
|
const nodeVersion = pipeline.sync.github.nodeVersion ?? DEFAULT_NODE_VERSION;
|
|
265
271
|
const runtime = resolveRuntimeSpecs(pipeline.sync.github.runtime, options.projectKind, nodeVersion);
|
|
266
272
|
const setup = resolveGitHubSetup(pipeline.sync.github.setup, options.packageManager, options.packageManagerVersion);
|
|
273
|
+
const jobs = Object.values(pipeline.jobs)
|
|
274
|
+
.map((job) => ({
|
|
275
|
+
id: job.id,
|
|
276
|
+
target: [...job.target],
|
|
277
|
+
trigger: [...job.trigger],
|
|
278
|
+
env: { ...pipeline.env, ...(job.env ?? {}) },
|
|
279
|
+
environment: job.environment,
|
|
280
|
+
requires: job.requires,
|
|
281
|
+
execution: job.execution,
|
|
282
|
+
github: githubConfigForJob(pipeline, job),
|
|
283
|
+
if: renderGitHubJobCondition(job, pipeline.triggers)
|
|
284
|
+
}))
|
|
285
|
+
.sort((left, right) => left.id.localeCompare(right.id));
|
|
286
|
+
const sourceImpactJobs = resolveGitHubSourceImpactJobs(pipeline, options.cwd, jobs);
|
|
287
|
+
const sourceImpact = {
|
|
288
|
+
...pipeline.sync.github.sourceImpact,
|
|
289
|
+
generatedJobs: sourceImpactJobs.map((job) => ({
|
|
290
|
+
job: job.job,
|
|
291
|
+
planJob: job.planJob,
|
|
292
|
+
matrixJob: job.matrixJob,
|
|
293
|
+
matrixRows: job.plan.matrix.include.length,
|
|
294
|
+
sources: Object.keys(job.plan.sources).sort((left, right) => left.localeCompare(right))
|
|
295
|
+
}))
|
|
296
|
+
};
|
|
267
297
|
return {
|
|
268
298
|
name: "Async Pipeline",
|
|
269
299
|
configPath: options.configPath,
|
|
270
300
|
workflowPath: options.workflowPath,
|
|
271
301
|
projectKind: options.projectKind,
|
|
272
302
|
triggers,
|
|
273
|
-
jobs
|
|
274
|
-
|
|
275
|
-
id: job.id,
|
|
276
|
-
target: [...job.target],
|
|
277
|
-
trigger: [...job.trigger],
|
|
278
|
-
env: { ...pipeline.env, ...(job.env ?? {}) },
|
|
279
|
-
environment: job.environment,
|
|
280
|
-
requires: job.requires,
|
|
281
|
-
execution: job.execution,
|
|
282
|
-
github: githubConfigForJob(pipeline, job),
|
|
283
|
-
if: renderGitHubJobCondition(job, pipeline.triggers)
|
|
284
|
-
}))
|
|
285
|
-
.sort((left, right) => left.id.localeCompare(right.id)),
|
|
303
|
+
jobs,
|
|
304
|
+
sourceImpactJobs,
|
|
286
305
|
tasks: pipeline.tasks,
|
|
287
306
|
packageManager: options.packageManager,
|
|
288
307
|
packageManagerVersion: options.packageManagerVersion,
|
|
@@ -297,6 +316,7 @@ function buildRenderModel(pipeline, options) {
|
|
|
297
316
|
dependabotAutoMerge: pipeline.sync.github.dependabotAutoMerge,
|
|
298
317
|
packagePreviews,
|
|
299
318
|
evidence,
|
|
319
|
+
sourceImpact,
|
|
300
320
|
bridge,
|
|
301
321
|
pages,
|
|
302
322
|
manualDispatchJobs
|
|
@@ -315,6 +335,63 @@ function resolveGitHubEvidence(pipeline) {
|
|
|
315
335
|
}
|
|
316
336
|
return config;
|
|
317
337
|
}
|
|
338
|
+
function resolveGitHubSourceImpactJobs(pipeline, cwd, jobs) {
|
|
339
|
+
const config = pipeline.sync.github.sourceImpact;
|
|
340
|
+
if (!config.enabled)
|
|
341
|
+
return [];
|
|
342
|
+
const jobsById = new Map(jobs.map((job) => [job.id, job]));
|
|
343
|
+
const explicitJobs = new Set(config.jobs);
|
|
344
|
+
const selectedJobIds = config.jobs.length > 0 ? config.jobs : jobs.map((job) => job.id);
|
|
345
|
+
const generatedIds = new Set([
|
|
346
|
+
"package-preview",
|
|
347
|
+
"dependabot-auto-merge",
|
|
348
|
+
"async-bridge",
|
|
349
|
+
pipeline.sync.github.evidence.job,
|
|
350
|
+
pipeline.sync.github.pages.job
|
|
351
|
+
].map((id) => id.toLowerCase()));
|
|
352
|
+
const existingJobIds = new Set(Object.keys(pipeline.jobs).map((id) => id.toLowerCase()));
|
|
353
|
+
const result = [];
|
|
354
|
+
for (const jobId of selectedJobIds) {
|
|
355
|
+
const job = jobsById.get(jobId);
|
|
356
|
+
if (!job) {
|
|
357
|
+
throw pipelineError("ASYNC_PIPELINE_GITHUB_SOURCE_IMPACT_INVALID", `sync.github.sourceImpact references missing job "${jobId}".`);
|
|
358
|
+
}
|
|
359
|
+
if (job.github?.runsOnMatrix) {
|
|
360
|
+
throw pipelineError("ASYNC_PIPELINE_GITHUB_SOURCE_IMPACT_INVALID", `sync.github.sourceImpact cannot target job "${jobId}" because that job already uses github.runsOnMatrix.`);
|
|
361
|
+
}
|
|
362
|
+
const plan = sourceImpactPlanForJob(pipeline, cwd, jobId);
|
|
363
|
+
if (plan.matrix.include.length === 0) {
|
|
364
|
+
if (explicitJobs.has(jobId)) {
|
|
365
|
+
throw pipelineError("ASYNC_PIPELINE_GITHUB_SOURCE_IMPACT_INVALID", `sync.github.sourceImpact job "${jobId}" has no source task refs.`);
|
|
366
|
+
}
|
|
367
|
+
continue;
|
|
368
|
+
}
|
|
369
|
+
const generatedJobPrefix = safeGeneratedJobId(jobId);
|
|
370
|
+
const planJob = `${generatedJobPrefix}-source-plan`;
|
|
371
|
+
const matrixJob = `${generatedJobPrefix}-sources`;
|
|
372
|
+
for (const generatedJob of [planJob, matrixJob]) {
|
|
373
|
+
const generatedJobKey = generatedJob.toLowerCase();
|
|
374
|
+
if (existingJobIds.has(generatedJobKey)) {
|
|
375
|
+
throw pipelineError("ASYNC_PIPELINE_GITHUB_SOURCE_IMPACT_JOB_CONFLICT", `Generated source-impact job "${generatedJob}" conflicts with an existing pipeline job.`);
|
|
376
|
+
}
|
|
377
|
+
if (generatedIds.has(generatedJobKey)) {
|
|
378
|
+
throw pipelineError("ASYNC_PIPELINE_GITHUB_SOURCE_IMPACT_JOB_CONFLICT", `Generated source-impact job "${generatedJob}" conflicts with another generated GitHub job.`);
|
|
379
|
+
}
|
|
380
|
+
generatedIds.add(generatedJobKey);
|
|
381
|
+
}
|
|
382
|
+
result.push({
|
|
383
|
+
job: jobId,
|
|
384
|
+
planJob,
|
|
385
|
+
matrixJob,
|
|
386
|
+
planPath: `.async/actions/source-impact/${safeArtifactPart(jobId)}-source-plan.json`,
|
|
387
|
+
plan,
|
|
388
|
+
if: job.if,
|
|
389
|
+
github: job.github,
|
|
390
|
+
env: job.env
|
|
391
|
+
});
|
|
392
|
+
}
|
|
393
|
+
return result.sort((left, right) => left.job.localeCompare(right.job));
|
|
394
|
+
}
|
|
318
395
|
function resolveGitHubBridge(pipeline) {
|
|
319
396
|
const config = pipeline.sync.github.bridge;
|
|
320
397
|
const actionsJobEnabled = gitHubBridgeActionsEnabled(config);
|
|
@@ -466,6 +543,10 @@ function renderWorkflow(model) {
|
|
|
466
543
|
renderPagesDeployJob(lines, job);
|
|
467
544
|
}
|
|
468
545
|
}
|
|
546
|
+
for (const sourceJob of model.sourceImpactJobs) {
|
|
547
|
+
renderSourceImpactPlanJob(lines, model, sourceJob);
|
|
548
|
+
renderSourceImpactMatrixJob(lines, model, sourceJob);
|
|
549
|
+
}
|
|
469
550
|
if (model.pages.enabled) {
|
|
470
551
|
renderGeneratedPagesJob(lines, model);
|
|
471
552
|
renderPagesDeployJob(lines, {
|
|
@@ -538,18 +619,7 @@ function renderJob(lines, model, job) {
|
|
|
538
619
|
if (pullRequests)
|
|
539
620
|
lines.push(` pull-requests: ${pullRequests}`);
|
|
540
621
|
}
|
|
541
|
-
lines.push(" steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, "", ...(model
|
|
542
|
-
? [
|
|
543
|
-
" - name: Restore task cache",
|
|
544
|
-
` uses: ${CACHE_ACTION}`,
|
|
545
|
-
" with:",
|
|
546
|
-
" path: .async/cache",
|
|
547
|
-
" key: async-pipeline-${{ runner.os }}-${{ github.sha }}",
|
|
548
|
-
" restore-keys: |",
|
|
549
|
-
" async-pipeline-${{ runner.os }}-",
|
|
550
|
-
""
|
|
551
|
-
]
|
|
552
|
-
: []), ...renderSetupSteps(model), ...(idToken === "write"
|
|
622
|
+
lines.push(" steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, "", ...renderSetupSteps(model), ...(idToken === "write"
|
|
553
623
|
? [
|
|
554
624
|
" - name: Use current npm",
|
|
555
625
|
" run: npm install -g npm@11.16.0",
|
|
@@ -559,6 +629,7 @@ function renderJob(lines, model, job) {
|
|
|
559
629
|
if (model.buildCommand) {
|
|
560
630
|
lines.push("", " - name: Build pipeline CLI", ` run: ${model.buildCommand}`);
|
|
561
631
|
}
|
|
632
|
+
renderTaskCacheRestoreSteps(lines, model, { kind: "job", id: job.id });
|
|
562
633
|
const lifecyclePlan = resolveLifecycleJobPlan(model, job);
|
|
563
634
|
if (lifecyclePlan) {
|
|
564
635
|
renderLifecycleJobPlan(lines, model, job, lifecyclePlan);
|
|
@@ -566,6 +637,7 @@ function renderJob(lines, model, job) {
|
|
|
566
637
|
else {
|
|
567
638
|
renderRunActionStep(lines, "Run pipeline job", `${model.command} github check && ${model.command} run ${shellWord(job.id)}${job.execution ? ` --execution ${shellWord(job.execution)}` : ""}`, job.env);
|
|
568
639
|
}
|
|
640
|
+
renderTaskCacheSaveSteps(lines, model, { kind: "job", id: job.id });
|
|
569
641
|
if (job.github?.pages) {
|
|
570
642
|
lines.push("");
|
|
571
643
|
renderPagesBuildSteps(lines, job.github.pages);
|
|
@@ -573,26 +645,71 @@ function renderJob(lines, model, job) {
|
|
|
573
645
|
renderEvidenceCollectStep(lines, model, { matrix: Boolean(runnerMatrix && runnerMatrix.length > 0) });
|
|
574
646
|
lines.push("");
|
|
575
647
|
}
|
|
648
|
+
function renderSourceImpactPlanJob(lines, model, sourceJob) {
|
|
649
|
+
const runsOn = sourceJob.github?.runsOn ?? "ubuntu-latest";
|
|
650
|
+
lines.push(` ${yamlKey(sourceJob.planJob)}:`, ` name: ${sourceJob.planJob}`);
|
|
651
|
+
if (sourceJob.if) {
|
|
652
|
+
lines.push(` if: ${sourceJob.if}`);
|
|
653
|
+
}
|
|
654
|
+
lines.push(` runs-on: ${Array.isArray(runsOn) ? JSON.stringify(runsOn) : runsOn}`, " permissions:", " contents: read", " outputs:", " matrix: ${{ steps.source-plan.outputs.matrix }}", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, "", ...renderSetupSteps(model));
|
|
655
|
+
renderWriteSourceImpactPlanStep(lines, sourceJob);
|
|
656
|
+
lines.push("", " - name: Plan source impact matrix", " id: source-plan", ` uses: ${ASYNC_SOURCE_IMPACT_ACTION}`, " with:", " mode: plan", ` source-plan: ${sourceJob.planPath}`, " output-matrix: true");
|
|
657
|
+
renderEvidenceCollectStep(lines, model);
|
|
658
|
+
lines.push("");
|
|
659
|
+
}
|
|
660
|
+
function renderSourceImpactMatrixJob(lines, model, sourceJob) {
|
|
661
|
+
const runsOn = sourceJob.github?.runsOn ?? "ubuntu-latest";
|
|
662
|
+
lines.push(` ${yamlKey(sourceJob.matrixJob)}:`, ` name: ${sourceJob.job} source (\${{ matrix.source }}:\${{ matrix.taskId }})`, ` needs: ${JSON.stringify(sourceJob.planJob)}`, ` if: \${{ always() && needs['${sourceJob.planJob}'].result == 'success' }}`, " strategy:", " fail-fast: false", ` matrix: \${{ fromJSON(needs['${sourceJob.planJob}'].outputs.matrix || '{"include":[]}') }}`, ` runs-on: ${Array.isArray(runsOn) ? JSON.stringify(runsOn) : runsOn}`, " permissions:", " contents: read", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, "", ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
663
|
+
if (model.buildCommand) {
|
|
664
|
+
lines.push("", " - name: Build pipeline CLI", ` run: ${model.buildCommand}`);
|
|
665
|
+
}
|
|
666
|
+
renderWriteSourceImpactPlanStep(lines, sourceJob);
|
|
667
|
+
lines.push("", " - name: Validate source checkout", ` uses: ${ASYNC_SOURCE_IMPACT_ACTION}`, " with:", " mode: checkout", ` source-plan: ${sourceJob.planPath}`, " source-id: ${{ matrix.source }}", " ref: ${{ matrix.ref }}", " path: ${{ matrix.path }}", "", " - name: Prepare source checkout", ` uses: ${ASYNC_SOURCE_IMPACT_ACTION}`, " with:", " mode: prepare", ` source-plan: ${sourceJob.planPath}`, " source-id: ${{ matrix.source }}", " path: ${{ matrix.path }}");
|
|
668
|
+
renderTaskCacheRestoreSteps(lines, model, {
|
|
669
|
+
kind: "task",
|
|
670
|
+
id: "\"${{ matrix.task }}\"",
|
|
671
|
+
manifestPath: ".async/actions/cache/${{ matrix.source }}-${{ matrix.taskId }}-cache-manifest.json"
|
|
672
|
+
});
|
|
673
|
+
renderRunActionStep(lines, "Run source task", `${model.command} github check && ${model.command} run-task "\${{ matrix.task }}"`, scopeActionEnv(sourceJob.env, new Set()), { artifactName: "async-pipeline-${{ github.job }}-${{ matrix.source }}-${{ matrix.taskId }}-runs" });
|
|
674
|
+
renderTaskCacheSaveSteps(lines, model, {
|
|
675
|
+
kind: "task",
|
|
676
|
+
id: "\"${{ matrix.task }}\"",
|
|
677
|
+
manifestPath: ".async/actions/cache/${{ matrix.source }}-${{ matrix.taskId }}-cache-manifest.json"
|
|
678
|
+
});
|
|
679
|
+
renderEvidenceCollectStep(lines, model, { matrix: true });
|
|
680
|
+
lines.push("");
|
|
681
|
+
}
|
|
682
|
+
function renderTaskCacheRestoreSteps(lines, model, target) {
|
|
683
|
+
if (!model.taskCache)
|
|
684
|
+
return;
|
|
685
|
+
const manifestPath = target.manifestPath ?? `.async/actions/cache/${safeArtifactPart(target.id)}-cache-manifest.json`;
|
|
686
|
+
lines.push("", " - name: Write task cache manifest", ` run: ${renderCacheManifestCommand(model, target, manifestPath, "read-only")}`, "", " - name: Restore Async task cache", " id: async-cache-restore", ` uses: ${ASYNC_CACHE_ACTION}`, " with:", " mode: restore", ` manifest: ${manifestPath}`, " trust: read-only");
|
|
687
|
+
}
|
|
688
|
+
function renderTaskCacheSaveSteps(lines, model, target) {
|
|
689
|
+
if (!model.taskCache)
|
|
690
|
+
return;
|
|
691
|
+
const manifestPath = target.manifestPath ?? `.async/actions/cache/${safeArtifactPart(target.id)}-cache-manifest.json`;
|
|
692
|
+
lines.push("", " - name: Save Async task cache", " if: ${{ success() && github.event_name != 'pull_request' && steps.async-cache-restore.outputs.cache-hit != 'true' }}", ` uses: ${ASYNC_CACHE_ACTION}`, " with:", " mode: save", ` manifest: ${manifestPath}`, " trust: read-write");
|
|
693
|
+
}
|
|
694
|
+
function renderCacheManifestCommand(model, target, manifestPath, trust) {
|
|
695
|
+
const targetFlag = target.kind === "job" ? "--job" : "--task";
|
|
696
|
+
return `${model.command} cache manifest ${targetFlag} ${target.id.startsWith("\"") ? target.id : shellWord(target.id)} --output ${shellWord(manifestPath)} --trust ${trust}`;
|
|
697
|
+
}
|
|
698
|
+
function renderWriteSourceImpactPlanStep(lines, sourceJob) {
|
|
699
|
+
const planJson = JSON.stringify(sourceJob.plan, null, 2);
|
|
700
|
+
lines.push("", " - name: Write generated source plan", " run: |", ` mkdir -p ${shellWord(dirname(sourceJob.planPath))}`, ` cat > ${shellWord(sourceJob.planPath)} <<'ASYNC_SOURCE_PLAN'`, ...planJson.split("\n").map((line) => ` ${line}`), " ASYNC_SOURCE_PLAN");
|
|
701
|
+
}
|
|
576
702
|
function renderGeneratedPagesJob(lines, model) {
|
|
577
703
|
const pages = model.pages;
|
|
578
704
|
if (!pages.target)
|
|
579
705
|
return;
|
|
580
|
-
lines.push(` ${yamlKey(pages.job)}:`, ` name: ${pages.job}`, ` if: ${renderGeneratedPagesCondition(pages)}`, " runs-on: ubuntu-latest", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, "", ...(model
|
|
581
|
-
? [
|
|
582
|
-
" - name: Restore task cache",
|
|
583
|
-
` uses: ${CACHE_ACTION}`,
|
|
584
|
-
" with:",
|
|
585
|
-
" path: .async/cache",
|
|
586
|
-
" key: async-pipeline-${{ runner.os }}-${{ github.sha }}",
|
|
587
|
-
" restore-keys: |",
|
|
588
|
-
" async-pipeline-${{ runner.os }}-",
|
|
589
|
-
""
|
|
590
|
-
]
|
|
591
|
-
: []), ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
706
|
+
lines.push(` ${yamlKey(pages.job)}:`, ` name: ${pages.job}`, ` if: ${renderGeneratedPagesCondition(pages)}`, " runs-on: ubuntu-latest", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, "", ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
592
707
|
if (model.buildCommand) {
|
|
593
708
|
lines.push("", " - name: Build pipeline CLI", ` run: ${model.buildCommand}`);
|
|
594
709
|
}
|
|
710
|
+
renderTaskCacheRestoreSteps(lines, model, { kind: "task", id: pages.target });
|
|
595
711
|
renderRunActionStep(lines, "Run Pages target", `${model.command} github check && ${model.command} run-task ${shellWord(pages.target)}`, {});
|
|
712
|
+
renderTaskCacheSaveSteps(lines, model, { kind: "task", id: pages.target });
|
|
596
713
|
lines.push("");
|
|
597
714
|
renderPagesBuildSteps(lines, pages);
|
|
598
715
|
renderEvidenceCollectStep(lines, model);
|
|
@@ -887,44 +1004,28 @@ function splitShellWords(command) {
|
|
|
887
1004
|
function safeArtifactPart(value) {
|
|
888
1005
|
return value.replace(/[^A-Za-z0-9_.-]+/gu, "-");
|
|
889
1006
|
}
|
|
1007
|
+
function safeGeneratedJobId(value) {
|
|
1008
|
+
const normalized = value.toLowerCase().replace(/[^a-z0-9_-]+/gu, "-").replace(/^-+|-+$/gu, "");
|
|
1009
|
+
return normalized || "job";
|
|
1010
|
+
}
|
|
890
1011
|
function renderPackagePreviewJob(lines, model) {
|
|
891
1012
|
const preview = model.packagePreviews;
|
|
892
1013
|
if (!preview.package || !preview.target)
|
|
893
1014
|
return;
|
|
894
|
-
lines.push(" package-preview:", " name: package-preview", " if: github.event_name == 'pull_request' && github.event.pull_request.draft == false", " runs-on: ubuntu-latest", " permissions:", " contents: read", " issues: write", " packages: write", " pull-requests: write", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, " with:", " persist-credentials: false", "", ...(model
|
|
895
|
-
? [
|
|
896
|
-
" - name: Restore task cache",
|
|
897
|
-
` uses: ${CACHE_ACTION}`,
|
|
898
|
-
" with:",
|
|
899
|
-
" path: .async/cache",
|
|
900
|
-
" key: async-pipeline-${{ runner.os }}-${{ github.sha }}",
|
|
901
|
-
" restore-keys: |",
|
|
902
|
-
" async-pipeline-${{ runner.os }}-",
|
|
903
|
-
""
|
|
904
|
-
]
|
|
905
|
-
: []), ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
1015
|
+
lines.push(" package-preview:", " name: package-preview", " if: github.event_name == 'pull_request' && github.event.pull_request.draft == false", " runs-on: ubuntu-latest", " permissions:", " contents: read", " issues: write", " packages: write", " pull-requests: write", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, " with:", " persist-credentials: false", "", ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
906
1016
|
if (model.buildCommand) {
|
|
907
1017
|
lines.push("", " - name: Build pipeline CLI", ` run: ${model.buildCommand}`);
|
|
908
1018
|
}
|
|
1019
|
+
renderTaskCacheRestoreSteps(lines, model, { kind: "task", id: preview.target, manifestPath: ".async/actions/cache/package-preview-cache-manifest.json" });
|
|
909
1020
|
renderRunActionStep(lines, "Run package preview target", `${model.command} github check && ${model.command} run-task ${shellWord(preview.target)}`, {});
|
|
1021
|
+
renderTaskCacheSaveSteps(lines, model, { kind: "task", id: preview.target, manifestPath: ".async/actions/cache/package-preview-cache-manifest.json" });
|
|
910
1022
|
lines.push("", " - name: Publish package preview", ` uses: ${ASYNC_PREVIEW_ACTION}`, " with:", ` package-path: ${JSON.stringify(preview.package)}`, ` target-registry: ${JSON.stringify(preview.registry)}`, ...(preview.namespace ? [` namespace: ${JSON.stringify(preview.namespace)}`] : []), " mode: pr", ` comment: ${preview.comment ? "true" : "false"}`, ` token-env-name: ${JSON.stringify(preview.tokenEnv)}`, " env:", " CI: true", ` ${preview.tokenEnv}: \${{ secrets.${preview.tokenEnv} }}`);
|
|
911
1023
|
renderEvidenceCollectStep(lines, model);
|
|
912
1024
|
lines.push("");
|
|
913
1025
|
}
|
|
914
1026
|
function renderBridgeJob(lines, model) {
|
|
915
1027
|
const bridge = model.bridge;
|
|
916
|
-
lines.push(` ${bridge.job}:`, ` name: ${bridge.job}`, ` if: ${renderBridgeCondition(bridge)}`, " runs-on: ubuntu-latest", " permissions:", " contents: write", " pull-requests: write", " concurrency:", " group: async-bridge-${{ github.repository }}", " cancel-in-progress: false", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, " with:", " persist-credentials: false", "", ...(model
|
|
917
|
-
? [
|
|
918
|
-
" - name: Restore task cache",
|
|
919
|
-
` uses: ${CACHE_ACTION}`,
|
|
920
|
-
" with:",
|
|
921
|
-
" path: .async/cache",
|
|
922
|
-
" key: async-pipeline-${{ runner.os }}-${{ github.sha }}",
|
|
923
|
-
" restore-keys: |",
|
|
924
|
-
" async-pipeline-${{ runner.os }}-",
|
|
925
|
-
""
|
|
926
|
-
]
|
|
927
|
-
: []), ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
1028
|
+
lines.push(` ${bridge.job}:`, ` name: ${bridge.job}`, ` if: ${renderBridgeCondition(bridge)}`, " runs-on: ubuntu-latest", " permissions:", " contents: write", " pull-requests: write", " concurrency:", " group: async-bridge-${{ github.repository }}", " cancel-in-progress: false", " steps:", " - name: Checkout", ` uses: ${CHECKOUT_ACTION}`, " with:", " persist-credentials: false", "", ...renderSetupSteps(model), ...renderDependencyInstallSteps(model));
|
|
928
1029
|
if (model.buildCommand) {
|
|
929
1030
|
lines.push("", " - name: Build pipeline CLI", ` run: ${model.buildCommand}`);
|
|
930
1031
|
}
|
|
@@ -967,6 +1068,10 @@ function renderEvidenceFanInJob(lines, model) {
|
|
|
967
1068
|
}
|
|
968
1069
|
function evidenceProducerJobIds(model) {
|
|
969
1070
|
const ids = new Set(model.jobs.map((job) => job.id));
|
|
1071
|
+
for (const sourceJob of model.sourceImpactJobs) {
|
|
1072
|
+
ids.add(sourceJob.planJob);
|
|
1073
|
+
ids.add(sourceJob.matrixJob);
|
|
1074
|
+
}
|
|
970
1075
|
if (model.pages.enabled)
|
|
971
1076
|
ids.add(model.pages.job);
|
|
972
1077
|
if (model.packagePreviews.enabled)
|