Skip to content

fix(release): pipeline correctness for Windows packaging, checksums, publication ordering, and process identity - #5388

Merged
lidge-jun merged 5 commits into
devfrom
codex/260921-lane-e-release-pipeline
Sep 21, 2026
Merged

lidge-jun merged 5 commits into
devfrom
codex/260921-lane-e-release-pipeline

Conversation

@lidge-jun

@lidge-jun lidge-jun commented Sep 21, 2026 •

Copy link
Copy Markdown
Owner

Summary

Lane E of the app-runtime-ownership unit (decision D9, part one) fixes six confirmed release-pipeline defects and adds workflow-contract regression tests so each one fails CI if it regresses. Ordered commits, one fix each:

  1. Windows packaging ran bash under PowerShell. The Rename release assets step uses backslash continuations and "$VAR" expansion with no shell: bash, and the workflow sets no defaults.run.shell, so a Windows runner reads the script as PowerShell. The step now declares shell: bash.
  2. Checksums recorded a path the verifier cannot resolve. The standalone archive wrote sha256sum "dist/ocx-…" into the checksum file, but attach-release verifies with shasum -a 256 -c ./*.sha256 from inside dist/release, where no dist/ directory exists. The step now checksums the bare file name from the artifact directory, and the contract locks the whole path chain: bare recorded name, bare redirect target, the cd into the artifact directory, the dist/ocx-*.sha256 upload glob, and the flattened download beside the verifier.
  3. Publication could outrun packaging. publish declared needs: validate-dispatch only, so npm publication and the GitHub release could proceed while desktop packaging was failing, which is how a version becomes public with no app attached. publish now waits for package-standalone and package-desktop. The pre-existing assertion in tests/ci-workflows/ci-workflows.test.ts that encoded the old single-dependency shape is updated to the gated one.
  4. Service lifecycle triggers named one file of an eleven-file directory. service-lifecycle.yml and the release gate both keyed on src/service.ts while the implementation is src/service/**, and neither covered desktop/, whose shell packages and launches the service. A launchd.ts-only change produced no lifecycle run, and a release touching those files would gate on evidence that never ran. Both triggers and the gate regex now cover src/service/** and desktop/**, kept in sync. The contract test derives the service-module and desktop-surface lists from the tree itself rather than restating them.
  5. ocx.exe was not recognised as an opencodex process. isOcxCommandLine matched ocx and ocx.cmd but not the ocx.exe that scripts/build-standalone.ts and desktop/scripts/prepare-sidecar.ts both emit on Windows targets, and this predicate feeds pid identity — the shipped Windows binary was the one shape the identity check did not know. The executable token now accepts .exe: a quoted install path with spaces and any letter case matches, while lookalikes such as not-ocx.exe, myocx.exe and ocx.exes stay foreign.
  6. The desktop management client could route the admin token through a system proxy. ProxyClient builds its reqwest client with the system-proxy default enabled while attaching the admin token to loopback requests. The builder now calls .no_proxy(), and the contract test strips comments before asserting it, so a commented-out .no_proxy() cannot satisfy the check.

Security review: this PR changes release automation (.github/workflows/release.yml, .github/workflows/service-lifecycle.yml), which MAINTAINERS.md requires to carry explicit security review. The change is structured so a reviewer can assess it in separable parts: no permissions blocks change, no secrets are added, renamed, or re-scoped, and the publication-ordering commit (2) is independent of the packaging-correctness commits (1, 3). Commit 6 removes a credential-exposure default rather than adding a capability.

No GUI change; no screenshot required.

Verification

Local execution checks: NOT RUN (lane policy — no local bun test, bun run test, bun run test:changed, bun run typecheck, builds, installs, or ocx execution; hosted CI at the exact head SHA is the PR-ready gate and is reported separately).

Static verification performed instead:

  • Expected-red evidence, by source extraction against the pre-fix tree (not test execution): the old Rename release assets step contains no shell: key, so expect(step.shell).toBe("bash") fails with undefined; the old checksum arguments are sha256sum "dist/ocx-…", failing not.toContain("/"), and the old cd ../../.. fails the cd ../.. transition assertion; the old scalar needs: validate-dispatch fails the three-job sorted equality; the old gate regex has no src/service/.* or desktop/.* alternative, so src/service/state.ts tests false; the old predicate (?:ocx|opencodex)(?:\.cmd)? cannot match ocx.exe — after ocx, .exe matches neither \.cmd nor the trailing boundary; the old proxy.rs builder contains no .no_proxy().
  • End-to-end path walk (read-only): the checksum file lands at dist/ocx-*.sha256, the upload-artifact glob dist/ocx-*.sha256 matches it, the download-artifact step uses pattern: standalone-* with merge-multiple: true into dist/release, and the verifier runs shasum -a 256 -c ./*.sha256 from dist/release — the bare recorded name resolves at every hop.
  • Independent subagent audit (gpt-5.6-sol, read-only static review): both workflows parse as valid YAML; every service-lifecycle.yml trigger literal (including src/service/** and desktop/**) matches the release gate regex while src/router.ts and docs-site paths stay excluded; every new .exe positive/negative case traces correctly through the final predicate; reqwest is pinned at 0.12.24 and ClientBuilder::no_proxy() exists there, placed before .build(); git diff --check clean; no host names, IP addresses, SSH accounts, or absolute user paths in added lines. Its findings (a literal loopback IP in a test comment, checksum-chain joints left unasserted, a comment-tolerant source match) are folded into the final commits.
  • House-rule checks (static): the two new test files are registered in both scripts/test-layout/layout.json (explicit) and tests/fixtures/test-layout-expected.json; the only touched file covered by tests/fixtures/file-size-baseline.json is tests/ci-workflows/ci-workflows.test.ts at 5614 lines against a 5628 cap; git merge-tree --write-tree origin/dev HEAD produces a clean merge result.

Checklist

  • Scope stays focused and avoids unrelated cleanup. (Ten files: the two workflows, the process predicate, the desktop client, their tests, and the two test-layout maps.)
  • Docs or release notes were updated when needed. (No user-facing behaviour change; the contract tests and the in-workflow comments carry the documentation.)
  • Security-sensitive changes were reviewed for secrets, auth, and unsafe defaults. (Release automation changed with no permission or secret-scope change; the desktop client is forced direct to loopback; see the security-review note above.)

Summary by CodeRabbit

  • Bug Fixes

    • Improved recognition of Windows standalone executable command names, including .exe variants and quoted installation paths.
    • Desktop management requests now connect directly without using system-configured proxies, improving reliability for local connections.
  • Release Improvements

    • Improved release packaging and verification to ensure checksums, renamed assets, and publication steps complete in the correct order.
    • Expanded service and desktop change detection so relevant updates consistently trigger required lifecycle and release checks.

@lidge-jun
lidge-jun requested a review from Ingwannu as a code owner September 21, 2026 00:16
@chatgpt-codex-connector

chatgpt-codex-connector Bot commented Sep 21, 2026 •

Copy link
Copy Markdown

Codex Review Summary

This comment shows the latest Codex review activity on this pull request.

Review Status Commit Review trigger
📝 Code Review ✅ Completed 2026-09-21T00:19:32.080062Z 6903852 PR opened
ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review" or "@codex security review".

Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings.

@coderabbitai

coderabbitai Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Review Change StackReview Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Repository: lidge-jun/opencodex/.coderabbit.yaml

Review profile: ASSERTIVE

Plan: Advanced

Run ID: 074d5d9f-e358-485a-b16b-18e06a31df67

📥 Commits

Reviewing files that changed from the base of the PR and between 6903852 and 74cd7ec.

📒 Files selected for processing (1)
  • tests/ci-workflows/release-pipeline-contract.test.ts

Included review availability: Your plan provides up to 10 included reviews per hour; 1 remains after this review.


📝 Walkthrough

Walkthrough

The pull request updates release packaging and publication ordering, expands service lifecycle path coverage, disables desktop proxy use for loopback requests, and recognizes Windows .exe command names. New tests enforce these workflow, transport, and command-detection contracts.

Changes

Release pipeline and service lifecycle

Layer / File(s) Summary
Packaging and publication contracts
.github/workflows/release.yml, tests/ci-workflows/ci-workflows.test.ts, tests/ci-workflows/release-pipeline-contract.test.ts, scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
The release workflow stores standalone checksums in dist/release, pins the desktop rename step to Bash, and makes publish wait for both packaging jobs. Contract tests validate checksum wiring, shell declarations, job ordering, and test-layout placement.
Service lifecycle path coverage
.github/workflows/release.yml, .github/workflows/service-lifecycle.yml, tests/ci-workflows/release-pipeline-contract.test.ts
Workflow filters and the release service gate now cover src/service/** and desktop/**. Tests compare push and pull-request paths and validate the service gate pattern against implemented and unrelated paths.

Desktop direct transport

Layer / File(s) Summary
Direct proxy transport
desktop/src-tauri/src/proxy.rs, tests/clients/desktop-proxy-direct-transport.test.ts, scripts/test-layout/layout.json, tests/fixtures/test-layout-expected.json
ProxyClient::new adds .no_proxy() to the client builder. A source-level test verifies the builder call, and the test-layout mappings include the new test.

Windows command detection

Layer / File(s) Summary
Executable command matching
src/config/process-state.ts, tests/service/process-state.test.ts
isOcxCommandLine now accepts .exe command names. Tests cover quoted and unquoted paths, case variants, start commands, and invalid lookalike names.

Priority: ➖ Normal

Estimated code review effort: 3 (Moderate) | ~25 minutes

Change: Bug fix

Sequence Diagram(s)

sequenceDiagram
  participant package-standalone
  participant package-desktop
  participant publish
  participant attach-release
  package-standalone->>publish: complete standalone packaging
  package-desktop->>publish: complete desktop packaging
  publish->>attach-release: complete publication
  attach-release->>attach-release: verify dist/release checksums
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Docstring coverage is scoped to functions touched by this diff. Analyzed 6 functions across 6 files. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: Windows packaging, checksum handling, publication ordering, and process identity fixes. It is specific and related to the pull request objectives.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
  • Fix all pre-merge checks with AI
✨ Finishing Touches 💡 1
📝 Generate docstrings 💡
  • Commit to this branch
  • Create a new PR
🧪 Generate unit tests (beta)
  • Commit to this branch
  • Create a new PR

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions github-actions Bot added the bug Something isn't working label Sep 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

✅ Deterministic PR hygiene checks passed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 6903852fb6

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

|| normalized.includes("@bitkyc08/opencodex")
|| /@bitkyc08\/\.opencodex-/.test(normalized)
|| /(?:^|[\s/"'])(?:ocx|opencodex)(?:\.cmd)?(?:$|[\s"'])/.test(normalized);
|| /(?:^|[\s/"'])(?:ocx|opencodex)(?:\.cmd|\.exe)?(?:$|[\s"'])/.test(normalized);

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

P1 Badge Update the owned process-state architecture docs

This changes the command-line identity contract in src/config/process-state.ts, but the commit does not update either structure/runtime.md or structure/config.md, which structure/INDEX.md maps as owners of src/config/. Document the newly accepted Windows .exe process shape and its identity boundary in both mapped documents so the architecture SSoT remains synchronized with runtime behavior.

AGENTS.md reference: src/AGENTS.md:L11-L11

Useful? React with 👍 / 👎.

…ifiable checksums

The Windows desktop packaging lane ran a bash script under the runner default
shell: Rename release assets uses backslash continuations and "$VAR" expansion
with no shell: bash, and the workflow sets no defaults.run.shell, so a Windows
runner reads it as PowerShell. The standalone archive step also wrote checksums
naming a dist/ path, while attach-release verifies them with shasum -c from
inside dist/release, where no dist/ directory exists.

The step now declares shell: bash, and the checksum records the bare file
name it is verified against. The contract is enforced by
tests/ci-workflows/release-pipeline-contract.test.ts: every multi-line script
on a Windows runner must declare an explicit shell, and a checksum argument
must not carry a directory prefix the verifier cannot resolve. Both were
driven red against the unfixed workflow before this change.
publish declared needs: validate-dispatch only, so npm publication and the
GitHub release could proceed while desktop packaging was still failing,
which is how a version becomes public with no app attached. The job now
waits for package-standalone and package-desktop, matching the ordering
attach-release already demanded of it.

The release-pipeline contract test asserts the publish and attach-release
dependency sets, and the long-standing assertion in ci-workflows.test.ts
that encoded the old single-dependency shape is updated to the gated one.
Driven red before the change.
… the desktop shell

The lifecycle workflow and the release gate both keyed on src/service.ts,
but the service implementation is the eleven-file src/service/ directory,
and neither covered desktop/, whose shell packages and launches the
service. A change to launchd.ts or windows-scheduler.ts alone produced no
lifecycle run, and a release touching those files would have gated on
evidence that never ran.

Both triggers and the release gate regex now cover src/service/** and
desktop/**, kept in sync in both files. The contract test derives the
service module and desktop surface lists from the tree itself rather than
restating them, so a new file that falls outside the gate fails the test.
Driven red before the change.
…tity

isOcxCommandLine matched ocx and ocx.cmd but not ocx.exe, even though
scripts/build-standalone.ts and desktop/scripts/prepare-sidecar.ts both
emit ocx.exe on Windows targets and the bundled sidecar is copied as
ocx-<triple>.exe. The predicate feeds pid identity, so the shipped Windows
binary was the one shape the identity check did not know: a service restart
or a receipt-backed stop could not tell its own process from a foreign one.

The executable token now accepts .exe as well as .cmd, so a quoted install
path with spaces and any letter case matches, while lookalikes such as
not-ocx.exe, myocx.exe and ocx.exes stay foreign. Driven red in
tests/service/process-state.test.ts before the change.
ProxyClient sends the admin token to 127.0.0.1, but reqwest resolves system
proxy configuration by default, so a machine-wide proxy would receive a
credential that is only ever meant for loopback. The builder now disables
proxy resolution explicitly.

tests/clients/desktop-proxy-direct-transport.test.ts asserts the builder
carries no_proxy(), driven red before the change.
@lidge-jun
lidge-jun force-pushed the codex/260921-lane-e-release-pipeline branch from 6903852 to 74cd7ec Compare September 21, 2026 00:26
@lidge-jun

Copy link
Copy Markdown
Owner Author

리뷰 · 우선순위 73 / 80

이 PR은 릴리스가 깨지던 여섯 곳을 하나씩 고치고, 다시 깨지면 CI가 빨간불을 켜게 테스트를 붙입니다. Windows 패키징에서 bash 문법을 PowerShell이 읽던 칸에 shell: bash를 넣었습니다. 체크섬 파일에 dist/... 경로를 적어서 dist/release 안에서 검증할 때 파일을 못 찾던 칸은, 작업 폴더를 dist/로 맞춘 뒤 파일 이름만 적도록 바꿨습니다. publish가 패키징을 기다리지 않아 npm·깃허브 릴리스가 앱 없이 나갈 수 있던 칸은 package-standalone과 package-desktop을 같이 기다리게 했습니다. 서비스 수명 검사와 릴리스 문은 src/service.ts 한 파일만 보던 것을 src/service/**와 desktop/**까지 같이 보게 맞췄습니다. 프로세스인지 판별하는 정규식은 Windows의 ocx.exe를 몰랐는데, 이제 .exe도 받습니다. 데스크톱 관리 클라이언트가 관리자 토큰을 시스템 프록시로 보낼 수 있던 칸은 .no_proxy()로 막았습니다. base는 dev이고, types/config 분할·프리뷰 배포와는 무관합니다. permissions·시크릿 범위는 안 바뀌었습니다.

src/config/process-state.ts isOcxCommandLine - 테스트는 ocx.exe / opencodex.exe만 잡습니다. 그런데 desktop/scripts/prepare-sidecar.ts는 Windows에서 ocx-<triple>.exe로 복사합니다. 정규식은 ocx 바로 뒤에 .exe나 끝/공백만 허용해서, ocx-x86_64-....exe 같은 이름은 여전히 외국 프로세스로 남습니다. 서비스 pid 소유권 검사가 사이드카 명령줄을 보면 이번 수리가 그 길을 못 덮습니다.

.github/workflows/service-lifecycle.yml / release.yml 게이트 - desktop/** 전체입니다. 아이콘·위젯·문서만 바뀌어도 서비스 수명 워크플로가 돌고, 릴리스는 그 증거를 기다립니다. 의도한 커버리지와 CI 비용이 같이 커집니다.

메인테이너의 판단이 필요한 지점

릴리스 자동화 변경이라 MAINTAINERS.md상 보안 리뷰 스탬프가 필요합니다. 시크릿·permissions는 안 건드렸고, 프록시 우회는 자격 증명이 밖으로 안 나가게 막는 쪽입니다. desktop/**을 이대로 둘지, desktop/scripts/**·desktop/src-tauri/**처럼 좁힐지는 제품·CI 비용 판단입니다. 사이드카 ocx-<triple>.exe를 이번 PR에서 같이 맞출지도 정해 주세요. 열린 중복 PR은 보이지 않습니다.

너의 추천

여섯 결함 수리와 계약 테스트 방향은 맞습니다. 사이드카 파일명이 프로세스 소유권 경로에 실제로 들어오면 ocx-<triple>.exe 케이스와 테스트를 같은 PR에 넣으세요. 안 들어오면 주석으로 “사이드카 이름은 범위 밖”이라고 남기세요. desktop/**은 유지해도 되지만, 노이즈가 크면 스크립트·Tauri 쪽으로 좁히는 후속을 열어 두세요. 보안 리뷰 한 줄과 CI 초록 확인 뒤 머지해도 됩니다.

이 댓글은 grok-bot이 작성했습니다

@Ingwannu Ingwannu left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Requesting changes after explicit workflow/release security review of exact head 74cd7ec9645481f4bc60985fcc8907d1a48712d2.

The workflow fixes are directionally correct: permissions and secret scope are unchanged, package publication is ordered after both packaging jobs, the checksum chain is coherent, and .no_proxy() narrows the desktop admin-token transport to loopback. One functional blocker and one documentation obligation remain:

  1. isOcxCommandLine now recognizes only bare ocx.exe / opencodex.exe. The shipped Windows desktop sidecar is named ocx-<target-triple>.exe by desktop/scripts/prepare-sidecar.ts, so the process-identity path this PR claims to fix still classifies the packaged sidecar as foreign. Accept the exact trusted sidecar shape while continuing to reject lookalikes (not-ocx-*, myocx-*, extra suffixes), and add positive/negative tests using the actual generated filename.
  2. This changes the owned src/config/ process-identity contract but does not update the mapped structure/runtime.md and structure/config.md SSoT. Document the accepted Windows executable shapes and boundary in both.

Please also decide whether desktop/** is intentionally the service-lifecycle trigger or whether it should be narrowed to runtime-relevant desktop paths; broad coverage is safe but materially increases CI cost. Replacement exact-head CI must be fully green before integration.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants