Skip to content

ci: run reviews on the released xmd binary; publish packages to npm+JSR#82

Merged
taras merged 6 commits into
mainfrom
refactor/review-uses-release-binary
Jul 18, 2026
Merged

ci: run reviews on the released xmd binary; publish packages to npm+JSR#82
taras merged 6 commits into
mainfrom
refactor/review-uses-release-binary

Conversation

@taras

@taras taras commented Jul 18, 2026

Copy link
Copy Markdown
Owner

Motivation

The PR Review and Repo Analysis workflows run the xmd CLI from local
TypeScript source (deno run cli/src/cli.ts). They now run on the shipped
artifact — the released xmd binary — so CI exercises what users get.

Separately, all @executablemd/* library packages are published to npm
(primary)
and JSR (secondary).

Approach

A deno compile binary only resolves npm/jsr packages that were in its
compile-time graph (a runtime import of an unbundled package fails with
Could not find constraint … in the list of packages). So:

  • The binary bundles the review agent. release.yml and the build task
    compile with --include packages/code-review-agent; the review documents keep
    the bare specifier and resolve it from the bundled source. @executablemd/core
    and @executablemd/runtime already bundle because cli.ts imports them.
  • The review workflows download and run the binary. review.yml and
    repo-analysis.yml install xmd via install.sh and run it — no deno/node
    toolchain. The lint step is self-provisioned by a new EnsureOxlint
    component that curls the standalone oxlint binary and the tsgolint
    platform tarball and wires --type-aware through OXLINT_TSGOLINT_PATH.
    EnsureOxlint fails loudly on a failed download, and each workflow asserts
    the binaries exist (xmd embeds a failed component as a comment but still
    exits 0), failing the job otherwise.
  • The manifests drive publishing. scripts/build-npm.ts derives each npm
    package (name, entry points, dependencies, bin) from its deno.json /
    package.json via dnt; internal siblings resolve to their own versions and
    publish as dependencies rather than inlining. Each package.json now declares
    its real dependencies.
  • Publishing fans out per package. A reusable publish-one.yml builds and
    publishes one package to npm (OIDC) and JSR (best-effort).
    publish-packages.yml calls it once per package, ordered by needs: (leaves
    in parallel, then core, then cli). That orchestrator is generated from
    the manifests by scripts/gen-publish-workflow.ts (deno task gen:publish-workflow); CI regenerates it and fails if the committed copy
    drifts.

All package versions move to 0.2.0. Bundling core/components into the binary
so --component-dir core/components isn't needed is tracked separately in #83.

Release prerequisites

  • review.yml requires a release built with --include; it goes green once
    v0.2.0 is tagged (the current v0.1.0 binary predates it, so this branch's
    own review check — an advisory job — is red until then).
  • npm publishing uses OIDC trusted publishers (no token); configure one per
    package pointing at this repo + publish-one.yml. Link the @executablemd
    packages on JSR. Consumers of core/runtime/cli need --legacy-peer-deps
    until effection 4 is stable (its prerelease vs @effectionx/* peer ranges).

Scope confirmation

  • All changed files relate to the stated purpose
  • No drive-by refactors or "while I'm here" cleanups
  • No formatting changes mixed with functional changes

New abstractions (if any)

  • scripts/build-npm.ts — manifest-driven dnt build (consumed by
    publish-one.yml and local deno run).
  • scripts/gen-publish-workflow.ts — generates the publish orchestrator from
    the manifests (consumed by the CI staleness check and deno task).
  • .reviews/components/EnsureOxlint.md — provisions oxlint/tsgolint (consumed by
    Doctor, reached by all four review/analysis documents).
  • .github/workflows/publish-one.yml — reusable per-package publish (consumed by
    every job in publish-packages.yml).

New dependencies (if any)

  • Package: jsr:@deno/dnt (plus @std/fs, @std/path)
  • Justification: build-time only, in scripts/; transpiles the Deno TS packages
    (.ts-extension imports) into publishable npm packages with .d.ts types.

New tests (if any)

  • None. Verified via the existing suite, a local verdaccio registry (full
    build + publish of all five packages in dependency order), and end-to-end
    binary review runs (bundled review agent + live oxlint/tsgolint provisioning).

The PR-review and repo-analysis workflows ran the CLI from local source
(`deno run cli/src/cli.ts`). Run them on the shipped artifact instead.

The review documents eval-import `@executablemd/code-review-agent`. A
`deno compile` binary can only resolve npm/jsr packages present in its
compile-time graph — a package published to npm is NOT fetchable at
runtime by the binary (fails with "Could not find constraint ... in the
list of packages"). So the binary must bundle it:

- release.yml + the `build` task compile with
  `--include packages/code-review-agent`. `@executablemd/core`/`runtime`
  already resolve because cli.ts imports them statically.
- review.yml / repo-analysis.yml install the released binary via
  install.sh and run `xmd run ...`. `deno install` stays only to provide
  the `npx oxlint` toolchain the documents invoke.

Separately, publish all @executablemd library packages to npm (primary)
and JSR (secondary):

- scripts/build-npm.ts builds each package with dnt, externalizing
  sibling @executablemd deps instead of inlining them.
- publish-packages.yml builds + publishes in dependency order on the
  same `v*` tag that drives release.yml.
- Versions bumped 0.1.0 -> 0.2.0.
@github-actions

Copy link
Copy Markdown
    <!-- ERROR: Failed to evaluate expression prop "pr={pr}" on <PrPolicyReport />: pr is not defined -->
  

taras added 3 commits July 17, 2026 20:59
Use function*/yield* with effection main(), @effectionx/fs for file ops,
and until() for the dnt build + node:fs temp-dir/recursive-copy promises
(the two ops @effectionx/fs lacks). No behavior change.
…oxlint, OIDC

