Skip to content

Fix all CI/CD issues#119

Merged
konard merged 8 commits intomainfrom
issue-118-4ed21210786f
May 3, 2026
Merged

Fix all CI/CD issues#119
konard merged 8 commits intomainfrom
issue-118-4ed21210786f

Conversation

@konard
Copy link
Copy Markdown
Member

@konard konard commented May 3, 2026

Summary

Closes #118.

This PR is a CI/CD sweep across the JS and Rust pipelines, plus a deep
case study under
docs/case-studies/issue-118/ that
captures the analysis behind every change.

Highlights

  • False-positive guard — every release job now runs
    scripts/verify-release-badge.mjs after the publish step. If the
    per-version npm / crates.io badge is missing from the release
    body, the job fails with a single ::error:: line. (Fixes the
    "release exists but no badge — was it actually published?" case the
    issue calls out.)
  • Self-healing release detectionscripts/check-release-needed.mjs
    asks the registry (npm or crates.io) whether the current
    package.json/Cargo.toml version is already published, instead of
    trusting git tags. If a previous run created a tag but failed to
    publish, the next push to main recovers automatically.
  • Credential preflightscripts/preflight-credentials.mjs
    asserts each required token (GH_TOKEN, npm OIDC, crates.io
    reachability) before any publish step runs. Missing or expired
    credentials surface as a clear top-level error rather than a
    buried gh/npm failure.
  • Verbose modescripts/debug-print.mjs exposes a debug()
    helper that activates on START_DEBUG=1, RUNNER_DEBUG=1, or
    ACTIONS_STEP_DEBUG=true. Off by default per CLAUDE.md guidelines.
  • Workflow hardening — both workflows get per-job
    timeout-minutes, actions/checkout @v4 → @v6, setup-node @v4 → @v6, Node 20 → 24, if: always() && !cancelled() guards on
    downstream jobs (so transitive if: always() upstream no longer
    propagates skipped silently), and concurrency.cancel-in-progress
    scoped to non-main refs only.
  • Pre-flight syntax check — new syntax-check job runs
    scripts/check-mjs-syntax.sh (node --check over every .mjs)
    and, on PRs, scripts/simulate-fresh-merge.sh which merges the PR
    head into a throw-away worktree and re-runs the check against the
    actual merged tree.
  • Case studydocs/case-studies/issue-118/ contains the
    timeline, requirement-by-requirement breakdown, root causes,
    solution plan, comparison vs. the four link-foundation pipeline
    templates, raw inputs (issue JSON, recent run history, release
    bodies, failed-run logs), and a one-shot
    backfill-recipe.md
    for the two pre-badge JS releases.

One-shot post-merge work (documented, not run by CI)

  • Back-fill js-v0.25.5 and js-v0.26.0 release bodies using the
    recipe in docs/case-studies/issue-118/backfill-recipe.md. This is
    a manual step because mutating older releases is a one-time fix-up
    rather than something CI should re-run on every push.
  • rust-v0.13.0 / rust-v0.14.0 were referenced in earlier drafts of
    the case study as also needing back-fill — verification (see the
    recipe) showed neither tag nor release object exists for them, so
    there is nothing to PATCH.

Test plan

  • bash scripts/check-mjs-syntax.sh — 26 .mjs files parse with node --check.
  • bash scripts/simulate-fresh-merge.sh — clean merge against origin/main.
  • bun run lint (eslint) and bun run format:check (prettier) — both clean.
  • node scripts/check-file-size.mjs — every file under the 1000-line limit.
  • python3 -c 'import yaml; yaml.safe_load(...)' on both workflow files — valid.
  • Smoke-tested every new script locally (check-release-needed, preflight-credentials, verify-release-badge, backfill-release-notes --dry-run).
  • Real CI on this PR (will run automatically once marked ready for review).

Adding .gitkeep for PR creation (default mode).
This file will be removed when the task is complete.

Issue: #118
@konard konard self-assigned this May 3, 2026
konard added 4 commits May 3, 2026 13:28
Capture the investigation that backs PR #119: timeline of CI/CD
failures, requirement-by-requirement breakdown, root causes, the
solution plan, a comparison with the four link-foundation pipeline
templates, and a one-shot recipe for back-filling the two pre-badge
JS releases (js-v0.25.5, js-v0.26.0). Raw inputs (issue JSON, recent
run history, release bodies, failed-run logs) live alongside the
analysis so the next investigator can pick up where this one stopped.
- check-mjs-syntax.sh: pre-flight `node --check` over every .mjs file
  so syntax errors fail in 5s instead of inside a test job.
- simulate-fresh-merge.sh: validate the actual merged tree by merging
  the PR head into a throw-away worktree and re-running the syntax
  check. Catches conflict-resolution bugs that wouldn't show up
  against the PR head in isolation.
- debug-print.mjs: ::debug:: logger that activates on
  START_DEBUG / RUNNER_DEBUG / ACTIONS_STEP_DEBUG so verbose
  tracing can stay in the code with the default state off.

All three are ports of patterns from the link-foundation pipeline
templates (see docs/case-studies/issue-118/comparison-with-templates.md).
Closes the gap between this repo and the link-foundation pipeline
templates around release confidence.

- check-release-needed.mjs: query the public registry (npm or
  crates.io) instead of git tags to decide whether a publish is
  required. Heals automatically if a previous run pushed a tag but
  failed to publish.
- preflight-credentials.mjs: assert each required credential
  (GH_TOKEN, npm OIDC, crates.io reachability) before any publish
  step runs, emit a single ::error:: per failure so the cause is at
  the top of the run summary.
- verify-release-badge.mjs: post-condition guard that fetches the
  freshly-created release body and fails the job when the per-version
  npm/crates.io badge is missing — the false-positive case the issue
  called out.
