@guiho/runx 0.2.5 → 0.2.6
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +14 -0
- package/DOCS.md +5 -3
- package/docs/decisions/decisions.xdocs.md +3 -0
- package/docs/decisions/windows-self-upgrade.md +98 -0
- package/docs/plans/plans.xdocs.md +3 -0
- package/docs/plans/windows-self-upgrade.md +129 -0
- package/docs/reviews/implementation/implementation.xdocs.md +3 -0
- package/docs/reviews/implementation/windows-self-upgrade-review.md +79 -0
- package/docs/reviews/plans/plans.xdocs.md +3 -0
- package/docs/reviews/plans/windows-self-upgrade-review.md +71 -0
- package/docs/validation/validation.xdocs.md +3 -0
- package/docs/validation/windows-self-upgrade.md +85 -0
- package/library/self-management.d.ts +5 -4
- package/library/self-management.d.ts.map +1 -1
- package/library/self-management.js +66 -9
- package/package.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -15,6 +15,20 @@ owner: runx
|
|
|
15
15
|
|
|
16
16
|
# Changelog
|
|
17
17
|
|
|
18
|
+
## 0.2.6 - 2026-07-14
|
|
19
|
+
|
|
20
|
+
### Added
|
|
21
|
+
|
|
22
|
+
- Added Windows CI coverage for replacing a running executable, verifying the
|
|
23
|
+
installed target version, cleaning the old image, and rolling back failures.
|
|
24
|
+
|
|
25
|
+
### Fixed
|
|
26
|
+
|
|
27
|
+
- Fixed `runx upgrade` on Windows so it replaces and verifies `runx.exe` before
|
|
28
|
+
reporting success instead of returning a detached `scheduled: true` state.
|
|
29
|
+
- Failed Windows replacements now restore the previous executable, report a
|
|
30
|
+
non-zero error, and clean `.new` and `.old` update files safely.
|
|
31
|
+
|
|
18
32
|
## 0.2.4 - 2026-07-14
|
|
19
33
|
|
|
20
34
|
### Added
|
package/DOCS.md
CHANGED
|
@@ -122,9 +122,11 @@ runx agents install local
|
|
|
122
122
|
|
|
123
123
|
`devops/install.ps1` installs Windows assets and `devops/install.sh` installs
|
|
124
124
|
macOS/Linux assets from GitHub Releases into the user's local bin directory.
|
|
125
|
-
`runx upgrade` uses the matching release asset and replaces a native executable
|
|
126
|
-
|
|
127
|
-
the
|
|
125
|
+
`runx upgrade` uses the matching release asset and replaces a native executable.
|
|
126
|
+
On Windows it renames the running executable, installs and verifies the target
|
|
127
|
+
version at the original path, and restores the previous executable if replacement
|
|
128
|
+
fails. Only cleanup of the renamed old image is deferred until RunX exits.
|
|
129
|
+
`runx uninstall` removes the same native executable and supports `--dry-run`.
|
|
128
130
|
|
|
129
131
|
When the latest GitHub Release matches the installed version, `runx upgrade` prints `Already up to date.` and exits successfully without downloading or scheduling executable replacement. JSON output reports `upToDate: true`.
|
|
130
132
|
|
|
@@ -8,17 +8,20 @@ files:
|
|
|
8
8
|
citty-cli-migration.md: Defines the accepted full migration from handwritten parsing and routing to Citty.
|
|
9
9
|
mirror-automatic-push.md: Defines automatic Mirror release pushes with a synchronized protected-main gate.
|
|
10
10
|
npm-trusted-publishing.md: Defines the accepted npm OIDC publishing workflow and protected-branch patch-release trial.
|
|
11
|
+
windows-self-upgrade.md: Defines synchronous Windows executable replacement, verification, rollback, and cleanup.
|
|
11
12
|
documents:
|
|
12
13
|
alpha-boundaries.md: Decision record for RunX alpha boundaries.
|
|
13
14
|
citty-cli-migration.md: Decision record for the complete Citty CLI parser and router migration.
|
|
14
15
|
mirror-automatic-push.md: Accepted release-policy decision for Mirror push=true and protected delivery ordering.
|
|
15
16
|
npm-trusted-publishing.md: Decision record for GitHub Actions npm trusted publishing and the first automated patch release.
|
|
17
|
+
windows-self-upgrade.md: Accepted design for synchronous and recoverable Windows native self-upgrade.
|
|
16
18
|
tags:
|
|
17
19
|
- decisions
|
|
18
20
|
keywords:
|
|
19
21
|
- runx
|
|
20
22
|
- decisions
|
|
21
23
|
- mirror push
|
|
24
|
+
- windows self-upgrade
|
|
22
25
|
flags: []
|
|
23
26
|
status: stable
|
|
24
27
|
---
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RunX Windows Self-Upgrade
|
|
3
|
+
purpose: Define synchronous and recoverable replacement of the running RunX executable on Windows.
|
|
4
|
+
description: Records the accepted Windows executable-swap, verification, rollback, cleanup, testing, and patch-release behavior.
|
|
5
|
+
created: 2026-07-14
|
|
6
|
+
flags:
|
|
7
|
+
- accepted
|
|
8
|
+
- implemented
|
|
9
|
+
tags:
|
|
10
|
+
- decisions
|
|
11
|
+
- cli
|
|
12
|
+
- windows
|
|
13
|
+
keywords:
|
|
14
|
+
- runx upgrade
|
|
15
|
+
- windows self-upgrade
|
|
16
|
+
- executable replacement
|
|
17
|
+
- rollback
|
|
18
|
+
owner: runx-decisions
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# RunX Windows Self-Upgrade
|
|
22
|
+
|
|
23
|
+
## Context
|
|
24
|
+
|
|
25
|
+
RunX 0.2.5 downloads a Windows update to `runx.exe.new`, starts a detached
|
|
26
|
+
`cmd.exe` process, reports `scheduled: true`, and exits without confirming that
|
|
27
|
+
the installed executable was replaced. The detached move can fail silently and
|
|
28
|
+
leave later invocations on the previous version.
|
|
29
|
+
|
|
30
|
+
Windows permits the running executable to be renamed while its image remains
|
|
31
|
+
mapped. A new executable can then be installed at the original path before the
|
|
32
|
+
current RunX process exits. Only deletion of the renamed, still-running image
|
|
33
|
+
must wait until the process releases it.
|
|
34
|
+
|
|
35
|
+
## Decision
|
|
36
|
+
|
|
37
|
+
`runx upgrade` on Windows will complete the executable swap synchronously:
|
|
38
|
+
|
|
39
|
+
1. Download the selected release asset to `runx.exe.new`.
|
|
40
|
+
2. Remove any stale backup that is not in use.
|
|
41
|
+
3. Rename the installed `runx.exe` to `runx.exe.old`.
|
|
42
|
+
4. Rename `runx.exe.new` to the canonical installed path.
|
|
43
|
+
5. Execute the newly installed path with `--version` and require the expected
|
|
44
|
+
target version and a zero exit code.
|
|
45
|
+
6. If installation or verification fails, remove the failed replacement and
|
|
46
|
+
restore `runx.exe.old` to the canonical path.
|
|
47
|
+
7. After successful verification, delete the backup immediately when possible;
|
|
48
|
+
when Windows still locks the running image, schedule only that backup cleanup
|
|
49
|
+
for after the current process exits.
|
|
50
|
+
|
|
51
|
+
The upgrade result reports `scheduled: false`. Deferred deletion of the old,
|
|
52
|
+
renamed image is cleanup and does not defer or weaken successful replacement.
|
|
53
|
+
|
|
54
|
+
## Error Handling
|
|
55
|
+
|
|
56
|
+
- A replacement failure must throw a concise `RunXError` and produce a non-zero
|
|
57
|
+
CLI exit code.
|
|
58
|
+
- Rollback must be attempted whenever the installed executable was renamed but
|
|
59
|
+
the new executable was not verified.
|
|
60
|
+
- If rollback also fails, the error must report both the replacement and
|
|
61
|
+
recovery failures and identify the affected installed path.
|
|
62
|
+
- The `.new` file must be removed after failures and must not remain after a
|
|
63
|
+
successful rename.
|
|
64
|
+
- A locked stale `.old` file must stop a new upgrade before the current
|
|
65
|
+
executable is modified.
|
|
66
|
+
|
|
67
|
+
## Testing
|
|
68
|
+
|
|
69
|
+
Automated coverage must prove that:
|
|
70
|
+
|
|
71
|
+
- the configured Windows self path contains the downloaded replacement before
|
|
72
|
+
`upgradeSelf()` resolves;
|
|
73
|
+
- successful Windows upgrades report `scheduled: false`;
|
|
74
|
+
- no `.new` file remains after success;
|
|
75
|
+
- a failed replacement restores the original executable;
|
|
76
|
+
- the equal-version no-op remains unchanged;
|
|
77
|
+
- `runx -h` continues to work outside a manifest directory.
|
|
78
|
+
|
|
79
|
+
The full release gate is `bun run typecheck`, `bun test`, `bun run build`,
|
|
80
|
+
`bun run binary`, `bun run binaries`, and strict XDocs validation.
|
|
81
|
+
|
|
82
|
+
## Release
|
|
83
|
+
|
|
84
|
+
After implementation and validation, use Mirror to plan and apply the next
|
|
85
|
+
patch. Update the changelog before applying the Mirror-managed version. Do not
|
|
86
|
+
hand-edit the package version or create the release tag manually.
|
|
87
|
+
|
|
88
|
+
## Acceptance Criteria
|
|
89
|
+
|
|
90
|
+
- When `runx upgrade` succeeds, the executable at the installed path is already
|
|
91
|
+
the target release.
|
|
92
|
+
- A same-terminal or new-terminal invocation immediately reports the target
|
|
93
|
+
version.
|
|
94
|
+
- The command never reports `scheduled: true` for upgrade replacement.
|
|
95
|
+
- Replacement and verification failures are visible and recover the old binary.
|
|
96
|
+
- Temporary update files are cleaned up safely.
|
|
97
|
+
- Issue #9 has automated Windows regression coverage and issue #1 remains
|
|
98
|
+
covered by the Citty help tests.
|
|
@@ -8,11 +8,13 @@ files:
|
|
|
8
8
|
citty-cli-migration.md: Executes the full Citty command-tree migration, compatibility validation, protected delivery, and patch release.
|
|
9
9
|
mirror-automatic-push.md: Enables automatic Mirror release pushes with a synchronized protected-main safety gate.
|
|
10
10
|
npm-trusted-publishing-release.md: Sequences workflow implementation, protected delivery, Mirror patching, and public trusted-publishing verification.
|
|
11
|
+
windows-self-upgrade.md: Sequences synchronous Windows replacement, rollback coverage, validation, issue closure, and patch delivery.
|
|
11
12
|
documents:
|
|
12
13
|
alpha-implementation.md: Plan used for the initial RunX implementation.
|
|
13
14
|
citty-cli-migration.md: Active plan for replacing handwritten CLI parsing and routing with Citty and delivering its patch release.
|
|
14
15
|
mirror-automatic-push.md: Approved plan for configuring, validating, and merging Mirror push=true without applying another release.
|
|
15
16
|
npm-trusted-publishing-release.md: Approved executable plan for the RunX 0.2.1 trusted-publishing release trial.
|
|
17
|
+
windows-self-upgrade.md: Approved executable plan for fixing Windows native self-upgrade and delivering its patch.
|
|
16
18
|
tags:
|
|
17
19
|
- plans
|
|
18
20
|
keywords:
|
|
@@ -20,6 +22,7 @@ keywords:
|
|
|
20
22
|
- implementation
|
|
21
23
|
- citty
|
|
22
24
|
- mirror push
|
|
25
|
+
- windows self-upgrade
|
|
23
26
|
flags: []
|
|
24
27
|
status: stable
|
|
25
28
|
---
|
|
@@ -0,0 +1,129 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RunX Windows Self-Upgrade Plan
|
|
3
|
+
purpose: Execute the accepted synchronous and recoverable Windows self-upgrade design.
|
|
4
|
+
description: Sequences executable replacement, regression coverage, documentation, validation, issue closure, and the next Mirror patch.
|
|
5
|
+
created: 2026-07-14
|
|
6
|
+
flags:
|
|
7
|
+
- approved
|
|
8
|
+
- executed
|
|
9
|
+
tags:
|
|
10
|
+
- plans
|
|
11
|
+
- cli
|
|
12
|
+
- windows
|
|
13
|
+
keywords:
|
|
14
|
+
- runx upgrade
|
|
15
|
+
- executable replacement
|
|
16
|
+
- rollback
|
|
17
|
+
- patch release
|
|
18
|
+
owner: runx-plans
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# RunX Windows Self-Upgrade Plan
|
|
22
|
+
|
|
23
|
+
## Sources
|
|
24
|
+
|
|
25
|
+
- [Windows Self-Upgrade Decision](../decisions/windows-self-upgrade.md)
|
|
26
|
+
- [Alpha Command Catalog Requirements](../requirements/alpha-command-catalog.md)
|
|
27
|
+
- [CLI Architecture](../architecture/cli-architecture.md)
|
|
28
|
+
- GitHub issues `CGuiho/runx#9` and `CGuiho/runx#1`
|
|
29
|
+
|
|
30
|
+
## Unit 1: Replace the Windows Executable Synchronously
|
|
31
|
+
|
|
32
|
+
- Goal: Make `upgradeSelf()` return successfully only after the canonical
|
|
33
|
+
Windows executable path contains and runs the target release.
|
|
34
|
+
- Owner: `C:\GUIHO\runx`.
|
|
35
|
+
- Dependencies: Accepted Windows self-upgrade decision.
|
|
36
|
+
- Files:
|
|
37
|
+
- `source/self-management.ts`
|
|
38
|
+
- `source/types.ts` only if the result contract must change
|
|
39
|
+
- Behavior:
|
|
40
|
+
- Download to `.new`, rename the installed executable to `.old`, install the
|
|
41
|
+
new file at the original path, and verify `<self-path> --version`.
|
|
42
|
+
- Return `scheduled: false` after verified replacement.
|
|
43
|
+
- On failure, remove the failed target, restore `.old`, remove `.new`, and
|
|
44
|
+
return a non-zero `RunXError` with recovery context.
|
|
45
|
+
- Delete `.old` immediately when possible; defer only deletion of the locked
|
|
46
|
+
old image after the current process exits.
|
|
47
|
+
- Data/schema, auth, permissions, and cache: No application data, schema,
|
|
48
|
+
authentication, authorization, or cache impact. Filesystem write permission
|
|
49
|
+
to the installed executable remains required.
|
|
50
|
+
- Acceptance: The installed path is the verified target before
|
|
51
|
+
`upgradeSelf()` resolves and no `.new` file remains.
|
|
52
|
+
- Stop conditions: Stop if Windows cannot rename the running executable, the
|
|
53
|
+
target cannot be verified, rollback cannot restore the installed path, or
|
|
54
|
+
unrelated self-management behavior changes.
|
|
55
|
+
|
|
56
|
+
## Unit 2: Add Regression Coverage
|
|
57
|
+
|
|
58
|
+
- Goal: Prove the fixed upgrade contract and preserve the already-correct help
|
|
59
|
+
behavior from issue #1.
|
|
60
|
+
- Dependencies: Unit 1 complete.
|
|
61
|
+
- Files:
|
|
62
|
+
- `source/guiho-runx.spec.ts` or a focused colocated self-management spec
|
|
63
|
+
- existing `source/cli.spec.ts` only if help coverage needs correction
|
|
64
|
+
- Checks:
|
|
65
|
+
- Mock the GitHub latest-release response and downloaded Windows binary.
|
|
66
|
+
- Configure `RUNX_SELF_PATH` to an isolated executable path.
|
|
67
|
+
- Assert the replacement exists at that path before the promise resolves,
|
|
68
|
+
reports `scheduled: false`, runs as the expected version, and leaves no
|
|
69
|
+
`.new` file.
|
|
70
|
+
- Force verification failure and assert restoration of the original file.
|
|
71
|
+
- Run the existing `-h`/`--help` tests outside a manifest directory.
|
|
72
|
+
- Acceptance: Windows CI exercises success and rollback; issue #1 remains
|
|
73
|
+
covered without changing its implemented behavior.
|
|
74
|
+
- Stop conditions: Stop on nondeterministic process cleanup, leaked test
|
|
75
|
+
processes, platform-dependent assertions on non-Windows runners, or failure
|
|
76
|
+
to restore global fetch/environment state.
|
|
77
|
+
|
|
78
|
+
## Unit 3: Align Documentation and Validate
|
|
79
|
+
|
|
80
|
+
- Goal: Make public and structured documentation describe completed rather than
|
|
81
|
+
scheduled Windows replacement, then pass the full release gate.
|
|
82
|
+
- Dependencies: Units 1 and 2 complete.
|
|
83
|
+
- Files:
|
|
84
|
+
- `DOCS.md`
|
|
85
|
+
- `source/source.xdocs.md`
|
|
86
|
+
- `runx.xdocs.md`
|
|
87
|
+
- accepted decision and plan lifecycle flags when implementation completes
|
|
88
|
+
- Checks:
|
|
89
|
+
- `bun run typecheck`
|
|
90
|
+
- `bun test`
|
|
91
|
+
- `bun run build`
|
|
92
|
+
- `bun run binary`
|
|
93
|
+
- `bun run binaries`
|
|
94
|
+
- `xdocs meta . --documents --strict --owner runx --format json`
|
|
95
|
+
- `xdocs doctor . --warnings-as-errors --format json`
|
|
96
|
+
- `git diff --check`
|
|
97
|
+
- Acceptance: All checks pass, docs agree with behavior, and the worktree
|
|
98
|
+
contains only understood task changes.
|
|
99
|
+
- Stop conditions: Stop on any failed validation or unexpected worktree change.
|
|
100
|
+
|
|
101
|
+
## Unit 4: Close Issues and Apply the Patch
|
|
102
|
+
|
|
103
|
+
- Goal: Close both resolved GitHub issues and publish the next
|
|
104
|
+
Mirror-managed patch state.
|
|
105
|
+
- Dependencies: Unit 3 committed and the worktree clean.
|
|
106
|
+
- Sequence:
|
|
107
|
+
- Confirm issue #9 acceptance evidence and issue #1 Citty regression evidence.
|
|
108
|
+
- Close issues #9 and #1 with concise resolution comments.
|
|
109
|
+
- Run `mirror config show` and `mirror version plan patch`.
|
|
110
|
+
- Update the configured changelog with the planned next version and commit
|
|
111
|
+
release preparation.
|
|
112
|
+
- Run the full typecheck/test gate again if release preparation changes any
|
|
113
|
+
executable inputs.
|
|
114
|
+
- Apply `mirror version apply patch --yes`; allow configured commit/push/tag
|
|
115
|
+
behavior and do not hand-edit Mirror-managed fields.
|
|
116
|
+
- Acceptance: Both issues are closed, the changelog records the fix, and
|
|
117
|
+
package version, release commit, tag, and configured remote push agree.
|
|
118
|
+
- Stop conditions: Stop if an issue acceptance criterion is unproved, Mirror
|
|
119
|
+
plans an unexpected version, validation fails, or protected push is rejected.
|
|
120
|
+
|
|
121
|
+
## TODO Alignment
|
|
122
|
+
|
|
123
|
+
This is one approved, focused execution session. No additional durable TODO
|
|
124
|
+
entry or delegated component task is required.
|
|
125
|
+
|
|
126
|
+
## First Executable Unit
|
|
127
|
+
|
|
128
|
+
Unit 1: implement synchronous Windows replacement and rollback in
|
|
129
|
+
`source/self-management.ts`.
|
|
@@ -5,8 +5,10 @@ parent: runx-reviews
|
|
|
5
5
|
children: []
|
|
6
6
|
files:
|
|
7
7
|
citty-cli-migration-review.md: Reviews the implemented Citty command tree, compatibility adapter, tests, documentation, packaging, and release readiness.
|
|
8
|
+
windows-self-upgrade-review.md: Reviews synchronous Windows replacement, rollback, cleanup, tests, CI coverage, and release readiness.
|
|
8
9
|
documents:
|
|
9
10
|
citty-cli-migration-review.md: Accepted implementation review for the full Citty CLI migration.
|
|
11
|
+
windows-self-upgrade-review.md: Accepted implementation review for the Windows native self-upgrade fix.
|
|
10
12
|
tags:
|
|
11
13
|
- reviews
|
|
12
14
|
- implementation
|
|
@@ -14,6 +16,7 @@ keywords:
|
|
|
14
16
|
- runx
|
|
15
17
|
- citty
|
|
16
18
|
- implementation review
|
|
19
|
+
- windows self-upgrade
|
|
17
20
|
flags: []
|
|
18
21
|
status: stable
|
|
19
22
|
---
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RunX Windows Self-Upgrade Implementation Review
|
|
3
|
+
purpose: Review the implemented Windows self-upgrade fix against its accepted decision and plan.
|
|
4
|
+
description: Confirms synchronous replacement, target verification, rollback, cleanup, tests, CI coverage, documentation, and release readiness.
|
|
5
|
+
created: 2026-07-14
|
|
6
|
+
flags:
|
|
7
|
+
- accepted
|
|
8
|
+
tags:
|
|
9
|
+
- reviews
|
|
10
|
+
- implementation
|
|
11
|
+
- windows
|
|
12
|
+
keywords:
|
|
13
|
+
- runx upgrade
|
|
14
|
+
- executable replacement
|
|
15
|
+
- rollback
|
|
16
|
+
- windows ci
|
|
17
|
+
owner: runx-implementation-reviews
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# RunX Windows Self-Upgrade Implementation Review
|
|
21
|
+
|
|
22
|
+
## Verdict
|
|
23
|
+
|
|
24
|
+
Accepted. No blocker, high, medium, or low findings remain.
|
|
25
|
+
|
|
26
|
+
## Findings
|
|
27
|
+
|
|
28
|
+
No actionable findings. The implementation matches the approved synchronous
|
|
29
|
+
replacement and recovery design without changing manifest execution, Citty
|
|
30
|
+
routing, uninstall behavior, or non-Windows upgrade behavior.
|
|
31
|
+
|
|
32
|
+
## Acceptance Criteria
|
|
33
|
+
|
|
34
|
+
- `upgradeSelf()` renames the running Windows executable, installs the
|
|
35
|
+
downloaded target at the canonical path, and awaits `<path> --version`.
|
|
36
|
+
- Successful Windows upgrade results report `scheduled: false`.
|
|
37
|
+
- Verification failures remove the invalid replacement and restore `.old`.
|
|
38
|
+
- `.new` is absent after success and rollback.
|
|
39
|
+
- Cleanup retries deletion of the locked `.old` image after the old process
|
|
40
|
+
releases it.
|
|
41
|
+
- A Windows-only test holds a real copied executable open during replacement,
|
|
42
|
+
verifies the new binary at the canonical path before completion, and observes
|
|
43
|
+
backup cleanup.
|
|
44
|
+
- A second Windows test proves rollback after invalid-binary verification.
|
|
45
|
+
- Existing Citty tests continue to prove `-h` and `--help` outside a manifest.
|
|
46
|
+
- The GitHub workflow now runs typecheck, tests, build, and native compilation
|
|
47
|
+
on `windows-latest` in addition to the existing Linux release-matrix gate.
|
|
48
|
+
|
|
49
|
+
## Verification Evidence
|
|
50
|
+
|
|
51
|
+
- `bun run typecheck`: passed.
|
|
52
|
+
- `bun test`: 15 passed, 0 failed, 134 assertions.
|
|
53
|
+
- `bun run build`: passed.
|
|
54
|
+
- `bun run binary`: passed.
|
|
55
|
+
- `bun run binaries`: twelve native assets built and verified.
|
|
56
|
+
- Windows x64 native `-h` outside a manifest: passed.
|
|
57
|
+
- Windows x64 native `--version`: reported `0.2.5` before the release bump.
|
|
58
|
+
- CI workflow YAML parse: passed.
|
|
59
|
+
- `xdocs doctor . --warnings-as-errors --format json`: 0 errors, 0 warnings.
|
|
60
|
+
- `git diff --check`: passed.
|
|
61
|
+
|
|
62
|
+
## Documentation and TODO
|
|
63
|
+
|
|
64
|
+
`DOCS.md`, source/root/workflow XDocs descriptors, the accepted decision, plan,
|
|
65
|
+
plan review, this implementation review, and validation evidence are aligned.
|
|
66
|
+
No TODO entry is needed for this completed single-session fix.
|
|
67
|
+
|
|
68
|
+
## Residual Risk
|
|
69
|
+
|
|
70
|
+
The new `windows-latest` job cannot execute until the committed workflow reaches
|
|
71
|
+
GitHub. Local Windows coverage exercises the same live-file replacement and
|
|
72
|
+
cleanup behavior. Public upgrade from 0.2.0 to the new patch can only be verified
|
|
73
|
+
after the patch release assets exist.
|
|
74
|
+
|
|
75
|
+
## References
|
|
76
|
+
|
|
77
|
+
- [Decision](../../decisions/windows-self-upgrade.md)
|
|
78
|
+
- [Plan](../../plans/windows-self-upgrade.md)
|
|
79
|
+
- [Plan Review](../plans/windows-self-upgrade-review.md)
|
|
@@ -7,10 +7,12 @@ files:
|
|
|
7
7
|
citty-cli-migration-review.md: Reviews completeness, compatibility, safety invariants, native packaging, and release sequencing for the Citty migration.
|
|
8
8
|
mirror-automatic-push-review.md: Reviews synchronized-main safeguards, read-only validation, protected delivery, and release side effects.
|
|
9
9
|
npm-trusted-publishing-release-review.md: Reviews sequencing, acceptance criteria, safety gates, and validation for the RunX 0.2.1 trusted-publishing plan.
|
|
10
|
+
windows-self-upgrade-review.md: Reviews synchronous replacement, rollback, regression coverage, issue closure, and patch delivery.
|
|
10
11
|
documents:
|
|
11
12
|
citty-cli-migration-review.md: Ready-for-execution review of the full Citty CLI migration plan.
|
|
12
13
|
mirror-automatic-push-review.md: Ready-for-execution review of the Mirror automatic-push configuration plan.
|
|
13
14
|
npm-trusted-publishing-release-review.md: Ready-for-execution review of the npm trusted-publishing release plan.
|
|
15
|
+
windows-self-upgrade-review.md: Ready-for-execution review of the Windows native self-upgrade fix plan.
|
|
14
16
|
tags:
|
|
15
17
|
- reviews
|
|
16
18
|
- plans
|
|
@@ -20,6 +22,7 @@ keywords:
|
|
|
20
22
|
- citty
|
|
21
23
|
- mirror push
|
|
22
24
|
- trusted publishing
|
|
25
|
+
- windows self-upgrade
|
|
23
26
|
flags: []
|
|
24
27
|
status: stable
|
|
25
28
|
---
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RunX Windows Self-Upgrade Plan Review
|
|
3
|
+
purpose: Verify that the Windows self-upgrade plan is safe, complete, and executable.
|
|
4
|
+
description: Reviews replacement sequencing, rollback, tests, documentation, issue closure, and Mirror patch delivery.
|
|
5
|
+
created: 2026-07-14
|
|
6
|
+
flags:
|
|
7
|
+
- approved
|
|
8
|
+
tags:
|
|
9
|
+
- reviews
|
|
10
|
+
- plans
|
|
11
|
+
- windows
|
|
12
|
+
keywords:
|
|
13
|
+
- runx upgrade
|
|
14
|
+
- executable replacement
|
|
15
|
+
- rollback
|
|
16
|
+
- mirror patch
|
|
17
|
+
owner: runx-plan-reviews
|
|
18
|
+
---
|
|
19
|
+
|
|
20
|
+
# RunX Windows Self-Upgrade Plan Review
|
|
21
|
+
|
|
22
|
+
## Verdict
|
|
23
|
+
|
|
24
|
+
Ready for execution.
|
|
25
|
+
|
|
26
|
+
## Findings
|
|
27
|
+
|
|
28
|
+
No blocker, high, medium, or low findings remain. The plan traces to the accepted
|
|
29
|
+
Windows self-upgrade decision and existing native-upgrade requirement. Each unit
|
|
30
|
+
has a bounded outcome, named files, dependencies, acceptance criteria, checks,
|
|
31
|
+
and explicit stop conditions.
|
|
32
|
+
|
|
33
|
+
## Sequencing and Failure Safety
|
|
34
|
+
|
|
35
|
+
- Replacement precedes verification, and successful completion requires the
|
|
36
|
+
canonical path to execute the expected target version.
|
|
37
|
+
- Rollback occurs before any failure is surfaced after the old executable was
|
|
38
|
+
renamed.
|
|
39
|
+
- Deferred work is limited to deleting the locked old image; replacement itself
|
|
40
|
+
is complete before success.
|
|
41
|
+
- Issue closure and Mirror application follow implementation validation and a
|
|
42
|
+
clean committed worktree.
|
|
43
|
+
- The plan recognizes configured Mirror commit, push, and tag side effects and
|
|
44
|
+
stops on protected-push rejection.
|
|
45
|
+
|
|
46
|
+
## Coverage Review
|
|
47
|
+
|
|
48
|
+
- Success, failure, rollback, temporary-file cleanup, and platform isolation are
|
|
49
|
+
explicit.
|
|
50
|
+
- Existing `-h` coverage directly supports closing issue #1.
|
|
51
|
+
- Typecheck, unit tests, library build, local binary, the twelve-target matrix,
|
|
52
|
+
strict XDocs checks, and diff hygiene are included.
|
|
53
|
+
- No route, API, UI, data schema, authentication, authorization, cache, cloud,
|
|
54
|
+
secret, or database work is in scope.
|
|
55
|
+
|
|
56
|
+
## TODO and Documentation Alignment
|
|
57
|
+
|
|
58
|
+
The work is a single focused package session and does not need a long-running
|
|
59
|
+
TODO entry. Public `DOCS.md`, source/root XDocs descriptors, lifecycle records,
|
|
60
|
+
and the changelog are included in the correct sequence.
|
|
61
|
+
|
|
62
|
+
## First Executable Unit
|
|
63
|
+
|
|
64
|
+
Implement synchronous replacement, target-version verification, rollback, and
|
|
65
|
+
old-image cleanup in `source/self-management.ts`.
|
|
66
|
+
|
|
67
|
+
## References
|
|
68
|
+
|
|
69
|
+
- [Plan](../../plans/windows-self-upgrade.md)
|
|
70
|
+
- [Decision](../../decisions/windows-self-upgrade.md)
|
|
71
|
+
- [CLI Architecture](../../architecture/cli-architecture.md)
|
|
@@ -6,10 +6,12 @@ children: []
|
|
|
6
6
|
files:
|
|
7
7
|
alpha-implementation-summary.md: Records the completed alpha implementation, checks, and release boundaries.
|
|
8
8
|
citty-cli-migration.md: Records the complete local validation gate for the Citty command-tree migration.
|
|
9
|
+
windows-self-upgrade.md: Records Windows replacement, rollback, cleanup, native build, CI, and XDocs validation evidence.
|
|
9
10
|
documents:
|
|
10
11
|
alpha-implementation-summary.md: Validation summary for the first RunX implementation.
|
|
11
12
|
citty-cli-migration.md: Validation evidence for TypeScript, tests, native assets, npm packaging, CLI behavior, and XDocs.
|
|
12
13
|
npm-trusted-publishing-0.2.2.md: Validation evidence for the blocked 0.2.2 npm trusted-publishing retry.
|
|
14
|
+
windows-self-upgrade.md: Validation evidence for GitHub issues #9 and #1 and the Windows self-upgrade patch.
|
|
13
15
|
tags:
|
|
14
16
|
- validation
|
|
15
17
|
keywords:
|
|
@@ -17,6 +19,7 @@ keywords:
|
|
|
17
19
|
- citty
|
|
18
20
|
- tests
|
|
19
21
|
- summary
|
|
22
|
+
- windows self-upgrade
|
|
20
23
|
flags: []
|
|
21
24
|
status: stable
|
|
22
25
|
---
|
|
@@ -0,0 +1,85 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: RunX Windows Self-Upgrade Validation
|
|
3
|
+
purpose: Record verification evidence for the Windows self-upgrade fix and issue closure.
|
|
4
|
+
description: Captures Windows replacement and rollback tests, complete Bun build checks, native CLI checks, CI syntax, XDocs health, and residual release risk.
|
|
5
|
+
created: 2026-07-14
|
|
6
|
+
flags:
|
|
7
|
+
- validated
|
|
8
|
+
tags:
|
|
9
|
+
- validation
|
|
10
|
+
- windows
|
|
11
|
+
- cli
|
|
12
|
+
keywords:
|
|
13
|
+
- runx upgrade
|
|
14
|
+
- windows executable
|
|
15
|
+
- rollback
|
|
16
|
+
- issue 9
|
|
17
|
+
- issue 1
|
|
18
|
+
owner: runx-validation
|
|
19
|
+
---
|
|
20
|
+
|
|
21
|
+
# RunX Windows Self-Upgrade Validation
|
|
22
|
+
|
|
23
|
+
## Summary
|
|
24
|
+
|
|
25
|
+
The implementation is ready for commit, issue closure, and the requested
|
|
26
|
+
Mirror-managed patch. All local checks passed on Windows.
|
|
27
|
+
|
|
28
|
+
## Scope
|
|
29
|
+
|
|
30
|
+
- Synchronous Windows replacement of a running native executable.
|
|
31
|
+
- Target-version verification before successful completion.
|
|
32
|
+
- Rollback after invalid replacement verification.
|
|
33
|
+
- Deferred cleanup of the locked old executable only.
|
|
34
|
+
- Existing manifest-free `-h` behavior from issue #1.
|
|
35
|
+
- Windows CI coverage and structured documentation health.
|
|
36
|
+
|
|
37
|
+
## Commands and Results
|
|
38
|
+
|
|
39
|
+
| Check | Result | Evidence |
|
|
40
|
+
| --- | --- | --- |
|
|
41
|
+
| `bun run typecheck` | passed | TypeScript completed without diagnostics. |
|
|
42
|
+
| `bun test` | passed | 15 tests, 0 failures, 134 assertions. |
|
|
43
|
+
| `bun run build` | passed | Library output compiled. |
|
|
44
|
+
| `bun run binary` | passed | Local Windows executable compiled. |
|
|
45
|
+
| `bun run binaries` | passed | All twelve release assets built and verified. |
|
|
46
|
+
| Native `runx-windows-x64.exe -h` outside a manifest | passed | Citty usage rendered with exit code 0. |
|
|
47
|
+
| Native `runx-windows-x64.exe --version` | passed | Reported `0.2.5` before the patch bump. |
|
|
48
|
+
| `Bun.YAML.parse(.github/workflows/ci.yml)` | passed | Workflow YAML parsed successfully. |
|
|
49
|
+
| `xdocs meta . --documents --strict --owner runx --format json` | passed | Root metadata and companions valid. |
|
|
50
|
+
| `xdocs doctor . --warnings-as-errors --format json` | passed | 0 errors and 0 warnings. |
|
|
51
|
+
| `git diff --check` | passed | No whitespace errors. |
|
|
52
|
+
|
|
53
|
+
## Behavioral Evidence
|
|
54
|
+
|
|
55
|
+
`source/self-management.spec.ts` runs only on Windows for the platform-specific
|
|
56
|
+
cases. Its success case launches a copied PowerShell executable from the
|
|
57
|
+
configured self path, keeps that executable running, upgrades the same path to
|
|
58
|
+
the current Bun executable, and requires the replacement to report the expected
|
|
59
|
+
version before `upgradeSelf()` resolves. It also observes removal of `.new` and
|
|
60
|
+
eventual removal of `.old` after the running process exits.
|
|
61
|
+
|
|
62
|
+
The rollback case downloads invalid executable bytes, requires verification to
|
|
63
|
+
fail, and confirms that the original bytes are restored with neither `.new` nor
|
|
64
|
+
`.old` left behind.
|
|
65
|
+
|
|
66
|
+
The existing `source/cli.spec.ts` root-help test proves both `-h` and `--help`
|
|
67
|
+
work without a manifest, satisfying issue #1.
|
|
68
|
+
|
|
69
|
+
## Skipped or Blocked Checks
|
|
70
|
+
|
|
71
|
+
- The new GitHub-hosted `windows-latest` job is not locally runnable; it will
|
|
72
|
+
execute after the commit reaches GitHub.
|
|
73
|
+
- An actual 0.2.0-to-new-patch public upgrade is blocked until the new release
|
|
74
|
+
assets are published.
|
|
75
|
+
|
|
76
|
+
## Readiness
|
|
77
|
+
|
|
78
|
+
Ready for commit, closing GitHub issues #9 and #1, changelog preparation, and
|
|
79
|
+
the requested Mirror patch plan/apply sequence.
|
|
80
|
+
|
|
81
|
+
## References
|
|
82
|
+
|
|
83
|
+
- [Implementation Review](../reviews/implementation/windows-self-upgrade-review.md)
|
|
84
|
+
- [Plan](../plans/windows-self-upgrade.md)
|
|
85
|
+
- [Decision](../decisions/windows-self-upgrade.md)
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import type { RunXUpgradeResult, UpdateResult } from './types.js';
|
|
2
|
-
export
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
2
|
+
export { checkForLatestVersion, listAvailableVersions, uninstallSelf, upgradeSelf, };
|
|
3
|
+
declare const checkForLatestVersion: () => Promise<UpdateResult>;
|
|
4
|
+
declare const listAvailableVersions: () => Promise<string[]>;
|
|
5
|
+
declare const upgradeSelf: (dryRun: boolean) => Promise<RunXUpgradeResult>;
|
|
6
|
+
declare const uninstallSelf: (dryRun: boolean) => Promise<{
|
|
6
7
|
executablePath: string;
|
|
7
8
|
scheduled: boolean;
|
|
8
9
|
dryRun: boolean;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"self-management.d.ts","sourceRoot":"","sources":["../source/self-management.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"self-management.d.ts","sourceRoot":"","sources":["../source/self-management.ts"],"names":[],"mappings":"AAKA,OAAO,KAAK,EAAE,iBAAiB,EAAE,YAAY,EAAE,MAAM,YAAY,CAAA;AAEjE,OAAO,EACL,qBAAqB,EACrB,qBAAqB,EACrB,aAAa,EACb,WAAW,GACZ,CAAA;AAMD,QAAA,MAAM,qBAAqB,QAAa,OAAO,CAAC,YAAY,CAY3D,CAAA;AAED,QAAA,MAAM,qBAAqB,QAAa,OAAO,CAAC,MAAM,EAAE,CAKvD,CAAA;AAED,QAAA,MAAM,WAAW,WAAkB,OAAO,KAAG,OAAO,CAAC,iBAAiB,CAmBrE,CAAA;AAED,QAAA,MAAM,aAAa,WAAkB,OAAO,KAAG,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,SAAS,EAAE,OAAO,CAAC;IAAC,MAAM,EAAE,OAAO,CAAA;CAAE,CAWrH,CAAA"}
|
|
@@ -1,9 +1,11 @@
|
|
|
1
|
+
import { Buffer } from 'node:buffer';
|
|
1
2
|
import { chmod, rename, rm } from 'node:fs/promises';
|
|
2
3
|
import { basename } from 'node:path';
|
|
3
4
|
import { RunXError } from './errors.js';
|
|
4
5
|
import { readVersion } from './help.js';
|
|
6
|
+
export { checkForLatestVersion, listAvailableVersions, uninstallSelf, upgradeSelf, };
|
|
5
7
|
const repositoryApi = 'https://api.github.com/repos/CGuiho/runx/releases';
|
|
6
|
-
|
|
8
|
+
const checkForLatestVersion = async () => {
|
|
7
9
|
const currentVersion = readVersion();
|
|
8
10
|
try {
|
|
9
11
|
const response = await fetch(`${repositoryApi}/latest`, { headers: { accept: 'application/vnd.github+json' } });
|
|
@@ -18,14 +20,14 @@ export const checkForLatestVersion = async () => {
|
|
|
18
20
|
return { currentVersion, latestVersion: currentVersion, updateAvailable: false };
|
|
19
21
|
}
|
|
20
22
|
};
|
|
21
|
-
|
|
23
|
+
const listAvailableVersions = async () => {
|
|
22
24
|
const response = await fetch(`${repositoryApi}?per_page=20`, { headers: { accept: 'application/vnd.github+json' } });
|
|
23
25
|
if (!response.ok)
|
|
24
26
|
throw new RunXError(`Could not retrieve RunX releases: HTTP ${response.status}`);
|
|
25
27
|
const releases = await response.json();
|
|
26
28
|
return releases.map((release) => release.tag_name.replace(/^@guiho\/runx@/, '').replace(/^v/, ''));
|
|
27
29
|
};
|
|
28
|
-
|
|
30
|
+
const upgradeSelf = async (dryRun) => {
|
|
29
31
|
const executablePath = requireNativeExecutable();
|
|
30
32
|
const update = await checkForLatestVersion();
|
|
31
33
|
const upToDate = !update.updateAvailable && update.latestVersion === update.currentVersion && Boolean(update.url);
|
|
@@ -43,10 +45,10 @@ export const upgradeSelf = async (dryRun) => {
|
|
|
43
45
|
await rename(temporaryPath, executablePath);
|
|
44
46
|
return { ...update, executablePath, scheduled: false, upToDate: false };
|
|
45
47
|
}
|
|
46
|
-
|
|
47
|
-
return { ...update, executablePath, scheduled:
|
|
48
|
+
await replaceWindowsExecutable(temporaryPath, executablePath, update.latestVersion);
|
|
49
|
+
return { ...update, executablePath, scheduled: false, upToDate: false };
|
|
48
50
|
};
|
|
49
|
-
|
|
51
|
+
const uninstallSelf = async (dryRun) => {
|
|
50
52
|
const executablePath = requireNativeExecutable();
|
|
51
53
|
if (dryRun)
|
|
52
54
|
return { executablePath, scheduled: false, dryRun: true };
|
|
@@ -66,10 +68,65 @@ const requireNativeExecutable = () => {
|
|
|
66
68
|
throw new RunXError('Self-management requires a native RunX executable installed from a GitHub release.');
|
|
67
69
|
return executablePath;
|
|
68
70
|
};
|
|
69
|
-
const
|
|
70
|
-
const
|
|
71
|
-
|
|
71
|
+
const replaceWindowsExecutable = async (temporaryPath, executablePath, expectedVersion) => {
|
|
72
|
+
const backupPath = `${executablePath}.old`;
|
|
73
|
+
let originalMoved = false;
|
|
74
|
+
try {
|
|
75
|
+
await rm(backupPath, { force: true });
|
|
76
|
+
await rename(executablePath, backupPath);
|
|
77
|
+
originalMoved = true;
|
|
78
|
+
await rename(temporaryPath, executablePath);
|
|
79
|
+
await verifyExecutableVersion(executablePath, expectedVersion);
|
|
80
|
+
await cleanupWindowsBackup(backupPath);
|
|
81
|
+
}
|
|
82
|
+
catch (error) {
|
|
83
|
+
const replacementFailure = errorMessage(error);
|
|
84
|
+
await rm(temporaryPath, { force: true }).catch(() => undefined);
|
|
85
|
+
if (!originalMoved) {
|
|
86
|
+
throw new RunXError(`Could not replace the Windows RunX executable at ${executablePath}: ${replacementFailure}`);
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
await rm(executablePath, { force: true });
|
|
90
|
+
await rename(backupPath, executablePath);
|
|
91
|
+
}
|
|
92
|
+
catch (rollbackError) {
|
|
93
|
+
throw new RunXError(`Windows RunX upgrade failed at ${executablePath}: ${replacementFailure}. Automatic rollback also failed: ${errorMessage(rollbackError)}`);
|
|
94
|
+
}
|
|
95
|
+
throw new RunXError(`Windows RunX upgrade failed; the previous executable was restored at ${executablePath}: ${replacementFailure}`);
|
|
96
|
+
}
|
|
97
|
+
};
|
|
98
|
+
const verifyExecutableVersion = async (executablePath, expectedVersion) => {
|
|
99
|
+
const verification = Bun.spawn([executablePath, '--version'], { stdin: 'ignore', stdout: 'pipe', stderr: 'pipe' });
|
|
100
|
+
const [stdout, stderr, exitCode] = await Promise.all([
|
|
101
|
+
new Response(verification.stdout).text(),
|
|
102
|
+
new Response(verification.stderr).text(),
|
|
103
|
+
verification.exited,
|
|
104
|
+
]);
|
|
105
|
+
const actualVersion = stdout.trim();
|
|
106
|
+
if (exitCode !== 0)
|
|
107
|
+
throw new Error(`replacement exited with code ${exitCode}${stderr.trim() ? `: ${stderr.trim()}` : ''}`);
|
|
108
|
+
if (actualVersion !== expectedVersion)
|
|
109
|
+
throw new Error(`replacement reported version ${actualVersion || '<empty>'}; expected ${expectedVersion}`);
|
|
110
|
+
};
|
|
111
|
+
const cleanupWindowsBackup = async (backupPath) => {
|
|
112
|
+
try {
|
|
113
|
+
await rm(backupPath, { force: true });
|
|
114
|
+
}
|
|
115
|
+
catch { }
|
|
116
|
+
scheduleWindowsBackupCleanup(backupPath);
|
|
117
|
+
};
|
|
118
|
+
const scheduleWindowsBackupCleanup = (backupPath) => {
|
|
119
|
+
const command = 'for ($attempt = 0; $attempt -lt 300; $attempt += 1) { if (-not (Test-Path -LiteralPath $env:RUNX_BACKUP_PATH)) { exit 0 }; try { Remove-Item -LiteralPath $env:RUNX_BACKUP_PATH -Force -ErrorAction Stop; exit 0 } catch { Start-Sleep -Milliseconds 100 } }; exit 1';
|
|
120
|
+
const encodedCommand = Buffer.from(command, 'utf16le').toString('base64');
|
|
121
|
+
Bun.spawn(['cmd.exe', '/d', '/s', '/c', `powershell.exe -NoLogo -NoProfile -NonInteractive -WindowStyle Hidden -EncodedCommand ${encodedCommand}`], {
|
|
122
|
+
detached: true,
|
|
123
|
+
env: { ...process.env, RUNX_BACKUP_PATH: backupPath },
|
|
124
|
+
stdout: 'ignore',
|
|
125
|
+
stderr: 'ignore',
|
|
126
|
+
stdin: 'ignore',
|
|
127
|
+
});
|
|
72
128
|
};
|
|
129
|
+
const errorMessage = (error) => error instanceof Error ? error.message : String(error);
|
|
73
130
|
const compareVersions = (left, right) => {
|
|
74
131
|
const parse = (value) => value.split(/[.+-]/).map((part) => Number.parseInt(part, 10) || 0);
|
|
75
132
|
const a = parse(left);
|
package/package.json
CHANGED