Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
107 changes: 106 additions & 1 deletion .github/workflows/checks.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,30 @@ jobs:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v5.5.0
- run: npm install
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run check
- run: npm test

Dist:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v5.5.0
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run build
- name: Verify dist/ is up to date
run: |
if ! git diff --exit-code dist/; then
echo "::error::dist/ is out of date — run 'npm run build' and commit the result"
exit 1
fi

Test:
runs-on: ubuntu-latest
Expand Down Expand Up @@ -62,6 +84,89 @@ jobs:
uses: ./
continue-on-error: true

Fixtures:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v5.5.0

- id: single-public
uses: ./
with:
manifest-path: tests/fixtures/single-public/Cargo.toml
- name: Verify single-public outputs
env:
PACKAGES: ${{ steps.single-public.outputs.packages }}
PUBLISH: ${{ steps.single-public.outputs.publish }}
MATRIX: ${{ steps.single-public.outputs.matrix }}
RUST_VERSION: ${{ steps.single-public.outputs.rust-version }}
EDITION: ${{ steps.single-public.outputs.edition }}
run: |
[ "$PACKAGES" = '["single-public"]' ] || { echo "packages: $PACKAGES"; exit 1; }
[ "$PUBLISH" = '["single-public"]' ] || { echo "publish: $PUBLISH"; exit 1; }
[ "$MATRIX" = '["--package=single-public"]' ] || { echo "matrix: $MATRIX"; exit 1; }
[ "$RUST_VERSION" = '1.85' ] || { echo "rust-version: $RUST_VERSION"; exit 1; }
[ "$EDITION" = '2021' ] || { echo "edition: $EDITION"; exit 1; }

- id: single-private
uses: ./
with:
manifest-path: tests/fixtures/single-private/Cargo.toml
- name: Verify single-private outputs
env:
PACKAGES: ${{ steps.single-private.outputs.packages }}
PUBLISH: ${{ steps.single-private.outputs.publish }}
MATRIX: ${{ steps.single-private.outputs.matrix }}
RUST_VERSION: ${{ steps.single-private.outputs.rust-version }}
EDITION: ${{ steps.single-private.outputs.edition }}
run: |
[ "$PACKAGES" = '["single-private"]' ] || { echo "packages: $PACKAGES"; exit 1; }
[ "$PUBLISH" = '[]' ] || { echo "publish: $PUBLISH"; exit 1; }
[ "$MATRIX" = '["--package=single-private"]' ] || { echo "matrix: $MATRIX"; exit 1; }
[ -z "$RUST_VERSION" ] || { echo "rust-version expected empty, got: $RUST_VERSION"; exit 1; }
[ "$EDITION" = '2021' ] || { echo "edition: $EDITION"; exit 1; }

- id: workspace-mixed
uses: ./
with:
manifest-path: tests/fixtures/workspace-mixed/Cargo.toml
- name: Verify workspace-mixed outputs
env:
PACKAGES: ${{ steps.workspace-mixed.outputs.packages }}
PUBLISH: ${{ steps.workspace-mixed.outputs.publish }}
MATRIX: ${{ steps.workspace-mixed.outputs.matrix }}
RUST_VERSION: ${{ steps.workspace-mixed.outputs.rust-version }}
EDITION: ${{ steps.workspace-mixed.outputs.edition }}
# Compare as sets (sorted) — cargo metadata's package order follows
# workspace member declaration order, but feature order is alphabetical.
# Sorting both sides keeps the assertion stable across cargo versions.
run: |
jq -ne --argjson got "$PACKAGES" '$got | sort == ["app","feature-lib","private-lib"]' \
|| { echo "packages: $PACKAGES"; exit 1; }
jq -ne --argjson got "$PUBLISH" '$got | sort == ["app","feature-lib"]' \
|| { echo "publish: $PUBLISH"; exit 1; }
jq -ne --argjson got "$MATRIX" '$got | sort == ["--package=app","--package=feature-lib --features=bar","--package=feature-lib --features=default","--package=feature-lib --features=foo","--package=private-lib"]' \
|| { echo "matrix: $MATRIX"; exit 1; }
[ "$RUST_VERSION" = '1.90' ] || { echo "rust-version: $RUST_VERSION"; exit 1; }
[ "$EDITION" = '2024' ] || { echo "edition: $EDITION"; exit 1; }

- id: toolchain-pinned
uses: ./
with:
manifest-path: tests/fixtures/toolchain-pinned/Cargo.toml
- name: Verify toolchain-pinned outputs
env:
PACKAGES: ${{ steps.toolchain-pinned.outputs.packages }}
RUST_VERSION: ${{ steps.toolchain-pinned.outputs.rust-version }}
EDITION: ${{ steps.toolchain-pinned.outputs.edition }}
run: |
[ "$PACKAGES" = '["toolchain-pinned"]' ] || { echo "packages: $PACKAGES"; exit 1; }
[ -z "$RUST_VERSION" ] || { echo "rust-version expected empty, got: $RUST_VERSION"; exit 1; }
[ "$EDITION" = '2021' ] || { echo "edition: $EDITION"; exit 1; }
- name: Verify pinned toolchain was actually installed
# Confirms ensureToolchain ran end-to-end. If a future regression
# silently skipped the install, this grep would fail.
run: rustup toolchain list | grep -E '^1\.85\.0'

Roas:
runs-on: ubuntu-latest
needs: Test
Expand Down
38 changes: 25 additions & 13 deletions .github/workflows/update-dist.yml
Original file line number Diff line number Diff line change
Expand Up @@ -15,21 +15,33 @@ jobs:
runs-on: ubuntu-latest
permissions:
contents: write
pull-requests: write
steps:
- uses: actions/checkout@1af3b93b6815bc44a9784bd300feb67ff0d1eeb3 # v5.5.0
- run: npm install
- run: npm run all
- name: Checking Git
- uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0
with:
node-version-file: .nvmrc
cache: npm
- run: npm ci
- run: npm run build
- name: Open PR if dist/ changed
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
DELTA="$(git status --short)"
if [ -n "${DELTA}" ]
then
git config --global user.name 'SV Tools Bot'
git config --global user.email 'sv.go.tools@gmail.com'
git checkout -b update-dist
git add .
git commit -m "Update Dist"
git push origin update-dist
# Stage only dist/, ignore any incidental drift elsewhere.
if [ -z "$(git status --short dist/)" ]; then
echo "dist/ already up to date"
exit 0
fi
git config --global user.name 'SV Tools Bot'
git config --global user.email 'sv.go.tools@gmail.com'
BRANCH="update-dist-${{ github.run_id }}"
git checkout -b "$BRANCH"
git add dist/
git commit -m "Update Dist"
git push origin "$BRANCH"
gh pr create \
--title "Update Dist" \
--body "Automated rebuild of dist/ via the Update Dist workflow." \
--head "$BRANCH" \
--base "${{ github.ref_name }}"
2 changes: 1 addition & 1 deletion LICENSE
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
MIT License

Copyright (c) 2025 SV Tools
Copyright (c) 2025 Sergey Vilgelm

Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
Expand Down
12 changes: 12 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,9 @@ matrix).
- `publish`: JSON array (string) of packages that can be published.
- `matrix`: JSON array (string) of command-line fragments suitable for use as a job matrix, e.g.
`["--package=foo","--package=bar --features=foo"]`
- `rust-version`: Workspace MSRV — the highest `rust-version` declared by any package, compared numerically (so `1.10`
beats `1.9`). Empty string if no package declares one. Useful as input to `actions-rust-lang/setup-rust-toolchain`.
- `edition`: The newest Rust edition used by any package (e.g. `2021`, `2024`). Empty string for an empty workspace.

## Examples

Expand Down Expand Up @@ -100,6 +103,15 @@ jobs:
- The action expects a Rust workspace or package with a `Cargo.toml`. If your manifest lives in a subdirectory, set
`manifest-path` accordingly.
- Outputs are emitted as JSON strings; use `fromJson` in workflows when you need native arrays or objects.
- If your project has a `rust-toolchain.toml` (or `rust-toolchain`) next to the manifest, the action installs the
pinned toolchain via `rustup toolchain install` before reading metadata. `rustup` is preinstalled on GitHub-hosted
runners; on self-hosted runners ensure it's on `PATH`.
- Matrix output behavior:
- A package with no features yields one row: `--package=<name>`.
- A package with features yields one row per feature: `--package=<name> --features=<feature>` — there is no
additional bare `--package=<name>` row in this case. The intent is to drive `cargo {test,build}` per feature
rather than to also test "no features".
- `publish = false` packages still appear in `packages` but are excluded from `publish`.

## License

Expand Down
16 changes: 10 additions & 6 deletions action.yml
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
name: "Rust Metadata Action"
description: "GitHub action to retrieve metadata, including packages, features, and other relevant information of the Rust project"
description: "Expose `cargo metadata` as workflow outputs: package list, publishable crates, and a per-package / per-feature job matrix."
branding:
icon: "code"
color: "red"
inputs:
manifest-path:
description: "Path to Cargo.toml manifest"
description: "Path to the project's `Cargo.toml`. The action runs from this file's directory, so any adjacent `rust-toolchain.toml` is honored — its pinned toolchain is installed via `rustup` before metadata is read."
required: false
default: "Cargo.toml"
outputs:
metadata:
description: "Raw Metadata"
description: "Full `cargo metadata --no-deps` output as a JSON-encoded string. Use `fromJson` in workflow expressions to parse it."
packages:
description: "List of packages: [foo, bar, baz]"
description: 'JSON-encoded array of every package name in the workspace. Example: `["foo","bar"]`.'
publish:
description: "List of packages that can be published: [foo, bar]"
description: 'JSON-encoded array of package names that are publishable. Packages declared `publish = false` are excluded; packages restricted to specific registries are included. Example: `["foo","bar"]`.'
matrix:
description: "List of packages and their features: [--package=foo, --package=bar --features=foo, --package=bar --features=bar]"
description: 'JSON-encoded array of cargo argument strings — one entry per package, or one per feature for packages that declare features. Intended for `strategy.matrix`. Example: `["--package=foo","--package=bar --features=full"]`.'
rust-version:
description: "Workspace MSRV: the highest `rust-version` declared by any package, compared numerically (so `1.10` > `1.9`). Empty string if no package declares one. Useful for `actions-rust-lang/setup-rust-toolchain`."
edition:
description: "The newest Rust edition used by any package in the workspace (e.g. `2021`, `2024`). Empty string for an empty workspace."
runs:
using: node24
main: "dist/index.js"
Loading
Loading