Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
65 commits
Select commit Hold shift + click to select a range
9fdd94c
refactor(setup nu): replace action flags with subcommands + shared co…
tonythethompson Aug 1, 2026
633c50c
fix: 3 findings — Prevent PATH subcommand from deleting active managed N
qodo-code-review[bot] Aug 1, 2026
fb0ff23
fix: address PR #66 review feedback
tonythethompson Aug 1, 2026
438d80b
fix: clarify PATH messaging in Nu setup hints
tonythethompson Aug 1, 2026
7e726a2
Add reserved `numan use` CLI stub
tonythethompson Aug 1, 2026
7951797
fix: apply CodeRabbit auto-fixes
coderabbitai[bot] Aug 1, 2026
8062a11
feat: implement numan use for side-by-side Nu version management
tonythethompson Aug 1, 2026
213e874
fix: 5 findings — Validate and normalize Nu versions; Propagate legacy m
qodo-code-review[bot] Aug 1, 2026
b8f4233
fix: address PR review — validate before delete, fix help text, fix c…
tonythethompson Aug 1, 2026
62b8656
chore: remove temp files
tonythethompson Aug 1, 2026
2591036
Update src/cmd/snapshot.rs
tonythethompson Aug 2, 2026
d9b8e0f
fix: PR 67 review - bootstrap yes flag, version check, mutation lock
tonythethompson Aug 2, 2026
0429557
Merge origin/master into feature/numan-use - resolve conflicts: take …
tonythethompson Aug 2, 2026
e245232
Update .gitignore
tonythethompson Aug 2, 2026
641bd24
wip(numan-use): integrate use + migration pieces (split pending)
tonythethompson Aug 2, 2026
e0e467e
feat(nu): extract legacy migrate fs + tests into src/nu/migrate_legacy
tonythethompson Aug 2, 2026
a006204
fix: Reconcile migration journal under lock
qodo-code-review[bot] Aug 2, 2026
10b4e31
fix: Recover prepared migrations from filesystem state
qodo-code-review[bot] Aug 2, 2026
7153a7f
fix: Remove redundant migration comment
qodo-code-review[bot] Aug 2, 2026
283a1c4
fix: Keep use list read-only
qodo-code-review[bot] Aug 2, 2026
45846fc
Potential fix for pull request finding
tonythethompson Aug 2, 2026
6679cc3
Potential fix for pull request finding
tonythethompson Aug 2, 2026
01c0e44
Update src/cmd/setup.rs
tonythethompson Aug 2, 2026
da3daec
Update src/nu/bootstrap.rs
tonythethompson Aug 2, 2026
2635dbd
Update src/state/migration_journal.rs
tonythethompson Aug 2, 2026
d52cda6
Update src/nu/version_manager.rs
tonythethompson Aug 2, 2026
d0a5c27
Update src/nu/migrate_legacy.rs
tonythethompson Aug 2, 2026
db0cbb0
Merge branch 'master' into pr-migrate-legacy-installs
tonythethompson Aug 2, 2026
a867a90
fix(migrate): address PR 69 unresolved review threads (8 fixes)
tonythethompson Aug 2, 2026
5bbdaf5
fix(migrate): rebase fallout — close stray fn, drop unused imports, g…
tonythethompson Aug 2, 2026
0e8863e
docs(agents): list nu/migrate_legacy.rs in the project structure
tonythethompson Aug 2, 2026
323fcb3
fix(migrate): address PR 69 remaining review threads (7 fixes)
tonythethompson Aug 2, 2026
5705910
refactor(setup): route setup_subcommand_lock across destructive setup…
tonythethompson Aug 2, 2026
e14959f
refactor(setup): require_tty_or_yes seam + apply across all destructi…
tonythethompson Aug 2, 2026
44381ff
feat(setup): --force flag for `setup nu use <path>` opt-in to destruc…
tonythethompson Aug 2, 2026
81ac8ee
fix: 4 findings — Lock direct setup execution; Fix setup test arguments;
qodo-code-review[bot] Aug 2, 2026
471347c
fix: Include legacy installs in version listing
qodo-code-review[bot] Aug 2, 2026
cea6efd
fix(migrate): install into versioned layout (PR69 Srm) + versioned-la…
tonythethompson Aug 2, 2026
ce81a65
audit(destructive): require_tty_or_yes for remove and snapshot delete…
tonythethompson Aug 2, 2026
2682b97
Update migrate_legacy.rs
tonythethompson Aug 2, 2026
225dc72
Merge origin/master into feature/setup-subcommand-lock
cursoragent Aug 2, 2026
bfa9ac3
Fix PR #71 review: version-before-mutation and force gates
cursoragent Aug 2, 2026
302d5b6
Fix PR #71 review: TTY seams, migration safety, marker order
tonythethompson Aug 3, 2026
8b8b1cd
Fix rustfmt in migrate_legacy regression test
tonythethompson Aug 3, 2026
3b655fb
fix(nu): return VersionManagerError from public version APIs
cursoragent Aug 3, 2026
8f009ff
Fix PR #71 review: docs, doctor hints, detect timeout
tonythethompson Aug 3, 2026
85dcc8c
Fix PR #71 review: PATH test mutex and clear-before-delete
tonythethompson Aug 3, 2026
438f695
fix(pr71): hoist migration symlink guard and cover use snapshots
cursoragent Aug 3, 2026
58bd10d
Fix cubic PR #71: corrupt marker, list migration, ignore gates
tonythethompson Aug 3, 2026
9079553
Fix Copilot review: gate use-latest off-tree preservation
tonythethompson Aug 3, 2026
2fd31e9
Fix Codex review: refresh Nu paths cache on version switch
tonythethompson Aug 3, 2026
fa60c64
Fix macOS Real-Nu PATH race in setup_nu_test
tonythethompson Aug 3, 2026
f157121
Fix PathRestoreGuard doctest under --ignored
tonythethompson Aug 3, 2026
75cd2ce
Fix setup nu short-circuit on dangling active marker
tonythethompson Aug 3, 2026
5e40ef6
Merge master into feature/setup-subcommand-lock
tonythethompson Aug 3, 2026
d24f79a
docs(roadmap): audit each Post-1.0 claim against shipped code
tonythethompson Aug 2, 2026
248e333
feat(roadmap-contract): freeze the cross-repo guardrail at numan-road…
tonythethompson Aug 2, 2026
f79ad65
fix(roadmap-contract): align CONTRACT_SHA with the v1 tag's resolved SHA
tonythethompson Aug 2, 2026
8092f97
fix(ci): drop CONTRACT_SHA verify in favor of tag-existence check
tonythethompson Aug 2, 2026
be59965
fix(roadmap-contract): correct bump tag math and mirror v1 SHA pin
cursoragent Aug 2, 2026
377b258
fix(roadmap-contract): address Cubic/CodeRabbit PR 72 findings
cursoragent Aug 2, 2026
163a140
chore: retarget roadmap contract v1 pin to 60a015fec1b2
cursoragent Aug 2, 2026
59554fa
Potential fix for pull request finding
tonythethompson Aug 3, 2026
c9637ba
ci(roadmap-drift): authenticate GitHub API verify step
tonythethompson Aug 3, 2026
4dbf830
feat(try): update starter selection for expanded 0.114 catalog
tonythethompson Aug 9, 2026
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
58 changes: 57 additions & 1 deletion .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -60,9 +60,65 @@ jobs:
roadmap-drift:
name: Roadmap drift
runs-on: ubuntu-latest
env:
# Roadmap contract v1 pin. CONTRACT_TAG is the human-readable handle;
# CONTRACT_SHA is the immutable fetch target. The verify step fails
# closed if the tag moves away from this SHA. See scripts/bump-contract.sh.
CONTRACT_TAG: numan-roadmap-contract/v1
CONTRACT_SHA: 60a015fec1b25886f139d568edb1a2ff6df212f3
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Run roadmap-drift check against consolidated roadmap
- name: Verify pinned SHA still resolves to tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tagged_sha=$(curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "User-Agent: numan-ci" \
"https://api.github.com/repos/tonythethompson/numan/git/refs/tags/${CONTRACT_TAG}" \
| python -c "
import json, os, sys, urllib.request
ref = json.load(sys.stdin)
sha = ref['object']['sha']
if ref['object']['type'] == 'tag':
headers = {
'Accept': 'application/vnd.github+json',
'User-Agent': 'numan-ci',
}
token = os.environ.get('GITHUB_TOKEN', '')
if token:
headers['Authorization'] = f'Bearer {token}'
req = urllib.request.Request(ref['object']['url'], headers=headers)
tag = json.load(urllib.request.urlopen(req))
sha = tag['object']['sha']
print(sha)
")
Comment on lines +75 to +96

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🔴 Critical | ⚡ Quick win

Fix the YAML block-scalar indentation in all roadmap workflows.

The Python source starts at YAML column zero. This ends the run: | scalar and makes each workflow invalid YAML. None of the contract validation jobs can load.

  • .github/workflows/ci.yml#L75-L96: indent the embedded Python program and its closing quote within the run: | scalar.
  • cross-repo-mirror/numan-plugins/.github/workflows/roadmap-drift.yml#L33-L54: indent the embedded Python program and its closing quote within the run: | scalar.
  • cross-repo-mirror/numan-registry/.github/workflows/roadmap-drift.yml#L33-L54: indent the embedded Python program and its closing quote within the run: | scalar.
🧰 Tools
🪛 actionlint (1.7.12)

[error] 81-81: could not parse as YAML: could not find expected ':'

(syntax-check)

🪛 YAMLlint (1.37.1)

[error] 82-82: syntax error: could not find expected ':'

(syntax)

📍 Affects 3 files
  • .github/workflows/ci.yml#L75-L96 (this comment)
  • cross-repo-mirror/numan-plugins/.github/workflows/roadmap-drift.yml#L33-L54
  • cross-repo-mirror/numan-registry/.github/workflows/roadmap-drift.yml#L33-L54
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/ci.yml around lines 75 - 96, Fix the run block containing
tagged_sha in .github/workflows/ci.yml lines 75-96 by indenting the entire
embedded Python program and its closing quote within the run: | scalar. Apply
the same indentation fix to
cross-repo-mirror/numan-plugins/.github/workflows/roadmap-drift.yml lines 33-54
and cross-repo-mirror/numan-registry/.github/workflows/roadmap-drift.yml lines
33-54, preserving the script content and ensuring all three workflows remain
valid YAML.

Source: Linters/SAST tools

if [ "$tagged_sha" != "$CONTRACT_SHA" ]; then
echo "::error::Tag $CONTRACT_TAG resolves to $tagged_sha but CONTRACT_SHA=$CONTRACT_SHA."
echo "Run scripts/bump-contract.sh to coordinate the bump across all three repos."
exit 1
fi
echo "OK: $CONTRACT_TAG pinned to $CONTRACT_SHA"
- name: Fetch consolidated roadmap from pin (numan@$CONTRACT_SHA)
run: |
curl --fail-with-body --silent --show-error --location \
https://raw.githubusercontent.com/tonythethompson/numan/$CONTRACT_SHA/docs/plans/consolidated-multi-repo-roadmap.md \
-o /tmp/consolidated-roadmap.md
diff -u docs/plans/consolidated-multi-repo-roadmap.md /tmp/consolidated-roadmap.md \
|| (echo "::error::Local consolidated roadmap diverges from $CONTRACT_SHA."; \
echo "Run scripts/bump-contract.sh and ship a coordinated bump PR."; \
exit 1)
- name: Fetch drift script from pin (numan@$CONTRACT_SHA)
run: |
curl --fail-with-body --silent --show-error --location \
https://raw.githubusercontent.com/tonythethompson/numan/$CONTRACT_SHA/scripts/check-roadmap-drift.py \
-o /tmp/check-roadmap-drift.py
diff -u scripts/check-roadmap-drift.py /tmp/check-roadmap-drift.py \
|| (echo "::error::Local drift script diverges from $CONTRACT_SHA."; \
echo "Bump the contract before changing the drift script."; \
exit 1)
- name: Run drift check against consolidated roadmap
run: python scripts/check-roadmap-drift.py
- name: Mirror to numan-plugins/numan-registry
run: |
Expand Down
4 changes: 4 additions & 0 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -80,13 +80,16 @@ src/
autoload_recovery.rs — Command-independent PendingAutoload reconciliation into lockfile + derived autoload state
autoload_state.rs — Derived autoload-state projection (NOT authoritative; lockfile is ground truth) (Phase 4)
lifecycle_journal.rs — pending-lifecycle.json for update/remove/nupm_import crash recovery (Phase 5–6)
migration_journal.rs — `state/migration-journal.json` for legacy-Nu single-binary → versioned-layout transition (Prepared → Renamed → Active stages); self-heal at top of `migrate_legacy_install_with_detector`, reconciled by `numan doctor --fix` (auto-tier repair)
snapshot.rs — Immutable activation snapshots (`create_snapshot`, `list_snapshots`, etc.)
rollback.rs — Journaled restore of Numan-owned state to a snapshot
nupm_import.rs — nupm-import provenance (`state/nupm-imports.json`, Phase 6.2)
nu/
bootstrap.rs — download/install official Nushell release under tools/nushell
paths.rs — Nu path cache (detect, load, save, validate_drift)
autoload.rs — render_use_statement, generate_autoload_content, FakeCandidateRunner, managed-file ops (Phase 4)
migrate_legacy.rs — Legacy single-binary Nu → versioned-layout transition (journaled; Phase-1 cleanup; see docs/numan-doctor.md)
version_manager.rs — Managed-Nu versioned layout (`tools/nushell/<version>/`), active marker (`nu_state/active-version.json`), on/off-tree resolution
util/
atomic.rs — write_json_atomic helper (tempfile+persist)
fs_safety.rs — OWNERSHIP_MARKER, acquire_mutation_lock (advisory fd_lock mutex), assert_managed_file_owned (Phase 4)
Expand Down Expand Up @@ -141,6 +144,7 @@ tests/
- **Activation scope**: `PluginActivation` struct stores `(nu_executable_sha256, nu_version, plugin_registry_path)`; a plugin is "active" only when all three match the current `NuPaths` — bare `bool` would go stale after `numan init --refresh`
- **Journal**: `state/pending-activation.json` written as all-`prepared` before first registration; each entry advances to `registered` atomically before lockfile update; reconciled on next `activate` run if process is interrupted
- **Plugin deactivate journal**: `state/pending-plugin-deactivate.json` (`Prepared` → `Unregistered` → clear lockfile `activation`); reconciled on next `deactivate`; doctor warns `journal.plugin_deactivate_pending`
- **Migration journal**: `state/migration-journal.json` for the legacy-Nu single-binary → versioned layout transition. Stages `Prepared` (before `create_dir_all`) → `Renamed` (after legitimate `rename`) → `Active` (after `write_active_version`); journal deleted on transition to `Active`. Every well-formed pending journal stage (`Prepared`, `Renamed`, and `Active`) is reconciled by `numan doctor --fix` (Auto-tier, fix hint `numan use`) and by the self-healing `reconcile(root)?` at the top of every `migrate_legacy_install_with_detector` call; file-system truth takes precedence over journal stage when they disagree. Unreadable or schema-mismatched journals emit `journal.migration_invalid` (Error severity, Manual repair tier: delete the stale journal); they are not auto-reconciled.
- **Active version marker**: `nu_state/active-version.json` (`{ "version": "X.Y.Z" }`, optionally `{ "version": "X.Y.Z", "binary_path": "/abs/path/to/nu" }` for off-tree selections). Sole authority for which `tools/nushell/<v>/` is selected. Written by `numan setup nu` and `numan use <version>|latest`. The optional `binary_path` records the resolved off-tree binary when `numan setup nu use <path>` swaps to a user-supplied Nu so subsequent `numan use list` and `find_nu_executable_with_root` can resolve the chosen version even when no on-tree install exists (the field uses `#[serde(default, skip_serializing_if = "Option::is_none")]` so the on-disk shape stays `{ "version": ... }` for on-tree selections and pre-existing markers still load).
- **Atomic writes**: all JSON state files (lockfile, journal, nu_state/paths.json) use `write_json_atomic` (tempfile in same dir + persist) — no partial-write corruption
- **Function signatures**: use `&Path` not `&PathBuf` in function parameters (clippy::ptr_arg is CI-enforced)
Expand Down
93 changes: 57 additions & 36 deletions cross-repo-mirror/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ This directory holds **drop-in artifacts** that complete the cross-repo
roadmap-drift guardrail started in PR 67 of `numan`. Each subdirectory
mirrors both the local-roadmap file and the GitHub Actions workflow
that runs `scripts/check-roadmap-drift.py` against the consolidated
roadmap hosted in `numan@master`.
roadmap hosted in `numan@<contract-tag>` (pinned by SHA; see
[`docs/contracts/roadmap-v1.md`](../contracts/roadmap-v1.md)).

## What ships

Expand All @@ -13,10 +14,10 @@ cross-repo-mirror/
├── README.md ← you are here
├── numan-plugins/
│ ├── docs/roadmap.md ← the local pointer
│ └── .github/workflows/roadmap-drift.yml ← the CI job snippet
│ └── .github/workflows/roadmap-drift.yml ← the CI job snippet (pinned to contract v1)
├── numan-registry/
│ ├── docs/roadmap.md ← the local pointer
│ └── .github/workflows/roadmap-drift.yml ← the CI job snippet
│ └── .github/workflows/roadmap-drift.yml ← the CI job snippet (pinned to contract v1)
└── snapshot-tests/
└── mirror_dry_run.sh ← local sanity check
```
Expand All @@ -30,37 +31,56 @@ script only one side knows about isn't a guardrail — both sibling repos
need to run it.

The CI workflow in each sibling repo `curl`s the consolidated roadmap
straight from `numan@master/docs/plans/consolidated-multi-repo-roadmap.md`
straight from `numan@<CONTRACT_SHA>/docs/plans/consolidated-multi-repo-roadmap.md`
into a temp file at the start of the job, then runs the exact same
`scripts/check-roadmap-drift.py` against that fetched copy. There is
never a stale copy in a sibling repo: the source of truth is the
published URL.
`scripts/check-roadmap-drift.py` against that fetched copy. The contract
SHA is recorded as `CONTRACT_SHA: 60a015fec1b25886f139d568edb1a2ff6df212f3`
in each workflow so a force-push to the contract tag can never
silently change the guardrail. Bumping the contract is a coordinated
operation across all three repos — see `scripts/bump-contract.sh` in
the `numan` repo.

## Installation

In each sibling repo:
From a checkout of `tonythethompson/numan` (this repo), copy the
sibling artifacts into each sibling working tree. Do not run the `cp`
lines from inside the sibling repo alone — the `cross-repo-mirror/`
tree lives only in `numan`.

```bash
mkdir -p scripts
# In the numan checkout:
NUMAN_ROOT=$(pwd) # path to tonythethompson/numan
CONTRACT_SHA=60a015fec1b25886f139d568edb1a2ff6df212f3
SIBLING=numan-plugins # or numan-registry
SIBLING_ROOT=/path/to/$SIBLING

mkdir -p "$SIBLING_ROOT/scripts" "$SIBLING_ROOT/docs" "$SIBLING_ROOT/.github/workflows"

# Fetch the pinned contract version of the drift script directly from
# the contract SHA so the sibling repo never holds a stale copy.
curl -sSfL \
"https://raw.githubusercontent.com/tonythethompson/numan/${CONTRACT_SHA}/scripts/check-roadmap-drift.py" \
-o "$SIBLING_ROOT/scripts/check-roadmap-drift.py"
chmod +x "$SIBLING_ROOT/scripts/check-roadmap-drift.py"

cp "$NUMAN_ROOT/cross-repo-mirror/$SIBLING/docs/roadmap.md" \
"$SIBLING_ROOT/docs/roadmap.md"
cp "$NUMAN_ROOT/cross-repo-mirror/$SIBLING/.github/workflows/roadmap-drift.yml" \
"$SIBLING_ROOT/.github/workflows/roadmap-drift.yml"

# Stage the pinned consolidated roadmap so the offline smoke test
# matches sibling CI (which curls the same path from CONTRACT_SHA).
mkdir -p "$SIBLING_ROOT/docs/plans"
curl -sSfL \
https://raw.githubusercontent.com/tonythethompson/numan/master/scripts/check-roadmap-drift.py \
-o scripts/check-roadmap-drift.py
chmod +x scripts/check-roadmap-drift.py

# Then drop the contents of the relevant subdirectory into the sibling repo:
# cross-repo-mirror/numan-plugins/docs/roadmap.md → <sibling>/docs/roadmap.md
# cross-repo-mirror/numan-plugins/.github/workflows/roadmap-drift.yml
# → <sibling>/.github/workflows/roadmap-drift.yml

mkdir -p docs .github/workflows
cp cross-repo-mirror/<sibling>/docs/roadmap.md docs/roadmap.md
cp cross-repo-mirror/<sibling>/.github/workflows/roadmap-drift.yml \
.github/workflows/roadmap-drift.yml

# Smoke-test locally before pushing:
python scripts/check-roadmap-drift.py
# expect: 0 errors, optionally a warning about the consolidated roadmap
# being fetched URL-side. The warning is informational only.
"https://raw.githubusercontent.com/tonythethompson/numan/${CONTRACT_SHA}/docs/plans/consolidated-multi-repo-roadmap.md" \
-o "$SIBLING_ROOT/docs/plans/consolidated-multi-repo-roadmap.md"

# Smoke-test locally before pushing (from the sibling working tree):
cd "$SIBLING_ROOT"
CONSOLIDATED_ROADMAP=docs/plans/consolidated-multi-repo-roadmap.md \
LOCAL_ROADMAP=docs/roadmap.md \
python scripts/check-roadmap-drift.py
# expect: 0 errors
```

## Mirror contract
Expand All @@ -70,12 +90,13 @@ phrases, new SHIPPED_MARKERS, stronger structural checks), the mirror
artifacts in this directory MUST stay in sync. The recommended
workflow:

1. Update `scripts/check-roadmap-drift.py` in `numan` and run its tests
(the negative-test proves it still bites).
2. Update each sibling's local CI workflow to mirror the change — most
changes are confined to the script itself; the workflow is just a
thin courier that `curl`s the consolidated roadmap and runs the script
with `CONSOLIDATED_ROADMAP=<path>`.
3. Bump the pinned `commit` SHA the workflow fetches (currently
`tonythethompson/numan@master`; the URL can be pinned to a tag for
reproducibility once a roadmap release cadence exists).
1. Update `scripts/check-roadmap-drift.py` in `numan` together with the
consolidated roadmap (or wait for a contract bump).
2. Bump the contract via `scripts/bump-contract.sh` — it opens a
coordinated PR set that publishes the new tag and updates all three
sibling workflow yml files in lockstep. **Never** edit this README
pin in isolation; the bump script is the only sanctioned path.
3. The contract tag is the human-readable handle; the SHA pinned into
each workflow is the literal fetch target. Both must agree, and
`scripts/bump-contract.sh` runs the agreement check locally before
each PR issuance.
57 changes: 46 additions & 11 deletions cross-repo-mirror/numan-plugins/.github/workflows/roadmap-drift.yml
Original file line number Diff line number Diff line change
@@ -1,9 +1,10 @@
name: Roadmap drift

# Mirror of numan/scripts/check-roadmap-drift.py runner. Fetches the
# canonical consolidated roadmap from numan@master so the sibling repo
# never holds a stale copy. Pinned actions-commit SHA matches
# numan/.github/workflows/ci.yml so future audit passes change in lockstep.
# canonical consolidated roadmap + drift script from numan pinned to
# the roadmap-contract v1 SHA so the guardrail cannot drift silently.
# Bumping the contract is a coordinated operation across all three
# repos; see scripts/bump-contract.sh in numan.

on:
pull_request:
Expand All @@ -16,30 +17,64 @@ permissions:

jobs:
roadmap-drift:
name: Roadmap drift against numan consolidated roadmap
name: Roadmap drift against numan consolidated roadmap (contract v1)
runs-on: ubuntu-latest
env:
# Roadmap contract v1 pin. Bumping requires scripts/bump-contract.sh
# in tonythethompson/numan — never edit this in isolation.
CONTRACT_TAG: numan-roadmap-contract/v1
CONTRACT_SHA: 60a015fec1b25886f139d568edb1a2ff6df212f3
steps:
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
- name: Fetch consolidated roadmap from numan@master
- name: Verify pinned SHA still resolves to tag
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
tagged_sha=$(curl --fail-with-body --silent --show-error \
-H "Authorization: Bearer ${GITHUB_TOKEN}" \
-H "Accept: application/vnd.github+json" \
-H "User-Agent: numan-ci" \
"https://api.github.com/repos/tonythethompson/numan/git/refs/tags/${CONTRACT_TAG}" \
| python -c "
import json, os, sys, urllib.request
ref = json.load(sys.stdin)
sha = ref['object']['sha']
if ref['object']['type'] == 'tag':
headers = {
'Accept': 'application/vnd.github+json',
'User-Agent': 'numan-ci',
}
token = os.environ.get('GITHUB_TOKEN', '')
if token:
headers['Authorization'] = f'Bearer {token}'
req = urllib.request.Request(ref['object']['url'], headers=headers)
tag = json.load(urllib.request.urlopen(req))
sha = tag['object']['sha']
print(sha)
")
if [ "$tagged_sha" != "$CONTRACT_SHA" ]; then
echo "::error::Tag $CONTRACT_TAG resolves to $tagged_sha but CONTRACT_SHA=$CONTRACT_SHA."
echo "Run scripts/bump-contract.sh in numan to coordinate the bump across all three repos."
exit 1
fi
echo "OK: $CONTRACT_TAG pinned to $CONTRACT_SHA"
- name: Fetch consolidated roadmap from pin (numan@$CONTRACT_TAG -> $CONTRACT_SHA)
run: |
mkdir -p docs/plans
curl --fail-with-body --silent --show-error --location \
https://raw.githubusercontent.com/tonythethompson/numan/master/docs/plans/consolidated-multi-repo-roadmap.md \
https://raw.githubusercontent.com/tonythethompson/numan/$CONTRACT_SHA/docs/plans/consolidated-multi-repo-roadmap.md \
-o docs/plans/consolidated-multi-repo-roadmap.md
# Sanity check: the file must not be empty AND must mention this
# repo by path so we know we didn't grab a placeholder document.
test -s docs/plans/consolidated-multi-repo-roadmap.md
grep -q "numan-plugins/docs/roadmap.md" docs/plans/consolidated-multi-repo-roadmap.md
env:
# Pin via tag once a roadmap release cadence exists; master is OK
# for the first cut because the consolidated doc is the canonical
# cross-repo authority referenced by all three repos.
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Pull check-roadmap-drift.py from numan@master
- name: Pull check-roadmap-drift.py from pin (numan@$CONTRACT_TAG -> $CONTRACT_SHA)
run: |
mkdir -p scripts
curl --fail-with-body --silent --show-error --location \
https://raw.githubusercontent.com/tonythethompson/numan/master/scripts/check-roadmap-drift.py \
https://raw.githubusercontent.com/tonythethompson/numan/$CONTRACT_SHA/scripts/check-roadmap-drift.py \
-o scripts/check-roadmap-drift.py
chmod +x scripts/check-roadmap-drift.py
- name: Run drift check
Expand Down
4 changes: 2 additions & 2 deletions cross-repo-mirror/numan-plugins/docs/roadmap.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,10 +6,10 @@ catalog intake, signing, plugin backfills, client compat, lifecycle
evidence, and the active-plugin gate — lives in the consolidated
cross-repo plan:

[**`numan/docs/plans/consolidated-multi-repo-roadmap.md`**](https://github.com/tonythethompson/numan/blob/master/docs/plans/consolidated-multi-repo-roadmap.md)
[**`numan/docs/plans/consolidated-multi-repo-roadmap.md`**](https://github.com/tonythethompson/numan/blob/60a015fec1b25886f139d568edb1a2ff6df212f3/docs/plans/consolidated-multi-repo-roadmap.md)

The cross-repo drill is enforced by
[`scripts/check-roadmap-drift.py`](https://github.com/tonythethompson/numan/blob/master/scripts/check-roadmap-drift.py),
[`scripts/check-roadmap-drift.py`](https://github.com/tonythethompson/numan/blob/60a015fec1b25886f139d568edb1a2ff6df212f3/scripts/check-roadmap-drift.py),
which CI runs at `.github/workflows/roadmap-drift.yml` and which fails
this PR if the local roadmap drifts from the consolidated truth.

Expand Down
Loading