Addresses the plannotator review of this branch.

Manifests are the source of truth:
- Populate each package.json with real dependencies (workspace:* for internal
  siblings), description, and a bin for cli.
- build-npm.ts takes a package dir and derives name/entryPoints/deps/description
  from deno.json + package.json (validated with zod — no `as` casts, per
  AGENTS.md rule 6); internal deps resolve to the sibling's own version. Drops
  the hardcoded package table and version constant.

Self-provisioned oxlint (no deno/node in the review CI):
- New .reviews/components/EnsureOxlint.md curls the standalone oxlint binary and
  the tsgolint npm tarball, wiring type-aware via OXLINT_TSGOLINT_PATH.
- Doctor + the review/analysis docs call the provisioned oxlint instead of
  `npx oxlint`. review.yml and repo-analysis.yml drop setup-deno + deno install.

Publishing:
- publish-packages.yml uses Node 24 + OIDC trusted publishing (no NPM_TOKEN).
- Orchestration moved into scripts/publish-packages.ts (Effection); publishable
  set and dependency order are derived from the manifests.
- Documents that core/runtime/cli need --legacy-peer-deps until effection 4 is
  stable (effection prerelease vs @effectionx peer ranges).

Also: AGENTS.md gains rules 6 (no `as`) and 7 (no braceless if); scripts comply.
taras added 2 commits July 17, 2026 22:57
Replace the single publish-packages.ts orchestration script with a GitHub
job graph, per review feedback:

- Add publish-one.yml (reusable, workflow_call: package/version/ref) that builds
  one package with dnt and publishes it to npm (OIDC) + JSR (best-effort), with
  a build retry to absorb sibling propagation.
- publish-packages.yml fans out one publish-one call per package, ordered by
  `needs:` — leaves (durable-streams, runtime, code-review-agent) in parallel,
  then core, then cli. Each package is a separate, re-runnable workflow run.
- Remove scripts/publish-packages.ts.

Also restore cli/src/cli.ts to mode 644 (pnpm/bun install flips it to 755 via
the new bin field; that mode change should not be committed).
…n't be provisioned

Addresses the latest review.

Generate the publish orchestrator from the manifests:
- scripts/gen-publish-workflow.ts derives the publishable set + topological
  `needs:` graph from each member's deno.json/package.json and writes
  .github/workflows/publish-packages.yml (`deno task gen:publish-workflow`).
- ci.yml regenerates it and `git diff --exit-code`s, failing with the command
  to run if the committed file is stale — so the workflow can't drift.

Don't let oxlint provisioning fail silently:
- EnsureOxlint uses `set -euo pipefail` and no longer swallows a failed
  download (curl -f + pipefail aborts the block instead of degrading).
- review.yml and repo-analysis.yml assert the lint binaries exist after the
  run (xmd embeds a failed component as a comment but still exits 0), failing
  the job with a clear ::error:: if provisioning didn't happen.
@taras
taras merged commit bba94e9 into main Jul 18, 2026
6 of 7 checks passed
@taras
taras deleted the refactor/review-uses-release-binary branch July 18, 2026 03:29
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