Skip to content

Post-merge cleanup: tests, fixtures, dist freshness check, publish=false fix, rust-version/edition outputs, 1.0.0#20

Merged
SVilgelm merged 10 commits into
mainfrom
chore/post-merge-cleanup
May 7, 2026
Merged

Post-merge cleanup: tests, fixtures, dist freshness check, publish=false fix, rust-version/edition outputs, 1.0.0#20
SVilgelm merged 10 commits into
mainfrom
chore/post-merge-cleanup

Conversation

@SVilgelm
Copy link
Copy Markdown
Member

@SVilgelm SVilgelm commented May 7, 2026

Follow-up to #19, addressing every item from the review and bumping to 1.0.0.

Summary

Code

  • Bug fix: publish = false was incorrectly classified as publishable. Cargo represents publish = false as [] in metadata, not false. The previous check pkg.publish !== false always returned true. Now: null and non-empty arrays are publishable; an empty array is excluded.
  • Default empty arrays for packages/publish/matrix. Previously, if cargo metadata ever returned without a packages key, downstream fromJson(...outputs.matrix) would crash on an empty string.
  • Dropped @actions/github. It was a runtime dep but never imported; bundling its @octokit/* graph was bloating dist/ for nothing.
  • Refactor: split into lib.js (testable) + one-line index.js entry. ncc still builds index.jsdist/index.js; tests import from lib.js directly.
  • Dropped dead hasOwnProperty guards (cargo metadata always emits both publish and features).

New outputs

  • rust-version — workspace MSRV: the highest rust-version declared by any package, compared numerically (so 1.10 beats 1.9). Empty string when no package declares one. Direct use case: feed into actions-rust-lang/setup-rust-toolchain.
  • edition — the highest edition used by any package (e.g. 2021, 2024). Empty string for an empty workspace.

Both are raw scalar strings (not JSON-encoded), so consumers can read them directly with ${{ steps.x.outputs.rust-version }} — no fromJson needed.

Tests

  • Unit tests (tests/parse-metadata.test.js) using node:test — no extra deps. 14 tests covering empty workspace, missing packages key, matrix-vs-features cases, every publish shape, MSRV max-across-packages with the 1.10 > 1.9 numeric edge case, and edition max-across-packages.
  • In-tree fixture projects under tests/fixtures/ exercise every code path end-to-end without depending on external repos:
    • single-public/ — one crate, default-publishable, rust-version = "1.85", edition = "2021".
    • single-private/ — one crate with publish = false, no rust-version (regression test for the publish-false bug + MSRV-empty case).
    • workspace-mixed/ — workspace with three members: app (1.85 / 2021), private-lib (no MSRV, publish = false), and feature-lib (1.90 / 2024 with default/foo/bar features). Exercises workspace iteration, the publish filter, per-feature matrix expansion, and the MSRV/edition max-across-packages logic together.
    • toolchain-pinned/ — one crate with rust-toolchain.toml pinning 1.85.0. Replaces the previous inline ToolchainPin job that scaffolded a project at runtime.

CI

  • Check job now also runs npm test.
  • Dist job rebuilds and asserts git diff --exit-code dist/, so PRs with stale bundled output are blocked instead of being patched after-the-fact.
  • Fixtures job runs the action against each in-tree fixture and asserts exact outputs (now including rust-version and edition):
    • Literal string equality on simple fixtures.
    • jq-based sorted set comparison for workspace-mixed (stable across cargo versions, since feature ordering is alphabetical).
    • For toolchain-pinned, also greps rustup toolchain list to confirm the install actually ran end-to-end — a future regression that silently skipped ensureToolchain would fail this check.
  • npm installnpm ci in both workflows for deterministic installs.
  • actions/setup-node added to both jobs for cached Node + npm installs (pinned by SHA, matching the existing convention).
  • update-dist.yml now uses a per-run branch (update-dist-<run_id>) and opens a PR via gh pr create, instead of failing on the second run when the fixed branch already exists.

Docs

  • README and action.yml document all outputs (including the two new ones), the toolchain auto-install, the matrix-features behavior, and the publish = false exclusion.

Misc

Breaking change to flag for the v1.0 cut

Packages with publish = false are now (correctly) excluded from the publish output. Prior versions included them due to the pkg.publish !== false bug. Any consumer that relied on the bug will see a behavior change.

Test plan

  • npm run all — fmt + build + test all green locally; 14/14 unit tests pass.
  • Bundled dist/index.js smoke-tested against all four fixtures locally; outputs match expectations including rust-version and edition.
  • CI: Check, Dist, Test, Fixtures, Roas, Nuspec should all pass.
  • After merge, tag v1.0.0 and move/refresh the floating v1 tag.

SVilgelm added 4 commits May 6, 2026 21:59
The repository is owned by Sergey Vilgelm, not "SV Tools".

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
- Split index.js into a one-line entry and lib.js (the testable surface).
  ncc still bundles index.js as the action entry, but the logic now lives
  in a module that can be imported by tests.
- Add node:test unit tests for parseMetadata covering the empty-workspace
  case, the missing-packages-key case, the matrix-vs-features cases, and
  every shape of the publish field.
- Fix a real bug: cargo metadata represents `publish = false` as `[]`,
  not `false`. The previous `pkg.publish !== false` check classified
  unpublishable packages as publishable. Now: `null` and non-empty arrays
  are publishable; an empty array is not.
- Default packages/publish/matrix outputs to `[]` so downstream
  `fromJson` never chokes on an empty string.
- Drop dead `hasOwnProperty` guards (cargo metadata always emits both
  `publish` and `features`).
- Drop @actions/github — it was a runtime dependency but never imported,
  and bundling its @octokit graph bloats dist/ for nothing.
- Add `"type": "module"` to make the ESM entry explicit (silences the
  MODULE_TYPELESS_PACKAGE_JSON warning under node:test).
- README: document the toolchain auto-install, the matrix-features
  behavior, and the `publish = false` exclusion.
- Bump to 0.3.1.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
checks.yml:
- Add `Dist` job that rebuilds and asserts `git diff --exit-code dist/`,
  so PRs with stale bundled output are caught up-front instead of being
  patched after-the-fact by the manual update-dist workflow.
- Add `ToolchainPin` regression test: scaffolds a project with
  `rust-toolchain.toml` pinning a specific version and verifies the
  action installs the toolchain and reads metadata. Without this, the
  rustup-1.28 fix has no CI coverage.
- Switch `npm install` to `npm ci` for deterministic, lockfile-faithful
  installs.
- Add actions/setup-node for cached node + npm installs.
- Run `npm test` in the Check job.

update-dist.yml:
- Use a per-run branch (`update-dist-<run_id>`) so back-to-back runs
  don't collide on a fixed branch name.
- Open a PR via `gh pr create` instead of just pushing the branch.
- Switch to `npm ci` for the same reasons as above.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
Four self-contained Rust fixtures under tests/fixtures/ exercise every
code path of the action without depending on external repos:

- single-public: one crate, default-publishable.
- single-private: one crate with `publish = false` — exercises the
  publish=false fix from this branch.
- workspace-mixed: workspace with three members — `app` (default),
  `private-lib` (publish = false), and `feature-lib` (default + foo +
  bar features). Exercises workspace iteration, the publish filter, and
  the per-feature matrix expansion together.
- toolchain-pinned: one crate with `rust-toolchain.toml` pinning
  1.85.0. Replaces the previous inline `ToolchainPin` job that
  scaffolded the project at runtime via heredoc.

The new `Fixtures` job runs the action against each one and asserts
exact outputs (with jq + sorted comparison for the workspace, since
feature ordering is alphabetical and we don't want to hard-code that).
For the toolchain-pinned case it also greps `rustup toolchain list` to
confirm the install actually happened — a future regression that
silently skipped ensureToolchain would fail this check.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
Copilot AI review requested due to automatic review settings May 7, 2026 05:07
The action has been in active use and the API surface (inputs and
outputs) is stable; mark 1.0.0.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

Post-merge cleanup that hardens the action’s behavior and CI by fixing Cargo publish=false handling, adding test coverage + fixtures, and enforcing dist/ freshness in workflows.

Changes:

  • Fixed publishability detection for Cargo metadata (publish=[] for publish=false) and added unit tests for all publish shapes.
  • Refactored action code into lib.js (testable) + thin index.js entry; expanded tests/fixtures to exercise real Cargo workspaces and toolchain pinning.
  • Hardened CI: deterministic npm ci, added Dist job to block stale dist/, and updated update-dist automation to open PRs per run.

Reviewed changes

Copilot reviewed 22 out of 25 changed files in this pull request and generated 4 comments.

Show a summary per file
File Description
tests/parse-metadata.test.js Adds node:test coverage for metadata parsing + publish semantics
tests/fixtures/workspace-mixed/** Adds a mixed workspace fixture (private + featureful + app)
tests/fixtures/toolchain-pinned/** Adds a pinned-toolchain fixture to verify rustup behavior
tests/fixtures/single-public/** Adds a single publishable crate fixture
tests/fixtures/single-private/** Adds a single non-publishable crate fixture
package.json Adds tests to all, switches to ESM, adjusts fmt/check globs, bumps version, drops @actions/github
lib.js Introduces testable core logic (toolchain install, cargo metadata, parsing, outputs)
index.js Becomes a thin entrypoint delegating to lib.js
README.md Documents toolchain install, matrix behavior, and publish=false exclusion
LICENSE Updates copyright holder
.github/workflows/update-dist.yml Uses setup-node + npm ci, opens PR via gh on per-run branch
.github/workflows/checks.yml Adds Dist verification job and fixture-based regression coverage

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread package.json
Comment thread .github/workflows/update-dist.yml Outdated
Comment thread .github/workflows/update-dist.yml Outdated
Comment thread lib.js Outdated
@SVilgelm SVilgelm changed the title Post-merge cleanup: tests, dist freshness check, publish=false fix, CI hardening Post-merge cleanup: tests, fixtures, dist freshness check, publish=false fix, 1.0.0 May 7, 2026
SVilgelm added 2 commits May 6, 2026 22:12
The previous descriptions were vague (\"Raw Metadata\") and the examples
used non-JSON pseudo-syntax that wouldn't actually parse. Replace them
with descriptions that:

- State that every output is a JSON-encoded string and reference
  `fromJson` for parsing.
- Use real JSON in the examples (`["foo","bar"]` rather than `[foo, bar]`).
- Document the `publish = false` exclusion and the
  restricted-registry inclusion under `publish`.
- Note that `manifest-path` determines the directory the action runs
  from, and that any adjacent `rust-toolchain.toml` is honored via
  `rustup toolchain install` before metadata is read.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
Two new outputs derived from the per-package `rust_version` and
`edition` fields in cargo metadata:

- `rust-version`: workspace MSRV — the highest `rust-version` declared
  by any package, compared numerically so `1.10` beats `1.9`. Empty
  string when no package declares one. Direct use case: feed it into
  `actions-rust-lang/setup-rust-toolchain`.
- `edition`: the highest edition used by any package in the workspace
  (e.g. `2021`, `2024`). Empty string for an empty workspace.

Both are raw scalar strings (not JSON-encoded), so consumers can read
them directly with `${{ steps.x.outputs.rust-version }}` — no
`fromJson` needed.

Test coverage:
- 6 new unit tests covering the empty case, max-across-packages, the
  numeric-vs-lexicographic edge case (1.10 > 1.9), and patch-level
  versions.
- Fixtures updated: single-public declares 1.85/2021; workspace-mixed
  declares 1.85/2021 in `app` and 1.90/2024 in `feature-lib` to
  exercise the workspace-MSRV max logic. The `Fixtures` CI job now
  asserts both new outputs for every fixture.

README and action.yml updated to document the outputs.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
@SVilgelm SVilgelm changed the title Post-merge cleanup: tests, fixtures, dist freshness check, publish=false fix, 1.0.0 Post-merge cleanup: tests, fixtures, dist freshness check, publish=false fix, rust-version/edition outputs, 1.0.0 May 7, 2026
@SVilgelm SVilgelm requested a review from Copilot May 7, 2026 05:23
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 26 changed files in this pull request and generated 4 comments.

Comment thread lib.js Outdated
Comment thread lib.js
Comment thread tests/fixtures/workspace-mixed/feature-lib/Cargo.toml
Comment thread .github/workflows/update-dist.yml Outdated
Audit pass on comments added in this branch against the project's
"only the WHY, never the WHAT, no rotting facts" guideline.

- lib.js: shorten the rustup-1.28 toolchain comment from 5 lines to 3.
  The load-bearing fact is "the proxy doesn't auto-install"; the rest
  was paraphrasing what the next line of code does.
- tests: drop the redundant `// Lexicographic compare would pick "1.9"
  over "1.10"` — the test name already says exactly that.
- checks.yml: drop the Fixtures job header comment (mostly described
  what the job does, which is obvious from the name + steps).
- checks.yml: drop the `# MSRV is max(1.85, 1.90)…` comment in the
  workspace-mixed step. It paraphrased the assertion just below it,
  and would silently rot if a fixture's version changed.

Kept (good WHYs that aren't derivable from the code): the publish-field
shape table, the compareVersion lexicographic warning, the editions
int-compare note, the jq-sort rationale, and the
ensureToolchain end-to-end verification grep.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
@SVilgelm SVilgelm requested a review from Copilot May 7, 2026 05:27
…dist

- lib.js: spawn cargo metadata with `cwd` set to the manifest's
  directory and pass the absolute manifest path. Without this, rustup
  would fall back to the default toolchain when a sub-directory
  rust-toolchain.toml was the source of truth (rustup walks up, not
  down). Metadata reading still worked, but ensureToolchain's install
  and the actual cargo invocation could silently use different
  toolchains.
- lib.js: sort features before emitting matrix entries. Cargo currently
  uses a BTreeMap (so output was already alphabetical), but this isn't
  contractual — make it explicit so consumers can rely on the order.
- lib.js: drop `{ required: true }` from `getInput("manifest-path")`
  since action.yml declares `required: false` with a default. The
  default makes the value always present, so the runtime check was
  redundant and inconsistent with the published contract.
- update-dist.yml: switch `npm run all` → `npm run build` and
  `git add .` → `git add dist/`. The bot is a dist/ rebuilder, not a
  one-shot full-format-and-test agent; this keeps automated PRs
  narrowly scoped and avoids accidentally bundling unrelated source
  drift.
- tests: add a reverse-insertion-order feature test that would fail if
  the new sort step were ever dropped.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Copilot reviewed 23 out of 26 changed files in this pull request and generated 3 comments.

Comment thread package.json
Comment thread tests/fixtures/single-public/Cargo.toml Outdated
Comment thread lib.js
cargo accepts `publish = true` (emits as `publish: null`, same as
omitting) but it isn't in the documented schema — the docs only cover
`publish = false` and `publish = ["registry"]`. Dropping it keeps the
fixture idiomatic; the fixture name conveys the intent.

Verified: smoke test still emits `publish: ["single-public"]` after
removal.

Assisted-By: Claude <noreply@anthropic.com>
Signed-off-by: Sergey Vilgelm <sergey@vilgelm.com>
@SVilgelm SVilgelm merged commit f3e4c81 into main May 7, 2026
11 checks passed
@SVilgelm SVilgelm deleted the chore/post-merge-cleanup branch May 7, 2026 06:16
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.

2 participants