- backfill-release-notes.mjs: one-shot helper to regenerate a release
  body for a given tag using the same template the publish path uses,
  for fixing pre-badge releases.
Adopts the patterns from the link-foundation pipeline templates:

- New `syntax-check` job runs check-mjs-syntax.sh and (on PRs)
  simulate-fresh-merge.sh, fast-failing in <30s on broken .mjs.
- Bump actions/checkout @v4@v6, setup-node @v4@v6,
  setup-dotnet @v4@v5; Node 20.x → 24.x.
- Per-job `timeout-minutes` everywhere so a hung step can't burn the
  free runner budget.
- `if: always() && !cancelled()` guards on lint/test/coverage/release
  so transitive `if: always()` upstream no longer propagates `skipped`
  silently down the chain.
- `concurrency.cancel-in-progress: github.ref != refs/heads/main` so
  in-flight main runs are never cancelled by a follow-up push.
- Release jobs (auto + instant + manual) now call:
    1. check-release-needed.mjs — registry-driven self-healing,
    2. preflight-credentials.mjs — clear error on missing token/OIDC,
    3. verify-release-badge.mjs — post-condition badge assertion.
- Rust coverage job caches the cargo-tarpaulin binary (3-4 min/run).
@konard konard changed the title [WIP] Fix all CI/CD issues Fix all CI/CD issues May 3, 2026
@konard konard marked this pull request as ready for review May 3, 2026 13:29
konard added 3 commits May 3, 2026 13:30
`git merge --no-commit` requires a committer identity even though it
won't write a commit (it still records MERGE_MSG). The GitHub runner
has no global identity configured, so the script failed with
"empty ident name" and then mis-reported it as a merge conflict.

- Configure a local user.email/user.name on the throw-away worktree
  if no identity is set, scoped to that worktree only.
- Distinguish "merge produced conflicts" from "git merge errored for
  another reason" by checking `git ls-files --unmerged` and printing
  the captured merge output before either ::error:: line.
Make the case-study claim true: each new pipeline script now imports
debug() / dumpEnv() and prints its resolved arguments + relevant env
keys when START_DEBUG, RUNNER_DEBUG, or ACTIONS_STEP_DEBUG is set.
Default-off, so no extra log noise on green runs.

Also tighten case-study/solutions.md and comparison-with-templates.md
to match what's actually shipped (drop overstated claims about
secretlint, jscpd, auto-detect crate name, and wiring debug into the
bun-based scripts — those move to follow-up).
@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

Working session summary

All checks on PR #119 pass cleanly on the latest SHA 899170f:

  • JavaScript CI/CD: Syntax Pre-Check, Detect Changes, Lint and Format, Bun tests on ubuntu/macos/windows, Code Coverage — all green. Release jobs correctly skipped (no fragments).
  • Rust CI/CD: Syntax Pre-Check, Detect Changes, Lint and Format, Tests on ubuntu/macos/windows, Code Coverage — all green. Release jobs correctly skipped.
  • PR state: OPEN, MERGEABLE, mergeStateStatus: CLEAN, no longer a draft.

PR #119 is ready for review: #119


This summary was automatically extracted from the AI working session output.

@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

🤖 Solution Draft Log

This log file contains the complete execution trace of the AI solution draft process.

💰 Cost: $17.340520

📊 Context and tokens usage:

Claude Opus 4.7: (5 sub-sessions)

  1. 113.8K / 1M (11%) input tokens, 10.3K / 128K (8%) output tokens
  2. 116.7K / 1M (12%) input tokens, 23.5K / 128K (18%) output tokens
  3. 116.2K / 1M (12%) input tokens, 37.4K / 128K (29%) output tokens
  4. 117.0K / 1M (12%) input tokens, 32.0K / 128K (25%) output tokens
  5. 44.3K / 1M (4%) input tokens, 773 / 128K (1%) output tokens

Total: (28.5K new + 487.5K cache writes + 21.7M cache reads) input tokens, 131.5K output tokens, $17.340520 cost

🤖 Models used:

  • Tool: Anthropic Claude Code
  • Requested: opus
  • Model: Claude Opus 4.7 (claude-opus-4-7)

📎 Log file uploaded as Gist (7074KB)


Now working session is ended, feel free to review and add any feedback on the solution draft.

@konard
Copy link
Copy Markdown
Member Author

konard commented May 3, 2026

✅ Ready to merge

This pull request is now ready to be merged:

  • All CI checks have passed
  • No merge conflicts
  • No pending changes

Monitored by hive-mind with --auto-restart-until-mergeable flag

@konard konard merged commit 9ac532d into main May 3, 2026
25 checks passed
konard added a commit that referenced this pull request May 3, 2026
…oken

`scripts/preflight-credentials.mjs` validated the token with
`gh api user --jq .login`. That endpoint requires a *user* identity, so
the auto-issued `secrets.GITHUB_TOKEN` (an installation token) returned
HTTP 403 "Resource not accessible by integration", and both release
pipelines failed at preflight on every push to `main` after PR #119
(runs 25282945820 and 25282945817).

Switch the probe to `gh api repos/${GITHUB_REPOSITORY}` which works for
both the installation token and personal access tokens (it only needs
`metadata: read`, already granted by every workflow). Fall back to
`gh auth status` when running locally without `GITHUB_REPOSITORY`.

Also annotate `crates-io` HTTP 404 as "package not yet published" so a
real lookup miss is not silently reported as `ok`. Switch from
`execSync` to `execFileSync` so the repo slug is passed as a literal
argv element rather than interpolated into a shell string.

See `docs/case-studies/issue-120/root-cause.md`.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Fix all CI/CD issues

1 participant