@joshuafolkken/kit 1.1178.0 → 1.1240.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/.claude/.claude-plugin/plugin.json +1 -1
- package/.claude/settings.json +2 -2
- package/.claude/skills/dependency-update/SKILL.md +4 -8
- package/.claude/skills/epic-commands/SKILL.md +2 -2
- package/.claude/skills/epic-commands/epic-bundle.md +1 -1
- package/.claude/skills/workflow-commands/SKILL.md +2 -4
- package/.claude/skills/workflow-commands/backlogrun-child.md +6 -4
- package/.claude/skills/workflow-commands/backlogrun-lanes.md +7 -3
- package/.claude/skills/workflow-commands/backlogrun-park.md +23 -2
- package/.claude/skills/workflow-commands/backlogrun-progress.md +9 -4
- package/.claude/skills/workflow-commands/backlogrun-recovery.md +4 -3
- package/.claude/skills/workflow-commands/backlogrun-steps.md +21 -13
- package/.claude/skills/workflow-commands/chain-rule.md +1 -1
- package/.claude/skills/workflow-commands/delegation.md +2 -2
- package/.claude/skills/workflow-commands/followup.md +10 -0
- package/.claude/skills/workflow-commands/fullrun-steps.md +1 -1
- package/.claude/skills/workflow-commands/halfrun.md +1 -1
- package/.claude/skills/workflow-commands/issue-scout.md +1 -1
- package/.claude/skills/workflow-commands/kickoff.md +2 -1
- package/.claude/skills/workflow-commands/latest-gate.md +1 -1
- package/.claude/skills/workflow-commands/observation-ledger.md +3 -3
- package/.claude/skills/workflow-commands/pre-gate-cut.md +6 -1
- package/.claude/skills/workflow-commands/prerequisite.md +2 -2
- package/.claude/skills/workflow-commands/progress-watcher.md +1 -1
- package/.claude/skills/workflow-commands/working-tree-hold.md +1 -1
- package/.codex/hooks.json +2 -2
- package/.cursorrules +6 -41
- package/.github/actions/setup-node/action.yml +1 -1
- package/.github/workflows/auto-tag.yml +3 -4
- package/.github/workflows/ci.yml +32 -31
- package/.github/workflows/dependabot-auto-merge.yml +13 -6
- package/.github/workflows/publish.yml +50 -4
- package/.github/workflows/sonar-qube.yml +3 -0
- package/.pnpmfile.mjs +4 -3
- package/.prettierignore +8 -2
- package/CLAUDE.md +18 -20
- package/README.md +44 -38
- package/SECURITY.md +15 -1
- package/dist/CLAUDE.md +18 -20
- package/dist/commands/document-section-cli.js +734 -0
- package/dist/hooks/audit-provision.js +346 -0
- package/dist/hooks/batch-guard.js +5 -4
- package/dist/hooks/{chunk-G6KRCKEC.js → chunk-4TZVB44A.js} +496 -378
- package/dist/hooks/chunk-5VNDSNG3.js +12 -0
- package/dist/hooks/chunk-CDY4NFQG.js +15 -0
- package/dist/hooks/chunk-ELCN5LCH.js +135 -0
- package/dist/hooks/{chunk-VQLLNFNT.js → chunk-F3ELQ2AF.js} +3 -3
- package/dist/hooks/{chunk-I4XSL4TT.js → chunk-GPTFMK5E.js} +1 -1
- package/dist/hooks/{chunk-3V37IID6.js → chunk-JBBMAPC4.js} +23 -14
- package/dist/hooks/{chunk-UAGLLIGZ.js → chunk-KHGB5S3Y.js} +7 -6
- package/dist/hooks/{chunk-GDS5N2KB.js → chunk-KHX4U3BG.js} +735 -683
- package/dist/hooks/{chunk-VESKRQOF.js → chunk-KRBERBBI.js} +3 -3
- package/dist/hooks/{chunk-DO6NWWHU.js → chunk-QVSWU24O.js} +4 -4
- package/dist/hooks/{chunk-S6X6WYKR.js → chunk-WK2XSHSI.js} +1643 -923
- package/dist/hooks/{chunk-ZAL3NRGL.js → chunk-XZYKEM6J.js} +25 -10
- package/dist/hooks/{chunk-32GQTL4F.js → chunk-ZQGD4GBY.js} +4 -2
- package/dist/hooks/codex-hook-adapter.js +14 -12
- package/dist/hooks/duplicate-read-guard.js +6 -5
- package/dist/hooks/format-edited.js +4 -3
- package/dist/hooks/inputs.json +203 -179
- package/dist/hooks/investigation-guard.js +6 -5
- package/dist/hooks/pretool-guard.js +12 -10
- package/dist/hooks/session-lang.js +2 -4
- package/dist/hooks/stop-guard.js +66 -72
- package/dist/josh.js +1088 -78
- package/dist/version/index.d.ts +7 -2
- package/dist/version/index.js +152 -75
- package/eslint/base.js +23 -1
- package/eslint/rules/code-quality.js +3 -0
- package/eslint/rules/early-return-one-liner.js +98 -0
- package/eslint/rules/unicorn.js +16 -1
- package/package.json +7 -6
- package/pnpm-workspace.yaml +9 -4
- package/prettier/basic.d.ts +3 -1
- package/prettier/index.d.ts +3 -1
- package/prettier/static.d.ts +3 -1
- package/prompts/coding-standards.md +1 -1
- package/prompts/collaboration-workflow/file-edits.md +4 -4
- package/prompts/collaboration-workflow/glossary.md +1 -1
- package/prompts/collaboration-workflow/issue-template.md +1 -0
- package/prompts/collaboration-workflow/operating-rules.md +17 -9
- package/prompts/collaboration-workflow/overview.md +2 -2
- package/prompts/collaboration-workflow/principles.md +14 -12
- package/prompts/collaboration-workflow/report-format.md +1 -1
- package/prompts/collaboration-workflow/residency.md +1 -1
- package/prompts/collaboration-workflow/rule-delivery.md +6 -6
- package/prompts/collaboration-workflow/shell-body.md +6 -6
- package/prompts/collaboration-workflow/turn-batching.md +1 -1
- package/prompts/collaboration-workflow/upstream-interrupt.md +12 -2
- package/prompts/collaboration-workflow/wip-cap.md +10 -10
- package/prompts/review-rubric.md +1 -2
- package/prompts/review.md +1 -1
- package/scripts/agent/agent-headless.ts +29 -0
- package/scripts/agent/agent-launch-environment.ts +6 -2
- package/scripts/agent/codex-agent-argv.ts +2 -1
- package/scripts/auto-ok/auto-ok-cli.ts +1 -1
- package/scripts/backlog/backlog-arrival.ts +3 -3
- package/scripts/backlog/backlog-budget.ts +1 -1
- package/scripts/backlog/backlog-drive-args.ts +135 -0
- package/scripts/backlog/backlog-drive-cli.ts +11 -137
- package/scripts/backlog/backlog-drive-launch.ts +39 -0
- package/scripts/backlog/backlog-drive-named-offer.ts +3 -5
- package/scripts/backlog/backlog-drive-named.ts +3 -3
- package/scripts/backlog/backlog-drive-offer-argv.ts +1 -1
- package/scripts/backlog/backlog-drive-owner.ts +1 -1
- package/scripts/backlog/backlog-drive-restore.ts +1 -1
- package/scripts/backlog/backlog-drive-retrospective.ts +1 -1
- package/scripts/backlog/backlog-drive.ts +1 -1
- package/scripts/backlog/backlog-next.ts +16 -5
- package/scripts/backlog/backlog-offer-cli.ts +6 -6
- package/scripts/backlog/backlog-offer.ts +1 -1
- package/scripts/backlog/backlog-plan-cli.ts +1 -1
- package/scripts/backlog/backlog-pool.ts +57 -11
- package/scripts/backlog/backlog-ready.ts +1 -1
- package/scripts/backlog/backlog-scope.ts +1 -1
- package/scripts/backlog/backlog-stalled-detect.ts +2 -2
- package/scripts/backlog/backlog-stalled.ts +1 -1
- package/scripts/build/build-codex-hooks.ts +3 -1
- package/scripts/build/build-commands.ts +66 -0
- package/scripts/build/build-hooks.ts +2 -1
- package/scripts/build/build-library.ts +12 -3
- package/scripts/build/build.ts +70 -0
- package/scripts/build/local-bin.ts +3 -13
- package/scripts/delegation/investigation-reads.ts +3 -1
- package/scripts/discovery/repo-origin.ts +5 -5
- package/scripts/doctor/doctor-consumer.ts +2 -2
- package/scripts/doctor/doctor-io.ts +2 -3
- package/scripts/document/capped-print-part.ts +30 -0
- package/scripts/document/capped-print.ts +3 -4
- package/scripts/document/document-byte-budget.ts +11 -2
- package/scripts/document/document-scan.ts +117 -0
- package/scripts/document/document-section.ts +143 -16
- package/scripts/document/entry-read-budget.ts +5 -3
- package/scripts/document/entry-read-set.ts +10 -89
- package/scripts/document/read-set-cost.ts +98 -0
- package/scripts/epic/epic-add-body.ts +1 -0
- package/scripts/epic/epic-audit-checks.ts +1 -1
- package/scripts/epic/epic-audit-rationale.ts +4 -3
- package/scripts/epic/epic-bundle-cli.ts +14 -3
- package/scripts/epic/epic-bundle-poll.ts +41 -0
- package/scripts/epic/epic-busy.ts +1 -1
- package/scripts/epic/epic-candidate-confirm.ts +3 -2
- package/scripts/epic/epic-classify.ts +1 -1
- package/scripts/epic/epic-cli-argv.ts +78 -46
- package/scripts/epic/epic-cli-remove.ts +11 -19
- package/scripts/epic/epic-cli.ts +74 -73
- package/scripts/epic/epic-close-comment.ts +1 -1
- package/scripts/epic/epic-close.ts +1 -1
- package/scripts/epic/epic-cross-repo.ts +5 -5
- package/scripts/epic/epic-graph.ts +1 -1
- package/scripts/epic/epic-index.ts +1 -1
- package/scripts/epic/epic-issue.ts +2 -6
- package/scripts/epic/epic-lane-offer.ts +2 -2
- package/scripts/epic/epic-next.ts +29 -42
- package/scripts/epic/epic-solo-stale.ts +1 -1
- package/scripts/epic/epic-triage.ts +8 -4
- package/scripts/epic/epic-validate.ts +1 -1
- package/scripts/followup/git-followup-finish.ts +2 -2
- package/scripts/followup/git-followup-label.ts +2 -2
- package/scripts/followup/git-followup-merged.ts +2 -2
- package/scripts/followup/git-pr-followup-checks.ts +142 -0
- package/scripts/followup/git-pr-followup.ts +4 -129
- package/scripts/gate/core-admission.ts +162 -0
- package/scripts/gate/core-budget.ts +190 -68
- package/scripts/gate/gate-plan.ts +80 -7
- package/scripts/gate/gate-report.ts +28 -9
- package/scripts/gate/gate-skip.ts +38 -7
- package/scripts/gate/hook-gate-reuse.ts +12 -10
- package/scripts/gate/machine-capacity.ts +175 -0
- package/scripts/gate/pre-commit-type-check.ts +4 -1
- package/scripts/gate/project-checks.ts +6 -9
- package/scripts/gate/type-check-step.ts +2 -3
- package/scripts/gate/verification-gate.ts +34 -10
- package/scripts/gh/git-gh-api-path.ts +2 -2
- package/scripts/gh/git-gh-issue-rest.ts +3 -3
- package/scripts/gh/git-gh-issue-write.ts +53 -7
- package/scripts/gh/git-gh-pr-read.ts +64 -19
- package/scripts/gh/git-gh-pr-rest.ts +1 -1
- package/scripts/gh/git-gh-pr.ts +5 -1
- package/scripts/gh/git-pr-ai-review.ts +1 -1
- package/scripts/gh/git-pr-checks-eval.ts +1 -3
- package/scripts/gh/git-pr-checks-parse.ts +1 -1
- package/scripts/gh/git-pr-checks-watch.ts +6 -5
- package/scripts/gh/git-pr-checks.ts +7 -5
- package/scripts/gh/git-pr-coderabbit.ts +1 -1
- package/scripts/gh/git-pr-error.ts +1 -3
- package/scripts/gh/git-pr.ts +9 -13
- package/scripts/gh/github-issue-url.ts +2 -2
- package/scripts/git/constants.ts +2 -6
- package/scripts/git/git-command.ts +20 -218
- package/scripts/git/git-diff-reads.ts +225 -0
- package/scripts/git/git-error.ts +4 -12
- package/scripts/git/git-pre-push-hook.ts +139 -0
- package/scripts/git/git-prompt.ts +2 -6
- package/scripts/git/git-spawn-sync.ts +3 -1
- package/scripts/git/git-spawn.ts +1 -1
- package/scripts/git/git-staging.ts +4 -6
- package/scripts/git/git-status.ts +3 -9
- package/scripts/hooks/codex-hook-adapter.ts +1 -1
- package/scripts/hooks/edited-cspell.ts +2 -4
- package/scripts/hooks/format-edited-file.ts +3 -12
- package/scripts/hooks/pre-push-unit.ts +31 -9
- package/scripts/hooks/reserved-run.ts +2 -2
- package/scripts/hooks/run-hook.sh +5 -4
- package/scripts/hooks/stop-guard.ts +5 -3
- package/scripts/init/init-actions.ts +0 -5
- package/scripts/init/init-ai-copy.ts +13 -2
- package/scripts/init/init-logic-json-merge.ts +19 -0
- package/scripts/init/init-logic-secretlint.ts +7 -7
- package/scripts/init/init-logic-workspace.ts +41 -2
- package/scripts/init/init-logic.ts +16 -26
- package/scripts/init/init.ts +10 -16
- package/scripts/init/initial-commit.ts +4 -4
- package/scripts/init/kit-development-versions.ts +40 -0
- package/scripts/init/kit-setup-state.ts +2 -2
- package/scripts/init/package-manager-pin.ts +35 -0
- package/scripts/init/start-exec.ts +68 -19
- package/scripts/init/start-setup-pr.ts +9 -9
- package/scripts/init/start-steps.ts +6 -10
- package/scripts/issue/defect-rate-cli.ts +1 -1
- package/scripts/issue/git-next-issues.ts +1 -1
- package/scripts/issue/issue-auto-ok.ts +32 -7
- package/scripts/issue/issue-backlinks-cli.ts +2 -2
- package/scripts/issue/issue-backlinks.ts +9 -9
- package/scripts/issue/issue-comment-cli.ts +19 -13
- package/scripts/issue/issue-file-cli.ts +72 -22
- package/scripts/issue/issue-file.ts +29 -1
- package/scripts/issue/issue-labels.ts +13 -0
- package/scripts/issue/issue-release-cli.ts +155 -0
- package/scripts/issue/issue-release.ts +58 -0
- package/scripts/issue/issue-scout-closed.ts +1 -1
- package/scripts/issue/issue-state-cli.ts +30 -74
- package/scripts/josh/josh-command-map.ts +2 -0
- package/scripts/josh/josh-command-types.ts +14 -0
- package/scripts/josh/josh-commands-ai.ts +4 -4
- package/scripts/josh/josh-commands-backlog.ts +4 -1
- package/scripts/josh/josh-commands-development.ts +18 -0
- package/scripts/josh/josh-commands-document.ts +1 -0
- package/scripts/josh/josh-commands-issue.ts +8 -1
- package/scripts/josh/josh-commands-lane.ts +24 -1
- package/scripts/josh/josh-commands-maintenance.ts +6 -6
- package/scripts/josh/josh-commands-run.ts +12 -7
- package/scripts/josh/josh-in-process.ts +28 -9
- package/scripts/josh/josh-logic.ts +34 -15
- package/scripts/josh/process-identity.ts +1 -3
- package/scripts/josh/session-language-cli.ts +3 -4
- package/scripts/lane/lane-await.ts +5 -1
- package/scripts/lane/lane-child-invocation.ts +2 -2
- package/scripts/lane/lane-cli.ts +12 -4
- package/scripts/lane/lane-dispatch.ts +14 -10
- package/scripts/lane/lane-environment.ts +5 -1
- package/scripts/lane/lane-handoff.ts +1 -1
- package/scripts/lane/lane-install.ts +2 -13
- package/scripts/lane/lane-launch-cli.ts +15 -1
- package/scripts/lane/lane-ledger.ts +145 -0
- package/scripts/lane/lane-leftover.ts +44 -30
- package/scripts/lane/lane-load.ts +108 -0
- package/scripts/lane/lane-measure-cli.ts +149 -0
- package/scripts/lane/lane-occupancy.ts +3 -0
- package/scripts/lane/lane-open.ts +5 -5
- package/scripts/lane/lane-paths.ts +13 -0
- package/scripts/lane/lane-reap.ts +1 -3
- package/scripts/lane/lane-registry.ts +11 -0
- package/scripts/lane/lane-relaunch.ts +1 -1
- package/scripts/lane/lane-stats.ts +224 -0
- package/scripts/lane/lane-stray.ts +140 -0
- package/scripts/lane/lane-vacant.ts +36 -0
- package/scripts/lane/openai-lane-ship.ts +2 -2
- package/scripts/lane/openai-lane-supervisor-owner.ts +180 -0
- package/scripts/lane/openai-lane-supervisor.ts +7 -165
- package/scripts/lane/openai-review-broker.ts +2 -4
- package/scripts/lib/ancestor-directories.ts +35 -0
- package/scripts/lib/animation-helpers.ts +2 -2
- package/scripts/lib/buffered-process.ts +3 -7
- package/scripts/lib/cli-flags.ts +71 -1
- package/scripts/lib/spawn-exit.ts +2 -2
- package/scripts/lib/timeouts.ts +35 -2
- package/scripts/lines/line-budget.ts +4 -6
- package/scripts/notify/telegram-test.ts +6 -0
- package/scripts/observations/observation-ledger-line.ts +6 -6
- package/scripts/observations/observation-ledger-migrate.ts +55 -4
- package/scripts/observations/observation-ledger.ts +26 -7
- package/scripts/overrides/lockfile-overrides.ts +1 -1
- package/scripts/overrides/overrides-logic.ts +1 -1
- package/scripts/propagate/propagate-git.ts +1 -1
- package/scripts/propagate/propagate-publish.ts +2 -2
- package/scripts/propagate/propagate-steps.ts +20 -15
- package/scripts/propagate/propagate.ts +36 -27
- package/scripts/refactor/import-graph.ts +18 -1
- package/scripts/refactor/refactor-lint.ts +4 -4
- package/scripts/release/github-release-environment.ts +1 -0
- package/scripts/release/github-release-runs.ts +115 -0
- package/scripts/release/github-release.ts +13 -67
- package/scripts/repo/repository-labels.ts +2 -2
- package/scripts/repo/required-checks-logic.ts +1 -3
- package/scripts/review/live-evidence.ts +2 -4
- package/scripts/review/review-brief-cli.ts +1 -1
- package/scripts/review/review-finding-ledger.ts +1 -1
- package/scripts/review/review-findings-cli.ts +1 -3
- package/scripts/rules/decision-oracle-batch.ts +128 -0
- package/scripts/rules/decision-oracle-tokens.ts +21 -0
- package/scripts/rules/decision-oracle.ts +8 -120
- package/scripts/rules/delivered-rules.ts +26 -182
- package/scripts/rules/direct-filing.ts +1 -1
- package/scripts/rules/early-heartbeat.ts +3 -3
- package/scripts/rules/git-argv.ts +1 -1
- package/scripts/rules/git-force.ts +1 -1
- package/scripts/rules/implementation-cut.ts +2 -2
- package/scripts/rules/index-guard.ts +1 -1
- package/scripts/rules/issue-comments.ts +118 -0
- package/scripts/rules/{issue-fold.ts → issue-fold-rule.ts} +2 -2
- package/scripts/rules/lane-background.ts +2 -2
- package/scripts/rules/lane-park.ts +18 -0
- package/scripts/rules/lane-split-park.ts +128 -0
- package/scripts/rules/oracle-firing.ts +1 -1
- package/scripts/rules/poll-loop.ts +1 -1
- package/scripts/rules/pre-gate-cut.ts +1 -1
- package/scripts/rules/protected-files.ts +59 -4
- package/scripts/rules/{run-tail.ts → run-tail-rule.ts} +2 -2
- package/scripts/rules/shell-body-trigger.ts +1 -1
- package/scripts/rules/stop-rules.ts +18 -7
- package/scripts/rules/test-declared-commit.ts +4 -4
- package/scripts/rules/wip-cap.ts +63 -0
- package/scripts/run/{run-carry-args.ts → carry/run-carry-args.ts} +37 -13
- package/scripts/run/{run-carry-change.ts → carry/run-carry-change.ts} +29 -7
- package/scripts/run/{run-carry-cli.ts → carry/run-carry-cli.ts} +4 -6
- package/scripts/run/{run-carry.ts → carry/run-carry.ts} +1 -1
- package/scripts/run/{run-cut-args.ts → cut/run-cut-args.ts} +1 -1
- package/scripts/run/{run-cut-cli.ts → cut/run-cut-cli.ts} +18 -5
- package/scripts/run/{run-cut.ts → cut/run-cut.ts} +1 -1
- package/scripts/run/{run-entry-cli.ts → entry/run-entry-cli.ts} +11 -11
- package/scripts/run/{run-entry.ts → entry/run-entry.ts} +2 -2
- package/scripts/run/{run-event-render.ts → event/run-event-render.ts} +1 -0
- package/scripts/run/{run-event-scope.ts → event/run-event-scope.ts} +1 -1
- package/scripts/run/{run-event-stream-emit.ts → event/run-event-stream-emit.ts} +1 -1
- package/scripts/run/{run-event-stream.ts → event/run-event-stream.ts} +20 -2
- package/scripts/run/{run-hold-cli.ts → hold/run-hold-cli.ts} +5 -5
- package/scripts/run/{run-merge-cli.ts → merge/run-merge-cli.ts} +10 -5
- package/scripts/run/{run-merge-steps.ts → merge/run-merge-steps.ts} +53 -5
- package/scripts/run/{run-merge.ts → merge/run-merge.ts} +1 -1
- package/scripts/run/{run-progress-cli.ts → progress/run-progress-cli.ts} +1 -1
- package/scripts/run/{run-progress-config.ts → progress/run-progress-config.ts} +2 -17
- package/scripts/run/{run-progress-read.ts → progress/run-progress-read.ts} +5 -5
- package/scripts/run/{run-progress.ts → progress/run-progress.ts} +1 -1
- package/scripts/run/{run-review-steps.ts → review/run-review-steps.ts} +2 -3
- package/scripts/run/run-ending.ts +2 -2
- package/scripts/run/run-halfrun-resume.ts +6 -1
- package/scripts/run/run-headless.ts +1 -1
- package/scripts/run/run-invocation.ts +85 -84
- package/scripts/run/run-liveness.ts +1 -1
- package/scripts/run/run-parent-cut-hook.ts +1 -1
- package/scripts/run/run-preflight.ts +6 -6
- package/scripts/run/run-prep-cli.ts +1 -1
- package/scripts/run/run-prrun-resume.ts +1 -1
- package/scripts/run/run-report-cli.ts +4 -4
- package/scripts/run/run-report.ts +2 -2
- package/scripts/run/run-status-cli.ts +1 -1
- package/scripts/run/run-step-cli.ts +3 -3
- package/scripts/run/run-step.ts +7 -4
- package/scripts/run/run-stop-notify.ts +1 -1
- package/scripts/run/run-stranded-detect.ts +4 -4
- package/scripts/run/run-stranded.ts +2 -2
- package/scripts/run/run-watcher-guard-cli.ts +1 -1
- package/scripts/run/run-watcher-guard.ts +5 -3
- package/scripts/run/run-watcher-hook.ts +1 -1
- package/scripts/run/{run-ship-cli.ts → ship/run-ship-cli.ts} +2 -2
- package/scripts/run/{run-ship-detach.ts → ship/run-ship-detach.ts} +3 -3
- package/scripts/run/ship/run-ship-lock.ts +50 -0
- package/scripts/run/{run-ship-preflight.ts → ship/run-ship-preflight.ts} +6 -2
- package/scripts/run/{run-ship-probe.ts → ship/run-ship-probe.ts} +2 -2
- package/scripts/run/{run-ship-return.ts → ship/run-ship-return.ts} +4 -3
- package/scripts/run/{run-ship-review-steps.ts → ship/run-ship-review-steps.ts} +3 -3
- package/scripts/run/{run-ship-scoped.ts → ship/run-ship-scoped.ts} +10 -1
- package/scripts/run/{run-ship-steps.ts → ship/run-ship-steps.ts} +11 -12
- package/scripts/run/ship/run-ship-stop-text.ts +13 -0
- package/scripts/run/{run-ship-sync.ts → ship/run-ship-sync.ts} +25 -11
- package/scripts/run/{run-tidy-lanes.ts → tidy/run-tidy-lanes.ts} +1 -1
- package/scripts/run/{run-wake-cli.ts → wake/run-wake-cli.ts} +25 -11
- package/scripts/run/{run-wake-describe.ts → wake/run-wake-describe.ts} +58 -7
- package/scripts/run/{run-wake-driver.ts → wake/run-wake-driver.ts} +59 -8
- package/scripts/run/{run-wake-handoff.ts → wake/run-wake-handoff.ts} +1 -1
- package/scripts/run/{run-wake-loop.ts → wake/run-wake-loop.ts} +13 -15
- package/scripts/run/wake/run-wake-restart.ts +58 -0
- package/scripts/run/{run-wake-session.ts → wake/run-wake-session.ts} +3 -3
- package/scripts/run/{run-wake.ts → wake/run-wake.ts} +16 -65
- package/scripts/safe-chain/preinstall-command.ts +20 -21
- package/scripts/safe-chain/project-config.ts +19 -15
- package/scripts/sync/sync-ai-files.ts +1 -1
- package/scripts/sync/sync-configs.ts +2 -1
- package/scripts/sync/sync.ts +15 -2
- package/scripts/test/unit-worker-share.ts +1 -0
- package/scripts/time-runtime/time-background.ts +5 -1
- package/scripts/time-runtime/time-instant.ts +3 -1
- package/scripts/time-runtime/time-spans.ts +6 -139
- package/scripts/time-runtime/time-tool-call.ts +160 -0
- package/scripts/version/effective-upstream.ts +2 -20
- package/scripts/version/latest-corepack.ts +124 -93
- package/scripts/version/latest-regression.ts +1 -1
- package/scripts/version/latest-update.ts +4 -4
- package/scripts/version/npm-registry.ts +20 -5
- package/scripts/version/publishable-range-check.ts +2 -2
- package/scripts/version/publishable-range.ts +1 -1
- package/scripts/version/release-age.ts +48 -38
- package/scripts/version/release-hold.ts +1 -1
- package/scripts/version/safe-chain-version-update.ts +40 -0
- package/scripts/version/upgrade-shell-command.ts +48 -8
- package/scripts/version/version-check-logic.ts +23 -14
- package/scripts/version/version-commands.ts +23 -11
- package/scripts/version/version-remote.ts +28 -13
- package/templates/pnpm-workspace.basic.yaml +4 -0
- package/templates/workflows/dependabot-auto-merge.yml +5 -5
- package/templates/workflows/github-release.yml +6 -0
- package/scripts/build/build-config-merge.ts +0 -27
- package/scripts/build/build-managed-marker.ts +0 -27
- package/scripts/build/build-self-sync-guard.ts +0 -27
- package/scripts/build/build-version.ts +0 -26
- package/scripts/version/preinstall-version-update.ts +0 -86
- /package/scripts/git/{schemas.ts → git-schemas.ts} +0 -0
- /package/scripts/overrides/{schemas.ts → overrides-schemas.ts} +0 -0
- /package/scripts/run/{run-carry-conversation.ts → carry/run-carry-conversation.ts} +0 -0
- /package/scripts/run/{run-carry-reclaim.ts → carry/run-carry-reclaim.ts} +0 -0
- /package/scripts/run/{run-carry-stash.ts → carry/run-carry-stash.ts} +0 -0
- /package/scripts/run/{run-carry-streak.ts → carry/run-carry-streak.ts} +0 -0
- /package/scripts/run/{run-cut-handoff.ts → cut/run-cut-handoff.ts} +0 -0
- /package/scripts/run/{run-cut-report.ts → cut/run-cut-report.ts} +0 -0
- /package/scripts/run/{run-entry-stop.ts → entry/run-entry-stop.ts} +0 -0
- /package/scripts/run/{run-event-cli.ts → event/run-event-cli.ts} +0 -0
- /package/scripts/run/{run-event-follow.ts → event/run-event-follow.ts} +0 -0
- /package/scripts/run/{run-event-watch.ts → event/run-event-watch.ts} +0 -0
- /package/scripts/run/{run-hold.ts → hold/run-hold.ts} +0 -0
- /package/scripts/run/{run-progress-args.ts → progress/run-progress-args.ts} +0 -0
- /package/scripts/run/{run-progress-clock.ts → progress/run-progress-clock.ts} +0 -0
- /package/scripts/run/{run-review-cli.ts → review/run-review-cli.ts} +0 -0
- /package/scripts/run/{run-review.ts → review/run-review.ts} +0 -0
- /package/scripts/run/{run-ship-next.ts → ship/run-ship-next.ts} +0 -0
- /package/scripts/run/{run-ship-pre-detach.ts → ship/run-ship-pre-detach.ts} +0 -0
- /package/scripts/run/{run-ship-review.ts → ship/run-ship-review.ts} +0 -0
- /package/scripts/run/{run-ship-stage.ts → ship/run-ship-stage.ts} +0 -0
- /package/scripts/run/{run-ship.ts → ship/run-ship.ts} +0 -0
- /package/scripts/run/{run-tidy-cli.ts → tidy/run-tidy-cli.ts} +0 -0
- /package/scripts/run/{run-tidy-stashes.ts → tidy/run-tidy-stashes.ts} +0 -0
- /package/scripts/run/{run-tidy.ts → tidy/run-tidy.ts} +0 -0
|
@@ -6,13 +6,19 @@ import { git_gh_repo } from '#scripts/gh/git-gh-repo'
|
|
|
6
6
|
import { github_issue_url, type IssueUrlTarget } from '#scripts/gh/github-issue-url'
|
|
7
7
|
import { git_error } from '#scripts/git/git-error'
|
|
8
8
|
import { josh_environment_file } from '#scripts/josh/josh-environment-file'
|
|
9
|
+
import { cli_flags } from '#scripts/lib/cli-flags'
|
|
9
10
|
import { telegram_notify } from './telegram-notify'
|
|
10
11
|
import { telegram_test_logic, type CliValues, type ResolvedContext } from './telegram-test-logic'
|
|
11
12
|
|
|
12
13
|
const REPO_NAME_SEPARATOR = '/'
|
|
14
|
+
const ARGV_OFFSET = 2
|
|
15
|
+
|
|
16
|
+
// The free-text values a caller writes, often opening with a Markdown `-` bullet.
|
|
17
|
+
const FREE_TEXT_FLAGS = ['--body', '--issue-title']
|
|
13
18
|
|
|
14
19
|
function parse_cli_arguments(): CliValues {
|
|
15
20
|
const { values } = parseArgs({
|
|
21
|
+
args: [...cli_flags.attach_values(process.argv.slice(ARGV_OFFSET), FREE_TEXT_FLAGS)],
|
|
16
22
|
options: {
|
|
17
23
|
'task-type': { type: 'string' },
|
|
18
24
|
'repo-name': { type: 'string' },
|
|
@@ -34,16 +34,16 @@ function slug_of(first_field: string): string {
|
|
|
34
34
|
|
|
35
35
|
// The `?? ''` lives in these small helpers rather than in `field_reason`, so that function stays a
|
|
36
36
|
// single lookup with no branching of its own.
|
|
37
|
-
function is_present(field: string | undefined): boolean {
|
|
38
|
-
return
|
|
37
|
+
function is_present(field: string | undefined = ''): boolean {
|
|
38
|
+
return field.trim().length > 0
|
|
39
39
|
}
|
|
40
40
|
|
|
41
|
-
function matches(pattern: RegExp, field: string | undefined): boolean {
|
|
42
|
-
return pattern.test(field
|
|
41
|
+
function matches(pattern: RegExp, field: string | undefined = ''): boolean {
|
|
42
|
+
return pattern.test(field)
|
|
43
43
|
}
|
|
44
44
|
|
|
45
|
-
function slug_ok(key_field: string | undefined): boolean {
|
|
46
|
-
return SLUG_PATTERN.test(slug_of(key_field
|
|
45
|
+
function slug_ok(key_field: string | undefined = ''): boolean {
|
|
46
|
+
return SLUG_PATTERN.test(slug_of(key_field))
|
|
47
47
|
}
|
|
48
48
|
|
|
49
49
|
// Each field's check paired with the reason it fails, so `field_reason` is a single lookup for the
|
|
@@ -1,8 +1,18 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import {
|
|
2
|
+
appendFileSync,
|
|
3
|
+
mkdirSync,
|
|
4
|
+
readdirSync,
|
|
5
|
+
readFileSync,
|
|
6
|
+
renameSync,
|
|
7
|
+
rmdirSync,
|
|
8
|
+
rmSync,
|
|
9
|
+
} from 'node:fs'
|
|
2
10
|
import path from 'node:path'
|
|
3
11
|
import { file_reader } from '#scripts/lib/read-file'
|
|
4
12
|
import {
|
|
13
|
+
LEDGER_FILE_EXTENSION,
|
|
5
14
|
LEGACY_LEDGER_FILE,
|
|
15
|
+
LEGACY_OBSERVATION_LEDGER_DIRECTORY,
|
|
6
16
|
LEGACY_OBSERVATION_LEDGER_PATHS,
|
|
7
17
|
MIGRATION_CLAIM_SUFFIX,
|
|
8
18
|
OBSERVATION_LEDGER_DIRECTORY,
|
|
@@ -136,14 +146,55 @@ function migrate_one(legacy: string, target: string): boolean {
|
|
|
136
146
|
return true
|
|
137
147
|
}
|
|
138
148
|
|
|
149
|
+
// The ledger file a name in the old directory belongs to — a claim `<name>.md.<pid>.migrating` left by
|
|
150
|
+
// a killed process included, whose `<name>.md` may already be gone.
|
|
151
|
+
function source_name(name: string): string | undefined {
|
|
152
|
+
const end = name.indexOf(LEDGER_FILE_EXTENSION)
|
|
153
|
+
|
|
154
|
+
return end === -1 ? undefined : name.slice(0, end + LEDGER_FILE_EXTENSION.length)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
function legacy_directory_names(directory: string): ReadonlyArray<string> {
|
|
158
|
+
try {
|
|
159
|
+
const names = readdirSync(directory).map((name) => source_name(name))
|
|
160
|
+
|
|
161
|
+
return [...new Set(names.filter((name) => name !== undefined))]
|
|
162
|
+
} catch {
|
|
163
|
+
return []
|
|
164
|
+
}
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
// **The old directory moves file by file into the same names** (joshuafolkken/kit#3341), so an
|
|
168
|
+
// issue's lines stay in that issue's file; the emptied directory is removed when nothing else is left.
|
|
169
|
+
function migrate_directory(root: string): boolean {
|
|
170
|
+
const legacy_directory = path.join(root, LEGACY_OBSERVATION_LEDGER_DIRECTORY)
|
|
171
|
+
const did_move = legacy_directory_names(legacy_directory)
|
|
172
|
+
.map((name) =>
|
|
173
|
+
migrate_one(
|
|
174
|
+
path.join(legacy_directory, name),
|
|
175
|
+
path.join(root, OBSERVATION_LEDGER_DIRECTORY, name),
|
|
176
|
+
),
|
|
177
|
+
)
|
|
178
|
+
.some(Boolean)
|
|
179
|
+
|
|
180
|
+
try {
|
|
181
|
+
rmdirSync(legacy_directory)
|
|
182
|
+
} catch {
|
|
183
|
+
// Absent, or still holding something that is not the ledger's — left where it is.
|
|
184
|
+
}
|
|
185
|
+
|
|
186
|
+
return did_move
|
|
187
|
+
}
|
|
188
|
+
|
|
139
189
|
// Returns whether anything was moved. `root` is the checkout the ledger lives in. Every old path is
|
|
140
190
|
// tried — `map` before `some`, so one that moved does not leave the next one where it was.
|
|
141
191
|
function migrate(root: string): boolean {
|
|
142
192
|
const target = path.join(root, OBSERVATION_LEDGER_DIRECTORY, LEGACY_LEDGER_FILE)
|
|
143
|
-
|
|
144
|
-
return LEGACY_OBSERVATION_LEDGER_PATHS.map((legacy) =>
|
|
193
|
+
const moved_files = LEGACY_OBSERVATION_LEDGER_PATHS.map((legacy) =>
|
|
145
194
|
migrate_one(path.join(root, legacy), target),
|
|
146
|
-
)
|
|
195
|
+
)
|
|
196
|
+
|
|
197
|
+
return [...moved_files, migrate_directory(root)].some(Boolean)
|
|
147
198
|
}
|
|
148
199
|
|
|
149
200
|
const observation_ledger_migrate = { migrate }
|
|
@@ -4,14 +4,24 @@
|
|
|
4
4
|
// tree, and `observations-flush.ts` commits what no run carried — so a literal in each would be three
|
|
5
5
|
// answers to one question, and a rename reaching only some of them would silently split them.
|
|
6
6
|
//
|
|
7
|
-
// **It sits
|
|
7
|
+
// **It sits outside `docs/`** (joshuafolkken/kit#3341): its files are machine-written data lines, not
|
|
8
|
+
// documents a person reads, and at about forty new files a day they were most of `docs/`.
|
|
8
9
|
//
|
|
9
10
|
// **It is a directory of one file per issue, not one file** (joshuafolkken/kit#2919). One file every
|
|
10
11
|
// run appended to made two lanes' pull requests conflict at its tail, so lanes parked their lines in
|
|
11
12
|
// the primary checkout for a batch-end flush — and another run's stash took them from there, and the
|
|
12
13
|
// flush never saw them. A run now appends to `<issue>.md` in its own work tree and merges it with its
|
|
13
14
|
// own pull request: parallel lanes write different files, so nothing conflicts and nothing waits.
|
|
14
|
-
const OBSERVATION_LEDGER_DIRECTORY = '
|
|
15
|
+
const OBSERVATION_LEDGER_DIRECTORY = '.josh/observations'
|
|
16
|
+
|
|
17
|
+
// Where the directory lived until joshuafolkken/kit#3341, a migration source for the same reasons as
|
|
18
|
+
// the single-file ledgers below: each `<name>.md` in it moves to the same name in the directory.
|
|
19
|
+
const LEGACY_OBSERVATION_LEDGER_DIRECTORY = 'docs/maintainers/observations'
|
|
20
|
+
|
|
21
|
+
const LEDGER_DIRECTORIES: ReadonlyArray<string> = [
|
|
22
|
+
OBSERVATION_LEDGER_DIRECTORY,
|
|
23
|
+
LEGACY_OBSERVATION_LEDGER_DIRECTORY,
|
|
24
|
+
]
|
|
15
25
|
|
|
16
26
|
const LEDGER_FILE_EXTENSION = '.md'
|
|
17
27
|
|
|
@@ -51,18 +61,25 @@ function is_ledger_file_name(name: string): boolean {
|
|
|
51
61
|
// (`observation-ledger-migrate.ts`): `docs/observations.md.<pid>.migrating`.
|
|
52
62
|
const MIGRATION_CLAIM_SUFFIX = '.migrating'
|
|
53
63
|
|
|
64
|
+
function is_legacy_directory_file(file_path: string): boolean {
|
|
65
|
+
return file_path.startsWith(`${LEGACY_OBSERVATION_LEDGER_DIRECTORY}/`)
|
|
66
|
+
}
|
|
67
|
+
|
|
54
68
|
// A claim left by a process killed mid-migration holds ledger lines, so it is the ledger too: never
|
|
55
69
|
// staged by an ordinary commit, and absorbed by the next migration.
|
|
56
70
|
function is_migration_claim(file_path: string): boolean {
|
|
57
|
-
|
|
58
|
-
|
|
71
|
+
if (!file_path.endsWith(MIGRATION_CLAIM_SUFFIX)) return false
|
|
72
|
+
|
|
73
|
+
return (
|
|
74
|
+
is_legacy_directory_file(file_path) ||
|
|
75
|
+
LEGACY_OBSERVATION_LEDGER_PATHS.some((legacy) => file_path.startsWith(`${legacy}.`))
|
|
59
76
|
)
|
|
60
77
|
}
|
|
61
78
|
|
|
62
|
-
// Porcelain status collapses an untracked directory to
|
|
63
|
-
//
|
|
79
|
+
// Porcelain status collapses an untracked directory to `.josh/observations/`, so the directory
|
|
80
|
+
// itself, trailing slash and all, is the ledger as much as any file inside it — the old directory too.
|
|
64
81
|
function is_in_directory(file_path: string): boolean {
|
|
65
|
-
return file_path.startsWith(`${
|
|
82
|
+
return LEDGER_DIRECTORIES.some((directory) => file_path.startsWith(`${directory}/`))
|
|
66
83
|
}
|
|
67
84
|
|
|
68
85
|
function is_ledger_path(file_path: string): boolean {
|
|
@@ -107,8 +124,10 @@ const observation_ledger = {
|
|
|
107
124
|
}
|
|
108
125
|
|
|
109
126
|
export {
|
|
127
|
+
LEDGER_DIRECTORIES,
|
|
110
128
|
LEDGER_FILE_EXTENSION,
|
|
111
129
|
LEGACY_LEDGER_FILE,
|
|
130
|
+
LEGACY_OBSERVATION_LEDGER_DIRECTORY,
|
|
112
131
|
LEGACY_OBSERVATION_LEDGER_PATHS,
|
|
113
132
|
MIGRATION_CLAIM_SUFFIX,
|
|
114
133
|
OBSERVATION_LEDGER_DIRECTORY,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { yaml_document } from '#scripts/lib/yaml-document'
|
|
2
2
|
import { overrides_check } from './overrides-logic'
|
|
3
|
-
import { lockfile_importers_schema, type LockfileImporter } from './schemas'
|
|
3
|
+
import { lockfile_importers_schema, type LockfileImporter } from './overrides-schemas'
|
|
4
4
|
|
|
5
5
|
// The importer sections whose entries pnpm rewrites through `overrides` before resolving.
|
|
6
6
|
const DEPENDENCY_GROUPS = ['dependencies', 'devDependencies', 'optionalDependencies'] as const
|
|
@@ -3,6 +3,7 @@ import path from 'node:path'
|
|
|
3
3
|
import { git_location_environment } from '#scripts/git/git-location-environment'
|
|
4
4
|
import { ls_remote_branch_arguments } from '#scripts/git/git-ls-remote'
|
|
5
5
|
import { git_spawn_sync } from '#scripts/git/git-spawn-sync'
|
|
6
|
+
import { GIT_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
6
7
|
|
|
7
8
|
// The git probes propagation needs before it writes anything into a working tree.
|
|
8
9
|
//
|
|
@@ -11,7 +12,6 @@ import { git_spawn_sync } from '#scripts/git/git-spawn-sync'
|
|
|
11
12
|
// its remote would propagate the *previous* release, which is already published and would report
|
|
12
13
|
// success (joshuafolkken/kit#863).
|
|
13
14
|
|
|
14
|
-
const GIT_TIMEOUT_MS = 10_000
|
|
15
15
|
// The probes that talk to the remote get their own budget. Ten seconds is generous for a local
|
|
16
16
|
// `rev-parse` and tight for an ssh handshake, and a probe that times out answers "no" — which for
|
|
17
17
|
// the two below would report a push that never reached origin as a push nobody attempted.
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { git_gh_exec } from '#scripts/gh/git-gh-exec'
|
|
2
2
|
import { poll } from '#scripts/lib/poll'
|
|
3
|
+
import { GH_API_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
3
4
|
import { with_page_size } from '#scripts/version/version-remote'
|
|
4
5
|
|
|
5
6
|
// Waiting for a specific version of this package to actually exist in the registry.
|
|
@@ -14,7 +15,6 @@ const VERSIONS_PAGE_SIZE = 100
|
|
|
14
15
|
const NAMES_JQ = '[.[] | .name]'
|
|
15
16
|
const DEFAULT_TIMEOUT_MS = 600_000
|
|
16
17
|
const DEFAULT_INTERVAL_MS = 15_000
|
|
17
|
-
const GH_TIMEOUT_MS = 20_000
|
|
18
18
|
// How many probes in a row may fail before the registry is called unreadable. One failure is a rate
|
|
19
19
|
// limit, a 5xx or a DNS hiccup — ending a ten-minute wait seconds in, on the first of those, is the
|
|
20
20
|
// opposite of waiting. Only a registry that fails this many times running is a broken one.
|
|
@@ -60,7 +60,7 @@ function fetch_published_versions(versions_endpoint: string): Array<string> | un
|
|
|
60
60
|
const stdout = git_gh_exec.read_gh_api_sync({
|
|
61
61
|
path: endpoint,
|
|
62
62
|
jq_filter: NAMES_JQ,
|
|
63
|
-
timeout_ms:
|
|
63
|
+
timeout_ms: GH_API_TIMEOUT_MS,
|
|
64
64
|
})
|
|
65
65
|
if (stdout === undefined) return undefined
|
|
66
66
|
|
|
@@ -4,7 +4,8 @@ import { git_gh_issue_write } from '#scripts/gh/git-gh-issue-write'
|
|
|
4
4
|
import { OTHER_CHANGE_LABEL } from '#scripts/issue/issue-labels'
|
|
5
5
|
import { GATE_COMMAND } from '#scripts/josh/josh-command-types'
|
|
6
6
|
import { error_text } from '#scripts/lib/error-message'
|
|
7
|
-
import {
|
|
7
|
+
import { SUITE_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
8
|
+
import { build_upgrade_command } from '#scripts/version/upgrade-shell-command'
|
|
8
9
|
import { create_version_command_config } from '#scripts/version/version-command-config'
|
|
9
10
|
import { execaSync } from 'execa'
|
|
10
11
|
import { propagate_git } from './propagate-git'
|
|
@@ -20,9 +21,6 @@ import type { PropagateTarget } from './propagate-targets'
|
|
|
20
21
|
// and the sequencing stays testable (joshuafolkken/kit#863).
|
|
21
22
|
|
|
22
23
|
const SUCCESS_EXIT_CODE = 0
|
|
23
|
-
// Long enough for a consumer's full unit suite and a `pnpm add`, short enough that a hung step ends
|
|
24
|
-
// the run instead of holding the whole propagation open.
|
|
25
|
-
const STEP_TIMEOUT_MS = 1_800_000
|
|
26
24
|
// The consumer-side gate. `josh gate` is the same command the AI documents require of a person
|
|
27
25
|
// (joshuafolkken/kit#914) — running the four checks concurrently and reporting every failure in one
|
|
28
26
|
// pass — so the chain is not repeated here. It resolves in the consumer's directory, which by this
|
|
@@ -54,11 +52,15 @@ function sync_command(bin_name: string): ReadonlyArray<string> {
|
|
|
54
52
|
// which would defeat the exact-version wait: a release published while the run was in flight would
|
|
55
53
|
// be the one every consumer received. `josh adopt` names `latest` deliberately, because it is pulling
|
|
56
54
|
// whatever is newest inward rather than carrying one known release outward. Built through kit's own
|
|
57
|
-
// upgrade-command builder either way, so the lockfile repair it chains stays single-sourced
|
|
58
|
-
|
|
55
|
+
// upgrade-command builder either way, so the lockfile repair it chains stays single-sourced — and
|
|
56
|
+
// its steps are argument vectors, so the version never passes through a shell.
|
|
57
|
+
function upgrade_command(
|
|
58
|
+
package_name: string,
|
|
59
|
+
version: string,
|
|
60
|
+
): ReadonlyArray<ReadonlyArray<string>> {
|
|
59
61
|
const config = create_version_command_config({ package_name })
|
|
60
62
|
|
|
61
|
-
return
|
|
63
|
+
return build_upgrade_command(version, true, config).steps
|
|
62
64
|
}
|
|
63
65
|
|
|
64
66
|
const PROPAGATE_ORIGIN =
|
|
@@ -149,8 +151,8 @@ const NO_COMMAND = 'no command defined'
|
|
|
149
151
|
// tail would throw on its first `replaceAll`, and nothing between here and the report catches it:
|
|
150
152
|
// the throw escapes the whole run, so every consumer after this one goes unattempted and the
|
|
151
153
|
// end-of-run block is never printed — "one consumer's failure never stops another", lost to a crash.
|
|
152
|
-
function captured(text: string | undefined): string {
|
|
153
|
-
return text
|
|
154
|
+
function captured(text: string | undefined = ''): string {
|
|
155
|
+
return text
|
|
154
156
|
}
|
|
155
157
|
|
|
156
158
|
function step_result(step: string, exit_code: number | undefined): StepResult {
|
|
@@ -172,7 +174,7 @@ function spawn_step(
|
|
|
172
174
|
cwd: target.path,
|
|
173
175
|
reject: false,
|
|
174
176
|
stdio: 'inherit',
|
|
175
|
-
timeout:
|
|
177
|
+
timeout: SUITE_TIMEOUT_MS,
|
|
176
178
|
})
|
|
177
179
|
|
|
178
180
|
return step_result(step, spawned.exitCode)
|
|
@@ -207,7 +209,7 @@ function spawn_captured(
|
|
|
207
209
|
stdin: 'inherit',
|
|
208
210
|
stdout: ['inherit', 'pipe'],
|
|
209
211
|
stderr: ['inherit', 'pipe'],
|
|
210
|
-
timeout:
|
|
212
|
+
timeout: SUITE_TIMEOUT_MS,
|
|
211
213
|
})
|
|
212
214
|
|
|
213
215
|
return {
|
|
@@ -383,11 +385,14 @@ function run_each(
|
|
|
383
385
|
return { step, is_ok: true }
|
|
384
386
|
}
|
|
385
387
|
|
|
388
|
+
// One entry per step, so `run_each` stops a toolkit's lockfile repair after its failed install.
|
|
386
389
|
function upgrade_commands(plan: ReleasePlan): ReadonlyArray<ToolkitCommand> {
|
|
387
|
-
return plan.releases.
|
|
388
|
-
package_name
|
|
389
|
-
|
|
390
|
-
|
|
390
|
+
return plan.releases.flatMap((release) =>
|
|
391
|
+
upgrade_command(release.package_name, release.version).map((command) => ({
|
|
392
|
+
package_name: release.package_name,
|
|
393
|
+
command,
|
|
394
|
+
})),
|
|
395
|
+
)
|
|
391
396
|
}
|
|
392
397
|
|
|
393
398
|
function sync_commands(plan: ReleasePlan): ReadonlyArray<ToolkitCommand> {
|
|
@@ -24,8 +24,7 @@ const SKIP_PUBLISH_FLAG = '--skip-publish-wait'
|
|
|
24
24
|
const DRY_RUN_FLAG = '--dry-run'
|
|
25
25
|
const TARGET_FLAG = '--target'
|
|
26
26
|
const KNOWN_FLAGS: ReadonlyArray<string> = [SKIP_PUBLISH_FLAG, DRY_RUN_FLAG]
|
|
27
|
-
// Shown in the usage line only
|
|
28
|
-
// flag-only refusal runs, and this entry never has to match an argument.
|
|
27
|
+
// Shown in the usage line only: `--target` carries a value, so it is spelled with a placeholder.
|
|
29
28
|
const TARGET_USAGE = `${TARGET_FLAG} <repo>`
|
|
30
29
|
const DRY_RUN_REASON = 'would be propagated'
|
|
31
30
|
const UNTOUCHED_NOTE = 'No consumer was touched.'
|
|
@@ -40,41 +39,51 @@ interface RunOptions {
|
|
|
40
39
|
usage?: string
|
|
41
40
|
}
|
|
42
41
|
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
42
|
+
// `--target` is read as a list so a second one is seen and refused rather than letting one of the two
|
|
43
|
+
// names win silently.
|
|
44
|
+
const OPTIONS = {
|
|
45
|
+
'skip-publish-wait': { type: 'boolean' },
|
|
46
|
+
'dry-run': { type: 'boolean' },
|
|
47
|
+
target: { type: 'string', multiple: true },
|
|
48
|
+
} as const
|
|
49
|
+
const USAGE = `Usage: josh propagate [${[...KNOWN_FLAGS, TARGET_USAGE].join('] [')}]`
|
|
50
|
+
const TARGET_MISSING = `${TARGET_FLAG} needs a repository name, e.g. ${TARGET_FLAG} app-kit`
|
|
51
|
+
|
|
52
|
+
type ParsedValues = NonNullable<ReturnType<typeof cli_flags.values_of<typeof OPTIONS>>>
|
|
53
|
+
|
|
54
|
+
function refused(usage: string): RunOptions {
|
|
55
|
+
return { is_dry_run: false, is_publish_wait_skipped: false, usage }
|
|
47
56
|
}
|
|
48
57
|
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
function split_target(argv: ReadonlyArray<string>): TargetArgument {
|
|
52
|
-
const index = argv.indexOf(TARGET_FLAG)
|
|
53
|
-
if (index === -1) return { rest: [...argv] }
|
|
54
|
-
const target = argv[index + 1]
|
|
55
|
-
const rest = argv.filter((_, position) => position !== index && position !== index + 1)
|
|
58
|
+
function to_options(values: ParsedValues): RunOptions {
|
|
59
|
+
const [target] = values.target ?? []
|
|
56
60
|
|
|
57
|
-
|
|
58
|
-
|
|
61
|
+
return {
|
|
62
|
+
is_dry_run: values['dry-run'] ?? false,
|
|
63
|
+
is_publish_wait_skipped: values['skip-publish-wait'] ?? false,
|
|
64
|
+
...(target !== undefined && { target }),
|
|
59
65
|
}
|
|
60
|
-
|
|
61
|
-
return { rest, target }
|
|
62
66
|
}
|
|
63
67
|
|
|
64
68
|
// Reject anything not on the list rather than ignoring it. `--dryrun` silently falling through to
|
|
65
|
-
// the real write path is the mistake this refusal exists to prevent.
|
|
69
|
+
// the real write path is the mistake this refusal exists to prevent. A `--target` with no usable name
|
|
70
|
+
// after it — last on the line, or followed by `--dry-run` — is named on its own, so the caller learns
|
|
71
|
+
// which value is missing rather than reading the generic usage.
|
|
72
|
+
function read_values(argv: ReadonlyArray<string>): ParsedValues | undefined {
|
|
73
|
+
const values = cli_flags.values_of(argv, OPTIONS)
|
|
74
|
+
|
|
75
|
+
return (values?.target?.length ?? 0) > 1 ? undefined : values
|
|
76
|
+
}
|
|
77
|
+
|
|
66
78
|
function parse_options(argv: ReadonlyArray<string>): RunOptions {
|
|
67
|
-
|
|
68
|
-
const
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
79
|
+
if (cli_flags.is_value_unusable(argv, TARGET_FLAG)) return refused(TARGET_MISSING)
|
|
80
|
+
const values = read_values(argv)
|
|
81
|
+
|
|
82
|
+
if (values === undefined) {
|
|
83
|
+
return refused(`Unknown argument(s) or a repeated ${TARGET_FLAG}: ${argv.join(' ')}\n${USAGE}`)
|
|
72
84
|
}
|
|
73
|
-
const usage =
|
|
74
|
-
target_usage ??
|
|
75
|
-
cli_flags.refuse_unknown_flags(rest, [...KNOWN_FLAGS, TARGET_USAGE], 'propagate')
|
|
76
85
|
|
|
77
|
-
return
|
|
86
|
+
return to_options(values)
|
|
78
87
|
}
|
|
79
88
|
|
|
80
89
|
// Propagation runs from the supplier's own repository, and only there — kit's, or a toolkit's built
|
|
@@ -28,7 +28,7 @@ type FileGraph = ReadonlyMap<string, ReadonlySet<string>>
|
|
|
28
28
|
function specifiers_of(source: string, pattern: RegExp): ReadonlyArray<string> {
|
|
29
29
|
const matches = [...source.matchAll(pattern)]
|
|
30
30
|
|
|
31
|
-
return matches.flatMap((match) =>
|
|
31
|
+
return matches.flatMap((match) => match[1] ?? [])
|
|
32
32
|
}
|
|
33
33
|
|
|
34
34
|
function parse_imports(source: string): ReadonlyArray<string> {
|
|
@@ -157,6 +157,22 @@ function expand(
|
|
|
157
157
|
return included
|
|
158
158
|
}
|
|
159
159
|
|
|
160
|
+
// Every file `entry` reaches through its own imports, `entry` included, with no stage cap — the whole
|
|
161
|
+
// transitive closure a running script loads, not the §4.3 neighborhood `expand` bounds.
|
|
162
|
+
function reachable(entry: string, forward: FileGraph): ReadonlySet<string> {
|
|
163
|
+
const included = new Set<string>([entry])
|
|
164
|
+
const pending = [entry]
|
|
165
|
+
|
|
166
|
+
for (let file = pending.pop(); file !== undefined; file = pending.pop()) {
|
|
167
|
+
const fresh = [...(forward.get(file) ?? [])].filter((target) => !included.has(target))
|
|
168
|
+
|
|
169
|
+
for (const target of fresh) included.add(target)
|
|
170
|
+
pending.push(...fresh)
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
return included
|
|
174
|
+
}
|
|
175
|
+
|
|
160
176
|
// The whole §4.3 walk: build the graph over the universe, then expand the seed within it.
|
|
161
177
|
function expand_scope(
|
|
162
178
|
seed: ReadonlyArray<string>,
|
|
@@ -174,6 +190,7 @@ const import_graph = {
|
|
|
174
190
|
expand_scope,
|
|
175
191
|
invert,
|
|
176
192
|
parse_imports,
|
|
193
|
+
reachable,
|
|
177
194
|
resolve_spec,
|
|
178
195
|
MAX_STAGES,
|
|
179
196
|
}
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import path from 'node:path'
|
|
2
2
|
import { find_local_bin_upwards } from '#scripts/build/local-bin'
|
|
3
|
+
import { LINT_TIMEOUT_MS } from '#scripts/lib/timeouts'
|
|
3
4
|
import { execa } from 'execa'
|
|
4
5
|
import { z } from 'zod'
|
|
5
6
|
|
|
@@ -20,7 +21,6 @@ const PNPM_EXEC = 'exec'
|
|
|
20
21
|
// `--no-inline-config` so a `/* eslint-disable */` in a file cannot hide a candidate from the scan the
|
|
21
22
|
// way it can from the gate; the scan reports what the rules say, not what a file asked to suppress.
|
|
22
23
|
const FORMAT_FLAGS: ReadonlyArray<string> = ['--format', 'json', '--no-inline-config']
|
|
23
|
-
const PROCESS_TIMEOUT_MS = 180_000
|
|
24
24
|
|
|
25
25
|
type Priority = 'high' | 'medium' | 'low'
|
|
26
26
|
const HIGH: Priority = 'high'
|
|
@@ -188,9 +188,9 @@ function eslint_command(root: string, files: ReadonlyArray<string>): ReadonlyArr
|
|
|
188
188
|
// `undefined` means the run did not produce parseable eslint JSON — a failure, kept distinct from a
|
|
189
189
|
// valid empty array (a clean scope), so a scan that could not run is never reported as `clear`
|
|
190
190
|
// (joshuafolkken/kit#2180).
|
|
191
|
-
function parse_results(raw: string | undefined): Array<LintResult> | undefined {
|
|
191
|
+
function parse_results(raw: string | undefined = 'null'): Array<LintResult> | undefined {
|
|
192
192
|
try {
|
|
193
|
-
const parsed = results_schema.safeParse(JSON.parse(raw
|
|
193
|
+
const parsed = results_schema.safeParse(JSON.parse(raw))
|
|
194
194
|
|
|
195
195
|
return parsed.success ? parsed.data : undefined
|
|
196
196
|
} catch {
|
|
@@ -216,7 +216,7 @@ async function run_eslint_json(
|
|
|
216
216
|
reject: false,
|
|
217
217
|
stdout: 'pipe',
|
|
218
218
|
stderr: 'ignore',
|
|
219
|
-
timeout:
|
|
219
|
+
timeout: LINT_TIMEOUT_MS,
|
|
220
220
|
})
|
|
221
221
|
|
|
222
222
|
return parse_results(result.stdout)
|
|
@@ -38,6 +38,7 @@ function read_release_input(environment: Environment): ReleaseInput {
|
|
|
38
38
|
workflow: optional(environment['RELEASE_WORKFLOW']),
|
|
39
39
|
jobs: job_names(environment['RELEASE_JOBS']),
|
|
40
40
|
await_publish: environment['RELEASE_AWAIT_PUBLISH'] === 'true',
|
|
41
|
+
release_run_workflow: optional(environment['RELEASE_RUN_WORKFLOW']),
|
|
41
42
|
},
|
|
42
43
|
}
|
|
43
44
|
}
|
|
@@ -0,0 +1,115 @@
|
|
|
1
|
+
import { z } from 'zod'
|
|
2
|
+
|
|
3
|
+
// A job skipped by its `if:` (or neutral) belongs to a run that succeeded; counting it would skip a
|
|
4
|
+
// lower tag whose release is still on its way.
|
|
5
|
+
const FAILED_CONCLUSIONS = new Set(['failure', 'cancelled', 'timed_out', 'startup_failure'])
|
|
6
|
+
const RELEASE_RUN_TITLE = 'GitHub Release '
|
|
7
|
+
const RUN_SCHEMA = z.object({
|
|
8
|
+
id: z.number(),
|
|
9
|
+
display_title: z.string(),
|
|
10
|
+
status: z.string().optional(),
|
|
11
|
+
conclusion: z.string().nullable().optional(),
|
|
12
|
+
})
|
|
13
|
+
const JOB_SCHEMA = z.object({
|
|
14
|
+
name: z.string(),
|
|
15
|
+
status: z.string(),
|
|
16
|
+
conclusion: z.string().nullable(),
|
|
17
|
+
})
|
|
18
|
+
const RUNS_SCHEMA = z.object({ workflow_runs: z.array(RUN_SCHEMA) })
|
|
19
|
+
const JOBS_SCHEMA = z.object({ jobs: z.array(JOB_SCHEMA) })
|
|
20
|
+
|
|
21
|
+
type Run = z.infer<typeof RUN_SCHEMA>
|
|
22
|
+
type PollState = 'ready' | 'failed' | 'pending'
|
|
23
|
+
|
|
24
|
+
// `read` answers a GET on a repository API path. `workflow` is the publish workflow and `jobs` the
|
|
25
|
+
// jobs in it whose failure counts (empty: every job). `release_run_workflow` is the workflow that
|
|
26
|
+
// creates a tag's release — the publish workflow itself when its run creates the release.
|
|
27
|
+
interface RunSource {
|
|
28
|
+
read: (path: string) => Promise<unknown>
|
|
29
|
+
workflow: string | undefined
|
|
30
|
+
jobs: ReadonlyArray<string>
|
|
31
|
+
release_run_workflow: string | undefined
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
function is_failed_job(job: z.infer<typeof JOB_SCHEMA>, names: ReadonlyArray<string>): boolean {
|
|
35
|
+
if (job.status !== 'completed' || !FAILED_CONCLUSIONS.has(job.conclusion ?? '')) return false
|
|
36
|
+
if (names.length === 0) return true
|
|
37
|
+
|
|
38
|
+
return names.some((name) => job.name === name || job.name.startsWith(`${name} /`))
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
async function list_runs(source: RunSource, workflow_name: string): Promise<ReadonlyArray<Run>> {
|
|
42
|
+
const workflow = encodeURIComponent(workflow_name)
|
|
43
|
+
|
|
44
|
+
return RUNS_SCHEMA.parse(await source.read(`/actions/workflows/${workflow}/runs?per_page=100`))
|
|
45
|
+
.workflow_runs
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
async function find_run(
|
|
49
|
+
source: RunSource,
|
|
50
|
+
workflow_name: string,
|
|
51
|
+
title: string,
|
|
52
|
+
): Promise<Run | undefined> {
|
|
53
|
+
const runs = await list_runs(source, workflow_name)
|
|
54
|
+
|
|
55
|
+
return runs.find((item) => item.display_title === title)
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
async function has_failed_job(source: RunSource, run_id: number): Promise<boolean> {
|
|
59
|
+
const { jobs } = JOBS_SCHEMA.parse(
|
|
60
|
+
await source.read(`/actions/runs/${String(run_id)}/jobs?per_page=100`),
|
|
61
|
+
)
|
|
62
|
+
|
|
63
|
+
return jobs.some((job) => is_failed_job(job, source.jobs))
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// A run started before its title named the tag may be at work on any tag.
|
|
67
|
+
function may_release(run: Run, tag: string): boolean {
|
|
68
|
+
if (run.status === 'completed') return false
|
|
69
|
+
|
|
70
|
+
return (
|
|
71
|
+
run.display_title === `${RELEASE_RUN_TITLE}${tag}` ||
|
|
72
|
+
!run.display_title.startsWith(RELEASE_RUN_TITLE)
|
|
73
|
+
)
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
// Unset, nothing tells that a release is over, so the lower tag is waited for.
|
|
77
|
+
async function is_release_over(source: RunSource, tag: string): Promise<boolean> {
|
|
78
|
+
const workflow = source.release_run_workflow
|
|
79
|
+
if (workflow === undefined) return false
|
|
80
|
+
if (workflow === source.workflow) return true
|
|
81
|
+
|
|
82
|
+
const runs = await list_runs(source, workflow)
|
|
83
|
+
|
|
84
|
+
return runs.every((run) => !may_release(run, tag))
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// A lower tag whose publication failed, or finished while no release run is left at work on it,
|
|
88
|
+
// never gains a release: a tag cut before joshuafolkken/kit#3138 was fixed would hold every later one.
|
|
89
|
+
async function is_release_lost(source: RunSource, tag: string): Promise<boolean> {
|
|
90
|
+
if (source.workflow === undefined) return false
|
|
91
|
+
|
|
92
|
+
const run = await find_run(source, source.workflow, `Publish ${tag}`)
|
|
93
|
+
if (!run) return false
|
|
94
|
+
if (await has_failed_job(source, run.id)) return true
|
|
95
|
+
|
|
96
|
+
return run.status === 'completed' && (await is_release_over(source, tag))
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
// A run not listed yet is pending too: the announcement that starts this release also starts the
|
|
100
|
+
// publication, so the release can ask before GitHub has queued that run.
|
|
101
|
+
async function publication_state(
|
|
102
|
+
source: RunSource,
|
|
103
|
+
workflow: string,
|
|
104
|
+
tag: string,
|
|
105
|
+
): Promise<PollState> {
|
|
106
|
+
const run = await find_run(source, workflow, `Publish ${tag}`)
|
|
107
|
+
if (run?.status !== 'completed') return 'pending'
|
|
108
|
+
|
|
109
|
+
return run.conclusion === 'success' ? 'ready' : 'failed'
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
const github_release_runs = { is_release_lost, publication_state }
|
|
113
|
+
|
|
114
|
+
export type { PollState, RunSource }
|
|
115
|
+
export { github_release_runs }
|