dorfl 0.14.2 → 0.15.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/dist/advance-ci-template.d.ts +17 -16
- package/dist/advance-ci-template.d.ts.map +1 -1
- package/dist/advance-ci-template.js +42 -53
- package/dist/advance-ci-template.js.map +1 -1
- package/dist/advance-drivers.d.ts +8 -0
- package/dist/advance-drivers.d.ts.map +1 -1
- package/dist/advance-drivers.js +28 -4
- package/dist/advance-drivers.js.map +1 -1
- package/dist/advance-isolated.d.ts.map +1 -1
- package/dist/advance-isolated.js +3 -2
- package/dist/advance-isolated.js.map +1 -1
- package/dist/advance-lifecycle-template.d.ts +41 -40
- package/dist/advance-lifecycle-template.d.ts.map +1 -1
- package/dist/advance-lifecycle-template.js +309 -489
- package/dist/advance-lifecycle-template.js.map +1 -1
- package/dist/advance-loop-driver.d.ts.map +1 -1
- package/dist/advance-loop-driver.js +3 -2
- package/dist/advance-loop-driver.js.map +1 -1
- package/dist/advance-treeless-publish.d.ts +31 -0
- package/dist/advance-treeless-publish.d.ts.map +1 -1
- package/dist/advance-treeless-publish.js +40 -0
- package/dist/advance-treeless-publish.js.map +1 -1
- package/dist/advance.d.ts +63 -3
- package/dist/advance.d.ts.map +1 -1
- package/dist/advance.js +80 -12
- package/dist/advance.js.map +1 -1
- package/dist/advancing-lock.d.ts +9 -0
- package/dist/advancing-lock.d.ts.map +1 -1
- package/dist/advancing-lock.js +21 -1
- package/dist/advancing-lock.js.map +1 -1
- package/dist/agent-env.d.ts +61 -0
- package/dist/agent-env.d.ts.map +1 -0
- package/dist/agent-env.js +144 -0
- package/dist/agent-env.js.map +1 -0
- package/dist/agent-stop.d.ts +35 -0
- package/dist/agent-stop.d.ts.map +1 -1
- package/dist/agent-stop.js +62 -0
- package/dist/agent-stop.js.map +1 -1
- package/dist/apply-merge-action.d.ts +76 -34
- package/dist/apply-merge-action.d.ts.map +1 -1
- package/dist/apply-merge-action.js +339 -129
- package/dist/apply-merge-action.js.map +1 -1
- package/dist/apply-stuck-action.js +2 -2
- package/dist/apply-stuck-action.js.map +1 -1
- package/dist/ci-agent-result.d.ts +151 -0
- package/dist/ci-agent-result.d.ts.map +1 -0
- package/dist/ci-agent-result.js +314 -0
- package/dist/ci-agent-result.js.map +1 -0
- package/dist/ci-handoff-apply.d.ts +162 -0
- package/dist/ci-handoff-apply.d.ts.map +1 -0
- package/dist/ci-handoff-apply.js +497 -0
- package/dist/ci-handoff-apply.js.map +1 -0
- package/dist/ci-handoff-format.d.ts +369 -0
- package/dist/ci-handoff-format.d.ts.map +1 -0
- package/dist/ci-handoff-format.js +646 -0
- package/dist/ci-handoff-format.js.map +1 -0
- package/dist/ci-handoff-lfs.d.ts +146 -0
- package/dist/ci-handoff-lfs.d.ts.map +1 -0
- package/dist/ci-handoff-lfs.js +397 -0
- package/dist/ci-handoff-lfs.js.map +1 -0
- package/dist/ci-handoff.d.ts +99 -0
- package/dist/ci-handoff.d.ts.map +1 -0
- package/dist/ci-handoff.js +277 -0
- package/dist/ci-handoff.js.map +1 -0
- package/dist/ci-lock-outputs.d.ts +93 -0
- package/dist/ci-lock-outputs.d.ts.map +1 -0
- package/dist/ci-lock-outputs.js +223 -0
- package/dist/ci-lock-outputs.js.map +1 -0
- package/dist/ci-phase-build.d.ts +178 -0
- package/dist/ci-phase-build.d.ts.map +1 -0
- package/dist/ci-phase-build.js +989 -0
- package/dist/ci-phase-build.js.map +1 -0
- package/dist/ci-phase-driver.d.ts +247 -0
- package/dist/ci-phase-driver.d.ts.map +1 -0
- package/dist/ci-phase-driver.js +370 -0
- package/dist/ci-phase-driver.js.map +1 -0
- package/dist/ci-phase-intake.d.ts +172 -0
- package/dist/ci-phase-intake.d.ts.map +1 -0
- package/dist/ci-phase-intake.js +555 -0
- package/dist/ci-phase-intake.js.map +1 -0
- package/dist/ci-phase-tasking.d.ts +205 -0
- package/dist/ci-phase-tasking.d.ts.map +1 -0
- package/dist/ci-phase-tasking.js +906 -0
- package/dist/ci-phase-tasking.js.map +1 -0
- package/dist/ci-phase-treeless.d.ts +224 -0
- package/dist/ci-phase-treeless.d.ts.map +1 -0
- package/dist/ci-phase-treeless.js +1482 -0
- package/dist/ci-phase-treeless.js.map +1 -0
- package/dist/ci-read-token.d.ts +47 -0
- package/dist/ci-read-token.d.ts.map +1 -0
- package/dist/ci-read-token.js +80 -0
- package/dist/ci-read-token.js.map +1 -0
- package/dist/cli.d.ts.map +1 -1
- package/dist/cli.js +447 -22
- package/dist/cli.js.map +1 -1
- package/dist/close-job-template.d.ts +10 -1
- package/dist/close-job-template.d.ts.map +1 -1
- package/dist/close-job-template.js +46 -14
- package/dist/close-job-template.js.map +1 -1
- package/dist/close-job.d.ts +20 -5
- package/dist/close-job.d.ts.map +1 -1
- package/dist/close-job.js +65 -12
- package/dist/close-job.js.map +1 -1
- package/dist/complete.d.ts.map +1 -1
- package/dist/complete.js +20 -1
- package/dist/complete.js.map +1 -1
- package/dist/config.d.ts +41 -44
- package/dist/config.d.ts.map +1 -1
- package/dist/config.js +2 -4
- package/dist/config.js.map +1 -1
- package/dist/continue-branch.d.ts +63 -15
- package/dist/continue-branch.d.ts.map +1 -1
- package/dist/continue-branch.js +75 -8
- package/dist/continue-branch.js.map +1 -1
- package/dist/crashed-run-save.d.ts +73 -0
- package/dist/crashed-run-save.d.ts.map +1 -0
- package/dist/crashed-run-save.js +86 -0
- package/dist/crashed-run-save.js.map +1 -0
- package/dist/cwd-section.d.ts.map +1 -1
- package/dist/cwd-section.js +10 -2
- package/dist/cwd-section.js.map +1 -1
- package/dist/decision-engine.d.ts +10 -0
- package/dist/decision-engine.d.ts.map +1 -1
- package/dist/decision-engine.js.map +1 -1
- package/dist/do-config.d.ts +2 -24
- package/dist/do-config.d.ts.map +1 -1
- package/dist/do-config.js +1 -33
- package/dist/do-config.js.map +1 -1
- package/dist/do.d.ts +149 -1
- package/dist/do.d.ts.map +1 -1
- package/dist/do.js +115 -25
- package/dist/do.js.map +1 -1
- package/dist/dorfl-item-template.d.ts +98 -0
- package/dist/dorfl-item-template.d.ts.map +1 -0
- package/dist/dorfl-item-template.js +504 -0
- package/dist/dorfl-item-template.js.map +1 -0
- package/dist/env-config.d.ts.map +1 -1
- package/dist/env-config.js +7 -5
- package/dist/env-config.js.map +1 -1
- package/dist/frontmatter.d.ts +52 -0
- package/dist/frontmatter.d.ts.map +1 -1
- package/dist/frontmatter.js +136 -2
- package/dist/frontmatter.js.map +1 -1
- package/dist/gc.d.ts +13 -0
- package/dist/gc.d.ts.map +1 -1
- package/dist/gc.js +16 -2
- package/dist/gc.js.map +1 -1
- package/dist/git.d.ts +18 -1
- package/dist/git.d.ts.map +1 -1
- package/dist/git.js +70 -5
- package/dist/git.js.map +1 -1
- package/dist/harness.d.ts +44 -5
- package/dist/harness.d.ts.map +1 -1
- package/dist/harness.js +24 -1
- package/dist/harness.js.map +1 -1
- package/dist/index.d.ts +11 -4
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +7 -3
- package/dist/index.js.map +1 -1
- package/dist/install-ci-action-pins.d.ts +123 -0
- package/dist/install-ci-action-pins.d.ts.map +1 -0
- package/dist/install-ci-action-pins.js +190 -0
- package/dist/install-ci-action-pins.js.map +1 -0
- package/dist/install-ci-actions-pr-creation.d.ts +70 -0
- package/dist/install-ci-actions-pr-creation.d.ts.map +1 -0
- package/dist/install-ci-actions-pr-creation.js +106 -0
- package/dist/install-ci-actions-pr-creation.js.map +1 -0
- package/dist/install-ci-capabilities/dorfl-item.d.ts +12 -0
- package/dist/install-ci-capabilities/dorfl-item.d.ts.map +1 -0
- package/dist/install-ci-capabilities/dorfl-item.js +26 -0
- package/dist/install-ci-capabilities/dorfl-item.js.map +1 -0
- package/dist/install-ci-core.d.ts +155 -37
- package/dist/install-ci-core.d.ts.map +1 -1
- package/dist/install-ci-core.js +304 -200
- package/dist/install-ci-core.js.map +1 -1
- package/dist/install-ci-github.d.ts +47 -2
- package/dist/install-ci-github.d.ts.map +1 -1
- package/dist/install-ci-github.js +113 -1
- package/dist/install-ci-github.js.map +1 -1
- package/dist/install-ci.d.ts +25 -6
- package/dist/install-ci.d.ts.map +1 -1
- package/dist/install-ci.js +72 -36
- package/dist/install-ci.js.map +1 -1
- package/dist/intake-marker.d.ts +6 -1
- package/dist/intake-marker.d.ts.map +1 -1
- package/dist/intake-marker.js.map +1 -1
- package/dist/intake-triage.d.ts.map +1 -1
- package/dist/intake-triage.js +6 -2
- package/dist/intake-triage.js.map +1 -1
- package/dist/intake-trigger-template.d.ts +31 -25
- package/dist/intake-trigger-template.d.ts.map +1 -1
- package/dist/intake-trigger-template.js +107 -292
- package/dist/intake-trigger-template.js.map +1 -1
- package/dist/intake.d.ts +186 -4
- package/dist/intake.d.ts.map +1 -1
- package/dist/intake.js +533 -416
- package/dist/intake.js.map +1 -1
- package/dist/integration-core.d.ts +154 -1
- package/dist/integration-core.d.ts.map +1 -1
- package/dist/integration-core.js +759 -486
- package/dist/integration-core.js.map +1 -1
- package/dist/integrator.d.ts +12 -0
- package/dist/integrator.d.ts.map +1 -1
- package/dist/integrator.js +21 -0
- package/dist/integrator.js.map +1 -1
- package/dist/isolation.d.ts +12 -2
- package/dist/isolation.d.ts.map +1 -1
- package/dist/isolation.js +19 -8
- package/dist/isolation.js.map +1 -1
- package/dist/item-lock.d.ts +93 -11
- package/dist/item-lock.d.ts.map +1 -1
- package/dist/item-lock.js +233 -65
- package/dist/item-lock.js.map +1 -1
- package/dist/land-question-residue.d.ts +58 -0
- package/dist/land-question-residue.d.ts.map +1 -0
- package/dist/land-question-residue.js +95 -0
- package/dist/land-question-residue.js.map +1 -0
- package/dist/lifecycle-gather.d.ts +16 -0
- package/dist/lifecycle-gather.d.ts.map +1 -1
- package/dist/lifecycle-gather.js +54 -6
- package/dist/lifecycle-gather.js.map +1 -1
- package/dist/merge-answer.d.ts +41 -0
- package/dist/merge-answer.d.ts.map +1 -0
- package/dist/merge-answer.js +60 -0
- package/dist/merge-answer.js.map +1 -0
- package/dist/merge-question-surfacer.d.ts +105 -30
- package/dist/merge-question-surfacer.d.ts.map +1 -1
- package/dist/merge-question-surfacer.js +202 -91
- package/dist/merge-question-surfacer.js.map +1 -1
- package/dist/merge-question-tick.d.ts +64 -0
- package/dist/merge-question-tick.d.ts.map +1 -0
- package/dist/merge-question-tick.js +96 -0
- package/dist/merge-question-tick.js.map +1 -0
- package/dist/mint-adr.d.ts.map +1 -1
- package/dist/mint-adr.js +20 -2
- package/dist/mint-adr.js.map +1 -1
- package/dist/needs-attention.d.ts +72 -2
- package/dist/needs-attention.d.ts.map +1 -1
- package/dist/needs-attention.js +275 -38
- package/dist/needs-attention.js.map +1 -1
- package/dist/phase-recorder.d.ts +168 -0
- package/dist/phase-recorder.d.ts.map +1 -0
- package/dist/phase-recorder.js +218 -0
- package/dist/phase-recorder.js.map +1 -0
- package/dist/phase.d.ts +94 -0
- package/dist/phase.d.ts.map +1 -0
- package/dist/phase.js +148 -0
- package/dist/phase.js.map +1 -0
- package/dist/pi-harness.d.ts.map +1 -1
- package/dist/pi-harness.js +19 -6
- package/dist/pi-harness.js.map +1 -1
- package/dist/prepare.d.ts.map +1 -1
- package/dist/prepare.js +4 -0
- package/dist/prepare.js.map +1 -1
- package/dist/prompt.d.ts +4 -0
- package/dist/prompt.d.ts.map +1 -1
- package/dist/prompt.js +48 -1
- package/dist/prompt.js.map +1 -1
- package/dist/protocol/CLAIM-PROTOCOL.md +3 -1
- package/dist/protocol/WORK-CONTRACT.md +1 -1
- package/dist/reap-branches.d.ts +48 -0
- package/dist/reap-branches.d.ts.map +1 -1
- package/dist/reap-branches.js +64 -0
- package/dist/reap-branches.js.map +1 -1
- package/dist/ref-write.d.ts +198 -0
- package/dist/ref-write.d.ts.map +1 -0
- package/dist/ref-write.js +79 -0
- package/dist/ref-write.js.map +1 -0
- package/dist/repo-config.js +8 -8
- package/dist/repo-config.js.map +1 -1
- package/dist/repo-mirror.d.ts +36 -0
- package/dist/repo-mirror.d.ts.map +1 -1
- package/dist/repo-mirror.js +91 -1
- package/dist/repo-mirror.js.map +1 -1
- package/dist/review-verdict.d.ts +1 -1
- package/dist/review-verdict.js +1 -1
- package/dist/run.d.ts +7 -1
- package/dist/run.d.ts.map +1 -1
- package/dist/run.js +28 -4
- package/dist/run.js.map +1 -1
- package/dist/scan.d.ts +19 -1
- package/dist/scan.d.ts.map +1 -1
- package/dist/scan.js +21 -8
- package/dist/scan.js.map +1 -1
- package/dist/sidecar.d.ts +14 -0
- package/dist/sidecar.d.ts.map +1 -1
- package/dist/sidecar.js +21 -0
- package/dist/sidecar.js.map +1 -1
- package/dist/single-job-warning.d.ts +63 -0
- package/dist/single-job-warning.d.ts.map +1 -0
- package/dist/single-job-warning.js +110 -0
- package/dist/single-job-warning.js.map +1 -0
- package/dist/skills/setup/protocol/CLAIM-PROTOCOL.md +3 -1
- package/dist/skills/setup/protocol/WORK-CONTRACT.md +1 -1
- package/dist/spec-complete.d.ts +13 -6
- package/dist/spec-complete.d.ts.map +1 -1
- package/dist/spec-complete.js +23 -11
- package/dist/spec-complete.js.map +1 -1
- package/dist/start.d.ts.map +1 -1
- package/dist/start.js +47 -14
- package/dist/start.js.map +1 -1
- package/dist/status.d.ts +12 -0
- package/dist/status.d.ts.map +1 -1
- package/dist/status.js +11 -3
- package/dist/status.js.map +1 -1
- package/dist/tasking.d.ts +246 -1
- package/dist/tasking.d.ts.map +1 -1
- package/dist/tasking.js +452 -358
- package/dist/tasking.js.map +1 -1
- package/dist/triage-persist.d.ts.map +1 -1
- package/dist/triage-persist.js +21 -2
- package/dist/triage-persist.js.map +1 -1
- package/dist/verify-workflow-template.d.ts.map +1 -1
- package/dist/verify-workflow-template.js +8 -4
- package/dist/verify-workflow-template.js.map +1 -1
- package/dist/verify.d.ts.map +1 -1
- package/dist/verify.js +4 -0
- package/dist/verify.js.map +1 -1
- package/dist/watch-session.d.ts +42 -13
- package/dist/watch-session.d.ts.map +1 -1
- package/dist/watch-session.js +52 -23
- package/dist/watch-session.js.map +1 -1
- package/dist/work-layout.d.ts +10 -4
- package/dist/work-layout.d.ts.map +1 -1
- package/dist/work-layout.js +11 -3
- package/dist/work-layout.js.map +1 -1
- package/dist/workspace.d.ts +65 -4
- package/dist/workspace.d.ts.map +1 -1
- package/dist/workspace.js +159 -11
- package/dist/workspace.js.map +1 -1
- package/package.json +1 -1
- package/src/advance-ci-template.ts +80 -81
- package/src/advance-drivers.ts +45 -6
- package/src/advance-isolated.ts +3 -5
- package/src/advance-lifecycle-template.ts +375 -538
- package/src/advance-loop-driver.ts +3 -5
- package/src/advance-treeless-publish.ts +61 -0
- package/src/advance.ts +145 -15
- package/src/advancing-lock.ts +36 -1
- package/src/agent-env.ts +174 -0
- package/src/agent-stop.ts +81 -0
- package/src/apply-merge-action.ts +401 -154
- package/src/apply-stuck-action.ts +2 -2
- package/src/ci-agent-result.ts +421 -0
- package/src/ci-handoff-apply.ts +737 -0
- package/src/ci-handoff-format.ts +926 -0
- package/src/ci-handoff-lfs.ts +519 -0
- package/src/ci-handoff.ts +392 -0
- package/src/ci-lock-outputs.ts +273 -0
- package/src/ci-phase-build.ts +1328 -0
- package/src/ci-phase-driver.ts +548 -0
- package/src/ci-phase-intake.ts +861 -0
- package/src/ci-phase-tasking.ts +1255 -0
- package/src/ci-phase-treeless.ts +2028 -0
- package/src/ci-read-token.ts +90 -0
- package/src/cli.ts +591 -29
- package/src/close-job-template.ts +52 -15
- package/src/close-job.ts +77 -12
- package/src/complete.ts +19 -1
- package/src/config.ts +43 -48
- package/src/continue-branch.ts +121 -16
- package/src/crashed-run-save.ts +162 -0
- package/src/cwd-section.ts +10 -0
- package/src/decision-engine.ts +10 -0
- package/src/do-config.ts +2 -57
- package/src/do.ts +170 -28
- package/src/dorfl-item-template.ts +556 -0
- package/src/env-config.ts +7 -5
- package/src/frontmatter.ts +155 -2
- package/src/gc.ts +27 -2
- package/src/git.ts +83 -5
- package/src/harness.ts +58 -6
- package/src/index.ts +50 -1
- package/src/install-ci-action-pins.ts +209 -0
- package/src/install-ci-actions-pr-creation.ts +186 -0
- package/src/install-ci-capabilities/dorfl-item.ts +33 -0
- package/src/install-ci-core.ts +388 -233
- package/src/install-ci-github.ts +149 -1
- package/src/install-ci.ts +83 -37
- package/src/intake-marker.ts +6 -1
- package/src/intake-triage.ts +7 -3
- package/src/intake-trigger-template.ts +126 -327
- package/src/intake.ts +725 -544
- package/src/integration-core.ts +975 -523
- package/src/integrator.ts +28 -0
- package/src/isolation.ts +29 -10
- package/src/item-lock.ts +308 -92
- package/src/land-question-residue.ts +122 -0
- package/src/lifecycle-gather.ts +82 -10
- package/src/merge-answer.ts +82 -0
- package/src/merge-question-surfacer.ts +320 -112
- package/src/merge-question-tick.ts +182 -0
- package/src/mint-adr.ts +28 -2
- package/src/needs-attention.ts +350 -42
- package/src/phase-recorder.ts +350 -0
- package/src/phase.ts +177 -0
- package/src/pi-harness.ts +25 -5
- package/src/prepare.ts +4 -0
- package/src/prompt.ts +68 -1
- package/src/reap-branches.ts +107 -0
- package/src/ref-write.ts +324 -0
- package/src/repo-config.ts +8 -8
- package/src/repo-mirror.ts +107 -1
- package/src/review-verdict.ts +1 -1
- package/src/run.ts +47 -6
- package/src/scan.ts +37 -7
- package/src/sidecar.ts +36 -0
- package/src/single-job-warning.ts +135 -0
- package/src/spec-complete.ts +36 -14
- package/src/start.ts +55 -16
- package/src/status.ts +29 -3
- package/src/tasking.ts +593 -378
- package/src/triage-persist.ts +24 -2
- package/src/verify-workflow-template.ts +8 -4
- package/src/verify.ts +4 -0
- package/src/watch-session.ts +86 -28
- package/src/work-layout.ts +11 -3
- package/src/workspace.ts +210 -14
|
@@ -0,0 +1,209 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The ONE table of third-party GitHub Actions that `dorfl install-ci` emits,
|
|
3
|
+
* each pinned to a full 40-character commit SHA, plus the regeneration rule that
|
|
4
|
+
* keeps a consumer repository's own pins.
|
|
5
|
+
*
|
|
6
|
+
* WHY SHAS. A tag such as `@v5` can be moved by whoever controls the action's
|
|
7
|
+
* repository, so a compromised or careless release reaches every job that uses
|
|
8
|
+
* it, including dorfl's jobs that hold `contents: write` and a provider API key.
|
|
9
|
+
* A commit SHA cannot move. This is GitHub's own hardening guidance ("Using
|
|
10
|
+
* third-party actions", Security hardening for GitHub Actions).
|
|
11
|
+
*
|
|
12
|
+
* THE FORMAT. Every reference is emitted as `owner/repo@<40-hex-sha> # vX.Y.Z`,
|
|
13
|
+
* the exact shape Dependabot writes and rewrites, so a consumer that enables
|
|
14
|
+
* Dependabot's `github-actions` ecosystem (with `/.github/actions/*` in
|
|
15
|
+
* `directories` for the composite action) gets reviewable bump PRs.
|
|
16
|
+
*
|
|
17
|
+
* HOW EACH SHA WAS RESOLVED (2026-09-26). For each tag:
|
|
18
|
+
* 1. `git ls-remote --tags https://github.com/<owner>/<repo>.git`, reading the
|
|
19
|
+
* peeled `refs/tags/<tag>^{}` line when the tag is annotated (that line is
|
|
20
|
+
* the COMMIT; the unpeeled line is the tag OBJECT and must never be used),
|
|
21
|
+
* else the tag line itself for a lightweight tag;
|
|
22
|
+
* 2. cross-checked with `gh api repos/<owner>/<repo>/git/ref/tags/<tag>`
|
|
23
|
+
* (object type) and `gh api repos/<owner>/<repo>/commits/<sha>` (the SHA is
|
|
24
|
+
* a commit that exists in the repository).
|
|
25
|
+
*
|
|
26
|
+
* actions/checkout v7.0.1: lightweight tag, `git/ref` type `commit`,
|
|
27
|
+
* 3d3c42e5aac5ba805825da76410c181273ba90b1 ("prep v7.0.1 release (#2531)").
|
|
28
|
+
* The floating `v7` tag points at the same commit. v7 is the current major
|
|
29
|
+
* and adds the fork-PR checkout block for `pull_request_target` /
|
|
30
|
+
* `workflow_run` that v5 lacks; the inputs dorfl passes (`fetch-depth`,
|
|
31
|
+
* `persist-credentials`) are unchanged.
|
|
32
|
+
* actions/setup-node v7.0.0: lightweight tag, `git/ref` type `commit`,
|
|
33
|
+
* 820762786026740c76f36085b0efc47a31fe5020 ("Migrate to ESM and upgrade
|
|
34
|
+
* dependencies (#1574)"). The floating `v7` tag points at the same commit.
|
|
35
|
+
* `node-version` and `package-manager-cache` are still inputs (see the
|
|
36
|
+
* comment on the setup-node step in `generateSetupAction`).
|
|
37
|
+
* pnpm/action-setup v6.1.0: ANNOTATED tag. `git/ref` type `tag` with object
|
|
38
|
+
* d9184bf108216479bc5a137cc391f4d7b14c870b (the tag object, NOT usable);
|
|
39
|
+
* peeled `^{}` and `gh api .../git/tags/d9184bf...` both give the commit
|
|
40
|
+
* ea17c68df8912ef543352723c149a84f56e3d413 ("feat: support pnpm v12
|
|
41
|
+
* (#288)"). With no `version` input it still reads `packageManager` from
|
|
42
|
+
* package.json, which is how dorfl's workspace mode uses it.
|
|
43
|
+
*
|
|
44
|
+
* Resolved 2026-09-28 (task `ci-split-generate-workflows`, the split CI item
|
|
45
|
+
* workflow's handoff artifact; the spec requires at least v4.1.3 of both):
|
|
46
|
+
* actions/upload-artifact v7.0.1: lightweight tag (`ls-remote` lists no
|
|
47
|
+
* peeled `^{}` line for it), `git/ref` type `commit`,
|
|
48
|
+
* 043fb46d1a93c77aae656e7c1c64a875d1fc6a0a ("Merge pull request #797 from
|
|
49
|
+
* actions/yacaovsnc/update-dependency"). The floating `v7` tag points at
|
|
50
|
+
* the same commit. The inputs dorfl passes (`name`, `path`,
|
|
51
|
+
* `retention-days`, `if-no-files-found`) are unchanged since v4.
|
|
52
|
+
* actions/download-artifact v8.0.1: lightweight tag, `git/ref` type
|
|
53
|
+
* `commit`, 3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c ("Add regression tests
|
|
54
|
+
* for CJK characters (#471)"). The floating `v8` tag points at the same
|
|
55
|
+
* commit. v8 fails on a digest mismatch by default (`digest-mismatch:
|
|
56
|
+
* error`), which is what the apply job wants; dorfl passes `name` and
|
|
57
|
+
* `path` and never `run-id` (the apply job reads its OWN run's artifact).
|
|
58
|
+
*
|
|
59
|
+
* To bump a pin: resolve the new tag the same way, update the entry, and record
|
|
60
|
+
* the resolution above. `install-ci-actions-pinned.test.ts` fails on any
|
|
61
|
+
* emitted `uses:` that is not a full SHA.
|
|
62
|
+
*/
|
|
63
|
+
|
|
64
|
+
/** One pinned third-party action. */
|
|
65
|
+
export interface ActionPin {
|
|
66
|
+
/** `owner/repo`, or `owner/repo/path` for an action in a subdirectory. */
|
|
67
|
+
readonly action: string;
|
|
68
|
+
/** The full 40-character lowercase commit SHA (never a tag object). */
|
|
69
|
+
readonly sha: string;
|
|
70
|
+
/** The release tag the SHA was resolved from, for the trailing comment. */
|
|
71
|
+
readonly version: string;
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/** Every third-party action dorfl's generators emit. */
|
|
75
|
+
export const ACTION_PINS = {
|
|
76
|
+
checkout: {
|
|
77
|
+
action: 'actions/checkout',
|
|
78
|
+
sha: '3d3c42e5aac5ba805825da76410c181273ba90b1',
|
|
79
|
+
version: 'v7.0.1',
|
|
80
|
+
},
|
|
81
|
+
setupNode: {
|
|
82
|
+
action: 'actions/setup-node',
|
|
83
|
+
sha: '820762786026740c76f36085b0efc47a31fe5020',
|
|
84
|
+
version: 'v7.0.0',
|
|
85
|
+
},
|
|
86
|
+
pnpmSetup: {
|
|
87
|
+
action: 'pnpm/action-setup',
|
|
88
|
+
sha: 'ea17c68df8912ef543352723c149a84f56e3d413',
|
|
89
|
+
version: 'v6.1.0',
|
|
90
|
+
},
|
|
91
|
+
uploadArtifact: {
|
|
92
|
+
action: 'actions/upload-artifact',
|
|
93
|
+
sha: '043fb46d1a93c77aae656e7c1c64a875d1fc6a0a',
|
|
94
|
+
version: 'v7.0.1',
|
|
95
|
+
},
|
|
96
|
+
downloadArtifact: {
|
|
97
|
+
action: 'actions/download-artifact',
|
|
98
|
+
sha: '3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c',
|
|
99
|
+
version: 'v8.0.1',
|
|
100
|
+
},
|
|
101
|
+
} as const satisfies Record<string, ActionPin>;
|
|
102
|
+
|
|
103
|
+
/**
|
|
104
|
+
* The value to put after `uses: ` for a pinned action:
|
|
105
|
+
* `owner/repo@<sha> # vX.Y.Z` (the Dependabot format).
|
|
106
|
+
*/
|
|
107
|
+
export function pinnedUses(pin: ActionPin): string {
|
|
108
|
+
return `${pin.action}@${pin.sha} # ${pin.version}`;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
// ─── preserving the consumer's pins on regeneration ─────────────────────────
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* A `uses:` step line: the prefix (indentation, an optional `- ` list marker,
|
|
115
|
+
* `uses:` and its spacing), then the VALUE (an optionally quoted
|
|
116
|
+
* `action@ref`), then an optional trailing comment. Matched per line, which is
|
|
117
|
+
* how both dorfl's templates and Dependabot write these lines. A flow-style or
|
|
118
|
+
* multi-line `uses:` value is not recognised (and so not preserved).
|
|
119
|
+
*/
|
|
120
|
+
const USES_LINE =
|
|
121
|
+
/^(\s*(?:-\s+)?uses:[ \t]+)((['"]?)([^\s'"@#]+)@([^\s'"#]+)\3(?:[ \t]+#.*)?)[ \t]*$/;
|
|
122
|
+
|
|
123
|
+
/**
|
|
124
|
+
* A full commit SHA (what counts as "pinned"). Case-insensitive here, so a
|
|
125
|
+
* consumer's uppercase pin is still kept; dorfl itself emits lowercase.
|
|
126
|
+
*/
|
|
127
|
+
const FULL_SHA = /^[0-9a-f]{40}$/i;
|
|
128
|
+
|
|
129
|
+
/** The lookup key for an action: GitHub treats `owner/repo` case-insensitively. */
|
|
130
|
+
function actionKey(action: string): string {
|
|
131
|
+
return action.toLowerCase();
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/** A remote action key (`owner/repo[/path]`), i.e. not `./local` nor `docker://`. */
|
|
135
|
+
function isRemoteAction(action: string): boolean {
|
|
136
|
+
return !action.startsWith('.') && /^[^/\s]+\/[^/\s]+/.test(action);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Parse the version out of a trailing `# v1.2.3` comment, for ranking. */
|
|
140
|
+
function versionOf(tail: string): number[] | undefined {
|
|
141
|
+
const m = /#\s*v?(\d+(?:\.\d+)*)/.exec(tail);
|
|
142
|
+
return m ? m[1].split('.').map(Number) : undefined;
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/** Compare two parsed versions; a missing version ranks lowest. */
|
|
146
|
+
function compareVersions(a?: number[], b?: number[]): number {
|
|
147
|
+
if (!a || !b) return (a ? 1 : 0) - (b ? 1 : 0);
|
|
148
|
+
for (let i = 0; i < Math.max(a.length, b.length); i++) {
|
|
149
|
+
const d = (a[i] ?? 0) - (b[i] ?? 0);
|
|
150
|
+
if (d !== 0) return d;
|
|
151
|
+
}
|
|
152
|
+
return 0;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/**
|
|
156
|
+
* Collect the SHA pins an existing file already carries, keyed by action
|
|
157
|
+
* (`owner/repo[/path]`). The value is the whole `uses:` value with its trailing
|
|
158
|
+
* comment, exactly as written. When the file pins the same action to several
|
|
159
|
+
* different SHAs, the one whose `# vX.Y.Z` comment is highest wins (ties: the
|
|
160
|
+
* first in the file), so a partial manual bump is never undone.
|
|
161
|
+
*/
|
|
162
|
+
export function existingPins(existing: string): Map<string, string> {
|
|
163
|
+
const pins = new Map<string, string>();
|
|
164
|
+
// `\r?\n`: a CRLF checkout (Windows, `core.autocrlf`) must not hide its pins,
|
|
165
|
+
// or dorfl's default would silently replace them.
|
|
166
|
+
for (const line of existing.split(/\r?\n/)) {
|
|
167
|
+
const m = USES_LINE.exec(line);
|
|
168
|
+
if (!m) continue;
|
|
169
|
+
const [, , value, , action, ref] = m;
|
|
170
|
+
if (!isRemoteAction(action) || !FULL_SHA.test(ref)) continue;
|
|
171
|
+
const key = actionKey(action);
|
|
172
|
+
const current = pins.get(key);
|
|
173
|
+
if (
|
|
174
|
+
current === undefined ||
|
|
175
|
+
compareVersions(versionOf(value), versionOf(current)) > 0
|
|
176
|
+
) {
|
|
177
|
+
pins.set(key, value);
|
|
178
|
+
}
|
|
179
|
+
}
|
|
180
|
+
return pins;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
/**
|
|
184
|
+
* Rewrite a freshly generated workflow / composite action so every `uses:` of an
|
|
185
|
+
* action that `existing` already pins to a full SHA keeps the consumer's
|
|
186
|
+
* reference and trailing comment. Matching is by action (`owner/repo`, plus the
|
|
187
|
+
* path for an action in a subdirectory), never by position. An existing TAG is
|
|
188
|
+
* not a pin: the generated reference (dorfl's SHA) stays. So the result never
|
|
189
|
+
* swaps a SHA for a tag. Only the value after `uses: ` changes; indentation and
|
|
190
|
+
* every other line are the generated ones.
|
|
191
|
+
*/
|
|
192
|
+
export function preserveExistingPins(
|
|
193
|
+
generated: string,
|
|
194
|
+
existing: string | undefined,
|
|
195
|
+
): string {
|
|
196
|
+
if (existing === undefined) return generated;
|
|
197
|
+
const pins = existingPins(existing);
|
|
198
|
+
if (pins.size === 0) return generated;
|
|
199
|
+
return generated
|
|
200
|
+
.split('\n')
|
|
201
|
+
.map((line) => {
|
|
202
|
+
const m = USES_LINE.exec(line);
|
|
203
|
+
if (!m) return line;
|
|
204
|
+
const [, prefix, , , action] = m;
|
|
205
|
+
const kept = pins.get(actionKey(action));
|
|
206
|
+
return kept === undefined ? line : `${prefix}${kept}`;
|
|
207
|
+
})
|
|
208
|
+
.join('\n');
|
|
209
|
+
}
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The "Allow GitHub Actions to create and approve pull requests" STEP for
|
|
3
|
+
* `install-ci` (task `install-ci-enables-actions-pr-creation`). A new GitHub
|
|
4
|
+
* repository has this setting OFF (`GET repos/<r>/actions/permissions/workflow`
|
|
5
|
+
* answers `can_approve_pull_request_reviews: false`), and with it off GitHub
|
|
6
|
+
* refuses every pull request the generated workflows try to open with the
|
|
7
|
+
* ambient `GITHUB_TOKEN`, so propose mode silently opens no PR. dorfl's own
|
|
8
|
+
* repository has it on, which is why this was only found in a fresh sandbox.
|
|
9
|
+
*
|
|
10
|
+
* BEHAVIOUR (mirrors the Tier-1 branch-protection step,
|
|
11
|
+
* `install-ci-branch-protection.ts`: auto-when-admin, instruct-otherwise):
|
|
12
|
+
*
|
|
13
|
+
* - READ the setting ({@link CIProviderContext.getActionsWorkflowPermissions}).
|
|
14
|
+
* Already on: leave it alone and say so.
|
|
15
|
+
* - Off (or unreadable) and the credential is repo-admin-scoped: SET it
|
|
16
|
+
* ({@link CIProviderContext.setActionsWorkflowPermissions}) with
|
|
17
|
+
* `can_approve_pull_request_reviews: true`, carrying the
|
|
18
|
+
* `default_workflow_permissions` value that was read so the GITHUB_TOKEN's
|
|
19
|
+
* default scope is preserved, never widened or narrowed.
|
|
20
|
+
* - Not admin (or unknown): never call the API; print the exact `gh api`
|
|
21
|
+
* command and the settings page instead. A rejected PUT is `failed` and
|
|
22
|
+
* prints the same command to retry by hand.
|
|
23
|
+
* - `--fake`: report what would be checked, call nothing.
|
|
24
|
+
*
|
|
25
|
+
* Never throws: a failure is reported in the result and the log, so install-ci's
|
|
26
|
+
* later steps still run.
|
|
27
|
+
*/
|
|
28
|
+
|
|
29
|
+
import type {
|
|
30
|
+
ActionsWorkflowPermissions,
|
|
31
|
+
CIProviderContext,
|
|
32
|
+
} from './install-ci-core.js';
|
|
33
|
+
|
|
34
|
+
export type {ActionsWorkflowPermissions};
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* The exact `gh api` command a user runs to turn the setting on by hand. When
|
|
38
|
+
* the current `default_workflow_permissions` is known it is passed back
|
|
39
|
+
* unchanged; when it is not, the field is omitted, which leaves it as it is on
|
|
40
|
+
* GitHub (both PUT fields are optional). Pure string, no I/O.
|
|
41
|
+
*/
|
|
42
|
+
export function formatManualActionsPrCreationCommand(
|
|
43
|
+
repo: string,
|
|
44
|
+
defaultWorkflowPermissions?: string,
|
|
45
|
+
): string {
|
|
46
|
+
const keep =
|
|
47
|
+
defaultWorkflowPermissions !== undefined
|
|
48
|
+
? ` -f default_workflow_permissions=${defaultWorkflowPermissions}`
|
|
49
|
+
: '';
|
|
50
|
+
return (
|
|
51
|
+
`gh api -X PUT repos/${repo}/actions/permissions/workflow ` +
|
|
52
|
+
`-F can_approve_pull_request_reviews=true${keep}`
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Options for {@link installCIActionsPrCreationStep}. */
|
|
57
|
+
export interface ActionsPrCreationStepOptions {
|
|
58
|
+
/** The CI-provider seam (GitHub adapter in production; stub in tests). */
|
|
59
|
+
ctx: CIProviderContext;
|
|
60
|
+
/** Snapshot mode: never call the live API. */
|
|
61
|
+
fake?: boolean;
|
|
62
|
+
/** Sink for human-facing progress lines. */
|
|
63
|
+
log: (line: string) => void;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** The outcome of one actions-PR-creation step run. */
|
|
67
|
+
export interface ActionsPrCreationStepResult {
|
|
68
|
+
/**
|
|
69
|
+
* - `already-enabled`: the setting was on; nothing was changed;
|
|
70
|
+
* - `set`: it was off (or unreadable), admin scope, the PUT succeeded;
|
|
71
|
+
* - `failed`: admin scope, the PUT was rejected (detail in `error`);
|
|
72
|
+
* - `instructed`: not admin (or unknown), the manual command was printed;
|
|
73
|
+
* - `skipped-no-repo`: the provider context has no `repo` known;
|
|
74
|
+
* - `skipped-no-seam`: the provider does not implement the seam;
|
|
75
|
+
* - `skipped-fake`: `--fake` snapshot mode (no real API touched).
|
|
76
|
+
*/
|
|
77
|
+
status:
|
|
78
|
+
| 'already-enabled'
|
|
79
|
+
| 'set'
|
|
80
|
+
| 'failed'
|
|
81
|
+
| 'instructed'
|
|
82
|
+
| 'skipped-no-repo'
|
|
83
|
+
| 'skipped-no-seam'
|
|
84
|
+
| 'skipped-fake';
|
|
85
|
+
/** The permissions read before any change (`undefined` when unreadable). */
|
|
86
|
+
before?: ActionsWorkflowPermissions;
|
|
87
|
+
/** The detected admin-scope verdict (`true`/`false`/`undefined`=unknown). */
|
|
88
|
+
adminScope?: boolean;
|
|
89
|
+
/** The failure detail when `status === 'failed'`. */
|
|
90
|
+
error?: string;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
const LABEL = 'actions PR creation';
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Check, and when off and permitted, enable the repository setting that lets
|
|
97
|
+
* GitHub Actions create pull requests (see the module header).
|
|
98
|
+
*/
|
|
99
|
+
export async function installCIActionsPrCreationStep(
|
|
100
|
+
options: ActionsPrCreationStepOptions,
|
|
101
|
+
): Promise<ActionsPrCreationStepResult> {
|
|
102
|
+
const {ctx, log, fake} = options;
|
|
103
|
+
|
|
104
|
+
if (fake) {
|
|
105
|
+
log(
|
|
106
|
+
`${LABEL}: skipped (--fake; would check that "Allow GitHub Actions to create and approve pull requests" is on, and enable it if not)`,
|
|
107
|
+
);
|
|
108
|
+
return {status: 'skipped-fake'};
|
|
109
|
+
}
|
|
110
|
+
if (
|
|
111
|
+
!ctx.getActionsWorkflowPermissions ||
|
|
112
|
+
!ctx.setActionsWorkflowPermissions
|
|
113
|
+
) {
|
|
114
|
+
log(
|
|
115
|
+
`${LABEL}: skipped (provider does not implement the seam, non-GitHub host).`,
|
|
116
|
+
);
|
|
117
|
+
return {status: 'skipped-no-seam'};
|
|
118
|
+
}
|
|
119
|
+
if (!ctx.repo) {
|
|
120
|
+
log(`${LABEL}: skipped (repo unknown, pass --repo to enable).`);
|
|
121
|
+
return {status: 'skipped-no-repo'};
|
|
122
|
+
}
|
|
123
|
+
const repo = ctx.repo;
|
|
124
|
+
|
|
125
|
+
const before = await ctx
|
|
126
|
+
.getActionsWorkflowPermissions()
|
|
127
|
+
.catch(() => undefined);
|
|
128
|
+
if (before?.canApprovePullRequestReviews === true) {
|
|
129
|
+
log(
|
|
130
|
+
`${LABEL}: "Allow GitHub Actions to create and approve pull requests" is already on for ${repo}.`,
|
|
131
|
+
);
|
|
132
|
+
return {status: 'already-enabled', before};
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
const keep = before?.defaultWorkflowPermissions;
|
|
136
|
+
const adminScope = ctx.getRepoAdminScope
|
|
137
|
+
? await ctx.getRepoAdminScope().catch(() => undefined)
|
|
138
|
+
: undefined;
|
|
139
|
+
|
|
140
|
+
if (adminScope !== true) {
|
|
141
|
+
log(
|
|
142
|
+
before === undefined
|
|
143
|
+
? `${LABEL}: could not read "Allow GitHub Actions to create and approve pull requests" for ${repo}, and no admin-scoped credential was detected.`
|
|
144
|
+
: `${LABEL}: "Allow GitHub Actions to create and approve pull requests" is OFF for ${repo}, and no admin-scoped credential was detected.`,
|
|
145
|
+
);
|
|
146
|
+
log(
|
|
147
|
+
' With it off, propose mode opens no pull request (GitHub refuses PRs created with GITHUB_TOKEN).',
|
|
148
|
+
);
|
|
149
|
+
log(
|
|
150
|
+
' install-ci will NOT call the GitHub API. Run this yourself (admin token required):',
|
|
151
|
+
);
|
|
152
|
+
log(` ${formatManualActionsPrCreationCommand(repo, keep)}`);
|
|
153
|
+
log(
|
|
154
|
+
` Manual UI fallback: https://github.com/${repo}/settings/actions ` +
|
|
155
|
+
'→ "Workflow permissions" → tick "Allow GitHub Actions to create and approve pull requests" → Save.',
|
|
156
|
+
);
|
|
157
|
+
return {status: 'instructed', before, adminScope};
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
try {
|
|
161
|
+
// Carry the default scope through unchanged; when it could not be read,
|
|
162
|
+
// omit it so GitHub keeps whatever it is.
|
|
163
|
+
await ctx.setActionsWorkflowPermissions({
|
|
164
|
+
canApprovePullRequestReviews: true,
|
|
165
|
+
...(keep !== undefined ? {defaultWorkflowPermissions: keep} : {}),
|
|
166
|
+
});
|
|
167
|
+
} catch (err) {
|
|
168
|
+
const error = err instanceof Error ? err.message : String(err);
|
|
169
|
+
log(`${LABEL}: FAILED (${error})`);
|
|
170
|
+
log(
|
|
171
|
+
' With it off, propose mode opens no pull request (GitHub refuses PRs created with GITHUB_TOKEN).',
|
|
172
|
+
);
|
|
173
|
+
log(
|
|
174
|
+
` Retry by hand (admin token required):\n` +
|
|
175
|
+
` ${formatManualActionsPrCreationCommand(repo, keep)}`,
|
|
176
|
+
);
|
|
177
|
+
return {status: 'failed', before, adminScope, error};
|
|
178
|
+
}
|
|
179
|
+
log(
|
|
180
|
+
`${LABEL}: enabled "Allow GitHub Actions to create and approve pull requests" on ${repo}` +
|
|
181
|
+
(keep !== undefined
|
|
182
|
+
? ` (default_workflow_permissions kept as ${keep}).`
|
|
183
|
+
: '.'),
|
|
184
|
+
);
|
|
185
|
+
return {status: 'set', before, adminScope};
|
|
186
|
+
}
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The SPLIT CI ITEM capability emitter (spec `ci-agent-job-without-write-token`,
|
|
3
|
+
* task `ci-split-generate-workflows`). A SELF-REGISTERING module (see
|
|
4
|
+
* `example-noop.ts`): it emits `dorfl-item.yml` (the lock, agent and apply jobs
|
|
5
|
+
* of ONE item) and `dorfl-item-dispatch.yml` (the per-item dispatch wrapper the
|
|
6
|
+
* advance tick starts), which the advance-lifecycle and intake workflows call.
|
|
7
|
+
*
|
|
8
|
+
* The workflow text lives in `dorfl-item-template.ts`; this file is the thin
|
|
9
|
+
* registry-wiring shim.
|
|
10
|
+
*/
|
|
11
|
+
|
|
12
|
+
import {registerCapability} from '../install-ci-core.js';
|
|
13
|
+
import {
|
|
14
|
+
ITEM_DISPATCH_WORKFLOW_PATH,
|
|
15
|
+
ITEM_WORKFLOW_PATH,
|
|
16
|
+
generateItemDispatchWorkflow,
|
|
17
|
+
generateItemWorkflow,
|
|
18
|
+
} from '../dorfl-item-template.js';
|
|
19
|
+
|
|
20
|
+
registerCapability({
|
|
21
|
+
id: 'dorfl-item',
|
|
22
|
+
label:
|
|
23
|
+
'Run each CI item as lock, agent and apply jobs (the per-item workflow the advance tick and intake call)',
|
|
24
|
+
emit(config) {
|
|
25
|
+
return [
|
|
26
|
+
{path: ITEM_WORKFLOW_PATH, content: generateItemWorkflow(config)},
|
|
27
|
+
{
|
|
28
|
+
path: ITEM_DISPATCH_WORKFLOW_PATH,
|
|
29
|
+
content: generateItemDispatchWorkflow(config),
|
|
30
|
+
},
|
|
31
|
+
];
|
|
32
|
+
},
|
|
33
|
+
});
|