Skip to content

Ci: repair the example issue-to-PR workflow - #172

Merged
JumpLink merged 2 commits into
mainfrom
fix/issue-to-pr-example-pipeline
Aug 24, 2026
Merged

Ci: repair the example issue-to-PR workflow#172
JumpLink merged 2 commits into
mainfrom
fix/issue-to-pr-example-pipeline

Conversation

@JumpLink

Copy link
Copy Markdown
Owner

Every [example] submission since #144 dropped the yarn.lock has failed. Issues #169, #170 and #171 are all stranded.

What was broken

1. The workflow was still a yarn workflow. It ran yarn install --immutable + yarn workspace @learn6502/examples run build on a plain ubuntu-latest runner. Yarn 1 ignored --immutable and wrote a fresh yarn.lock (which the git add -A would have committed), then the examples build died on spawn gjs ENOENT — ubuntu-latest ships no gjs. CI already solved this by running its GJS jobs in a fedora:43 container.

2. The examples build produced nothing, and exited 0 anyway. build.ts called buildExamples() without awaiting it; under GJS the process ends when the module body does, so it was torn down after the first directory. No index.ts written, examples.ts never regenerated — and a green step. Fixed in the first commit, along with two defects it was hiding: slugToCamelCase left a dash in identifiers when a digit followed it (line-buster-6502lineBuster-6502, which does not parse), and the directory order came straight from readdir, making the committed examples.ts filesystem-dependent.

3. Nothing checked that a submission works. No assemble, no type check, no assertion that the example is exported anywhere.

4. A failed submission was unrecoverable and silent. The issues event fires once, so a fixed workflow could never be retried, and a rejected contributor got no explanation.

What this changes

  • Runs in fedora:43 (GJS 1.86, the gjsify target) and installs with gjsify install --immutable. Build steps use the CLI version gjsify-lock.json pins, not the floating bootstrap copy.
  • Parsing and validation move to .github/scripts/example-from-issue.mjs: slug shape, allowed license, the 2048-char displayMemory, an already-used slug, and real TypeScript string escaping instead of shell + python3 json.dumps.
  • Three gates before a PR is opened: the source must assemble with the headless learn6502 CLI, the example must actually appear in examples.ts, and the generated index.ts must type-check against app-web.
  • workflow_dispatch(issue) so a submission can be retried without opening a new issue.
  • On failure the reason is posted back to the issue.
  • Explicit git add paths instead of git add -A, and every untrusted value reaches shell/JS through env rather than expression interpolation.

Verification

Ran the full pipeline locally against issues #169 and #171:

dino-run           Code assembled successfully, 749 bytes.
line-buster-6502   Code assembled successfully, 1390 bytes.

Both render, build into examples.ts, type-check via gjsify workspace @learn6502/app-web check, and run to completion under learn6502 run. The [example] placeholder body of #170 is rejected with the message the contributor needs.

https://claude.ai/code/session_01FrdNDUY1rJWBJsDU1KAwKj

The examples build spawned `buildExamples()` without awaiting it. Under GJS
the process ends when the module's synchronous body does, so the promise chain
was torn down after the first directory: no index.ts was written, examples.ts
was never regenerated, and the script still exited 0. Both CI and the
issue-to-PR workflow therefore "built" the examples while producing nothing.

Await the build at the top level, and while here fix two defects it hid:

- slugToCamelCase only un-dashed `-[a-z]`, so a slug with a digit after a dash
  ('line-buster-6502') generated the identifier 'lineBuster-6502', which does
  not parse.
- the directory list came straight from readdir, making the committed
  examples.ts order filesystem-dependent.

Claude-Session: https://claude.ai/code/session_01FrdNDUY1rJWBJsDU1KAwKj
Every example submitted since the yarn.lock was dropped (#144) failed. The
workflow still ran `yarn install --immutable` + `yarn workspace ... build` on a
plain ubuntu runner, so yarn 1 silently wrote a fresh lockfile (which `git add
-A` would have committed) and the examples build died on `spawn gjs ENOENT` —
ubuntu-latest ships no gjs.

- Run in the fedora:43 container the CI type-check job uses, and install with
  `gjsify install --immutable` per gjsify-lock.json. Build steps use the
  workspace-pinned CLI, not the floating bootstrap copy.
- Parse and validate the submission in .github/scripts/example-from-issue.mjs
  instead of shell + python json escaping: slug, license, the 2048-char
  displayMemory, an unused slug, and proper TS string escaping.
- Gate on the example actually working: it must assemble with the headless
  `learn6502` CLI, land in examples.ts, and type-check against app-web.
- Add workflow_dispatch(issue) so a submission can be retried — the issues
  event fires once, which is why the three open submissions were stranded.
- Report the reason back on the issue when a submission is rejected, instead of
  leaving the contributor with silence.
- Stage explicit paths rather than `git add -A`, and pass every untrusted value
  through env instead of splicing it into shell or JS.

Claude-Session: https://claude.ai/code/session_01FrdNDUY1rJWBJsDU1KAwKj
@JumpLink
JumpLink force-pushed the fix/issue-to-pr-example-pipeline branch from ca2ba51 to 52ce4f1 Compare August 24, 2026 07:19
@JumpLink
JumpLink merged commit ea4605a into main Aug 24, 2026
3 checks passed
@JumpLink
JumpLink deleted the fix/issue-to-pr-example-pipeline branch August 24, 2026 07:56
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.

1 participant