diff --git a/.github/workflows/checks.yml b/.github/workflows/checks.yml index ba6a6eb..40c2719 100644 --- a/.github/workflows/checks.yml +++ b/.github/workflows/checks.yml @@ -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 @@ -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 diff --git a/.github/workflows/update-dist.yml b/.github/workflows/update-dist.yml index 8112199..b7e5272 100644 --- a/.github/workflows/update-dist.yml +++ b/.github/workflows/update-dist.yml @@ -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 }}" diff --git a/LICENSE b/LICENSE index 7aada08..3b0f6e7 100644 --- a/LICENSE +++ b/LICENSE @@ -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 diff --git a/README.md b/README.md index 3f7e097..1f1f83e 100644 --- a/README.md +++ b/README.md @@ -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 @@ -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=`. + - A package with features yields one row per feature: `--package= --features=` — there is no + additional bare `--package=` 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 diff --git a/action.yml b/action.yml index e109b90..ed41133 100644 --- a/action.yml +++ b/action.yml @@ -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" diff --git a/dist/index.js b/dist/index.js index d0289dd..33a1f2d 100644 --- a/dist/index.js +++ b/dist/index.js @@ -1,136 +1,5 @@ -/******/ (() => { // webpackBootstrap -/******/ var __webpack_modules__ = ({ - -/***/ 6136: -/***/ ((module, __webpack_exports__, __nccwpck_require__) => { - -"use strict"; -__nccwpck_require__.a(module, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try { -__nccwpck_require__.r(__webpack_exports__); -/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(2698); -/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(5317); -/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_1___default = /*#__PURE__*/__nccwpck_require__.n(child_process__WEBPACK_IMPORTED_MODULE_1__); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(6928); -/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2___default = /*#__PURE__*/__nccwpck_require__.n(path__WEBPACK_IMPORTED_MODULE_2__); - - - - -function ensureToolchain(manifestPath) { - // rustup 1.28+ no longer auto-installs the toolchain pinned in - // rust-toolchain.toml when the cargo/rustc proxy is invoked. Running - // `rustup toolchain install` with no toolchain argument installs the - // *active* toolchain (i.e. whatever rust-toolchain.toml in the manifest's - // directory selects), without compiling anything. - const cwd = (0,path__WEBPACK_IMPORTED_MODULE_2__.dirname)((0,path__WEBPACK_IMPORTED_MODULE_2__.resolve)(manifestPath)); - const result = (0,child_process__WEBPACK_IMPORTED_MODULE_1__.spawnSync)( - "rustup", - ["toolchain", "install", "--no-self-update"], - { cwd, encoding: "utf8" }, - ); - if (result.error && result.error.code === "ENOENT") { - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq)("rustup not found on PATH; skipping toolchain install"); - return; - } - if (result.status !== 0) { - const stderr = (result.stderr || "").trim(); - throw new Error( - `rustup failed to install the active toolchain` + - (stderr ? `: ${stderr}` : ""), - ); - } -} - -function runCargoMetadata(manifestPath) { - return new Promise((resolve, reject) => { - const cmd = (0,child_process__WEBPACK_IMPORTED_MODULE_1__.spawn)("cargo", [ - "metadata", - "--manifest-path=" + manifestPath, - "--no-deps", - "--format-version", - "1", - ]); - - const stdoutChunks = []; - const stderrChunks = []; - cmd.stdout.on("data", (chunk) => stdoutChunks.push(chunk)); - cmd.stderr.on("data", (chunk) => stderrChunks.push(chunk)); - - cmd.on("error", (error) => { - reject(new Error(`cargo metadata failed to spawn: ${error.message}`)); - }); - - cmd.on("close", (code) => { - const stderr = Buffer.concat(stderrChunks).toString().trim(); - if (code !== 0) { - reject( - new Error( - `cargo metadata failed (exit code ${code})` + - (stderr ? `: ${stderr}` : ""), - ), - ); - return; - } - if (stderr) { - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq)(stderr); - } - const stdout = Buffer.concat(stdoutChunks).toString(); - try { - resolve(JSON.parse(stdout)); - } catch (err) { - reject( - new Error(`failed to parse cargo metadata output: ${err.message}`), - ); - } - }); - }); -} - -async function run() { - const manifestPath = (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .getInput */ .V4)("manifest-path", { required: true }); - ensureToolchain(manifestPath); - const metadata = await runCargoMetadata(manifestPath); - setActionOutput(metadata); -} - -function setActionOutput(metadata) { - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("metadata", JSON.stringify(metadata)); - if (metadata.hasOwnProperty("packages")) { - let allPackages = []; - let packagesToPublish = []; - let matrix = []; - metadata.packages.forEach((pkg) => { - allPackages.push(pkg.name); - if (pkg.hasOwnProperty("publish") && pkg.publish !== false) { - packagesToPublish.push(pkg.name); - } - if (pkg.hasOwnProperty("features")) { - const names = Object.getOwnPropertyNames(pkg.features); - if (names.length === 0) { - matrix.push(`--package=${pkg.name}`); - } else { - names.forEach((feature) => { - matrix.push(`--package=${pkg.name} --features=${feature}`); - }); - } - } - }); - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("packages", JSON.stringify(allPackages)); - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("publish", JSON.stringify(packagesToPublish)); - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("matrix", JSON.stringify(matrix)); - } -} - -try { - await run(); -} catch (error) { - (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setFailed */ .C1)(error.message); -} - -__webpack_async_result__(); -} catch(e) { __webpack_async_result__(e); } }, 1); - -/***/ }), +import { createRequire as __WEBPACK_EXTERNAL_createRequire } from "module"; +/******/ var __webpack_modules__ = ({ /***/ 770: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { @@ -143,7 +12,6 @@ __webpack_async_result__(); /***/ 218: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -"use strict"; var __webpack_unused_export__; @@ -416,7 +284,6 @@ __webpack_unused_export__ = debug; // for test /***/ 6752: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; var __webpack_unused_export__; @@ -658,7 +525,6 @@ module.exports = { /***/ 2279: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -774,7 +640,6 @@ module.exports = connect /***/ 6862: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -1033,7 +898,6 @@ module.exports = pipeline /***/ 4043: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -1255,7 +1119,6 @@ module.exports.RequestHandler = RequestHandler /***/ 3560: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -1483,7 +1346,6 @@ module.exports = stream /***/ 1882: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { InvalidArgumentError, SocketError } = __nccwpck_require__(8707) @@ -1599,7 +1461,6 @@ module.exports = upgrade /***/ 6615: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; module.exports.request = __nccwpck_require__(4043) @@ -1614,7 +1475,6 @@ module.exports.connect = __nccwpck_require__(2279) /***/ 9927: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; // Ported from https://github.com/nodejs/undici/pull/907 @@ -2107,7 +1967,6 @@ module.exports = { /***/ 9136: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const net = __nccwpck_require__(7030) @@ -2355,7 +2214,6 @@ module.exports = buildConnector /***/ 735: /***/ ((module) => { -"use strict"; /** @type {Record} */ @@ -2481,7 +2339,6 @@ module.exports = { /***/ 2414: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const diagnosticsChannel = __nccwpck_require__(3053) const util = __nccwpck_require__(7975) @@ -2691,7 +2548,6 @@ module.exports = { /***/ 8707: /***/ ((module) => { -"use strict"; const kUndiciError = Symbol.for('undici.error.UND_ERR') @@ -3124,7 +2980,6 @@ module.exports = { /***/ 4655: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -3611,7 +3466,6 @@ module.exports = { /***/ 7752: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -3771,7 +3625,6 @@ module.exports = { /***/ 3440: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -4498,7 +4351,6 @@ module.exports = { /***/ 7405: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { InvalidArgumentError } = __nccwpck_require__(8707) @@ -4636,7 +4488,6 @@ module.exports = Agent /***/ 837: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -4853,7 +4704,6 @@ module.exports = BalancedPool /***/ 637: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; /* global WebAssembly */ @@ -6231,7 +6081,6 @@ module.exports = connectH1 /***/ 8788: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -6983,7 +6832,6 @@ module.exports = connectH2 /***/ 3701: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; // @ts-check @@ -7614,7 +7462,6 @@ module.exports = Client /***/ 1841: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const Dispatcher = __nccwpck_require__(883) @@ -7820,7 +7667,6 @@ module.exports = DispatcherBase /***/ 883: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const EventEmitter = __nccwpck_require__(8474) @@ -7893,7 +7739,6 @@ module.exports = Dispatcher /***/ 3137: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const DispatcherBase = __nccwpck_require__(1841) @@ -8061,7 +7906,6 @@ module.exports = EnvHttpProxyAgent /***/ 4660: /***/ ((module) => { -"use strict"; /* eslint-disable */ @@ -8186,7 +8030,6 @@ module.exports = class FixedQueue { /***/ 2128: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const DispatcherBase = __nccwpck_require__(1841) @@ -8429,7 +8272,6 @@ module.exports = PoolStats /***/ 628: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -8544,7 +8386,6 @@ module.exports = Pool /***/ 6672: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { kProxy, kClose, kDestroy, kDispatch, kInterceptors } = __nccwpck_require__(6443) @@ -8826,7 +8667,6 @@ module.exports = ProxyAgent /***/ 50: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const Dispatcher = __nccwpck_require__(883) @@ -8869,7 +8709,6 @@ module.exports = RetryAgent /***/ 2581: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; // We include a version number for the Dispatcher API. In case of breaking changes, @@ -8909,7 +8748,6 @@ module.exports = { /***/ 8155: /***/ ((module) => { -"use strict"; module.exports = class DecoratorHandler { @@ -8961,7 +8799,6 @@ module.exports = class DecoratorHandler { /***/ 8754: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const util = __nccwpck_require__(3440) @@ -9201,7 +9038,6 @@ module.exports = RedirectHandler /***/ 7816: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -9583,7 +9419,6 @@ module.exports = RetryHandler /***/ 379: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { isIP } = __nccwpck_require__(7030) const { lookup } = __nccwpck_require__(610) @@ -9966,7 +9801,6 @@ module.exports = interceptorOpts => { /***/ 8060: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const util = __nccwpck_require__(3440) @@ -10097,7 +9931,6 @@ module.exports = createDumpInterceptor /***/ 5092: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const RedirectHandler = __nccwpck_require__(8754) @@ -10126,7 +9959,6 @@ module.exports = createRedirectInterceptor /***/ 1514: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const RedirectHandler = __nccwpck_require__(8754) @@ -10158,7 +9990,6 @@ module.exports = opts => { /***/ 2026: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const RetryHandler = __nccwpck_require__(7816) @@ -10185,7 +10016,6 @@ module.exports = globalOpts => { /***/ 2824: /***/ ((__unused_webpack_module, exports, __nccwpck_require__) => { -"use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.SPECIAL_HEADERS = exports.HEADER_STATE = exports.MINOR = exports.MAJOR = exports.CONNECTION_TOKEN_CHARS = exports.HEADER_CHARS = exports.TOKEN = exports.STRICT_TOKEN = exports.HEX = exports.URL_CHAR = exports.STRICT_URL_CHAR = exports.USERINFO_CHARS = exports.MARK = exports.ALPHANUM = exports.NUM = exports.HEX_MAP = exports.NUM_MAP = exports.ALPHA = exports.FINISH = exports.H_METHOD_MAP = exports.METHOD_MAP = exports.METHODS_RTSP = exports.METHODS_ICE = exports.METHODS_HTTP = exports.METHODS = exports.LENIENT_FLAGS = exports.FLAGS = exports.TYPE = exports.ERROR = void 0; @@ -10470,7 +10300,6 @@ exports.SPECIAL_HEADERS = { /***/ 3870: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Buffer } = __nccwpck_require__(4573) @@ -10483,7 +10312,6 @@ module.exports = Buffer.from('AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f3 /***/ 3434: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Buffer } = __nccwpck_require__(4573) @@ -10496,7 +10324,6 @@ module.exports = Buffer.from('AGFzbQEAAAABJwdgAX8Bf2ADf39/AX9gAX8AYAJ/fwBgBH9/f3 /***/ 172: /***/ ((__unused_webpack_module, exports) => { -"use strict"; Object.defineProperty(exports, "__esModule", ({ value: true })); exports.enumToMap = void 0; @@ -10518,7 +10345,6 @@ exports.enumToMap = enumToMap; /***/ 7501: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { kClients } = __nccwpck_require__(6443) @@ -10686,7 +10512,6 @@ module.exports = MockAgent /***/ 7365: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { promisify } = __nccwpck_require__(7975) @@ -10753,7 +10578,6 @@ module.exports = MockClient /***/ 2429: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { UndiciError } = __nccwpck_require__(8707) @@ -10789,7 +10613,6 @@ module.exports = { /***/ 1511: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { getResponseData, buildKey, addMockDispatch } = __nccwpck_require__(3397) @@ -11004,7 +10827,6 @@ module.exports.MockScope = MockScope /***/ 4004: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { promisify } = __nccwpck_require__(7975) @@ -11071,7 +10893,6 @@ module.exports = MockPool /***/ 1117: /***/ ((module) => { -"use strict"; module.exports = { @@ -11102,7 +10923,6 @@ module.exports = { /***/ 3397: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { MockNotMatchedError } = __nccwpck_require__(2429) @@ -11477,7 +11297,6 @@ module.exports = { /***/ 6142: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Transform } = __nccwpck_require__(7075) @@ -11528,7 +11347,6 @@ module.exports = class PendingInterceptorsFormatter { /***/ 1529: /***/ ((module) => { -"use strict"; const singulars = { @@ -11565,7 +11383,6 @@ module.exports = class Pluralizer { /***/ 6603: /***/ ((module) => { -"use strict"; /** @@ -11996,7 +11813,6 @@ module.exports = { /***/ 9634: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { kConstruct } = __nccwpck_require__(109) @@ -12863,7 +12679,6 @@ module.exports = { /***/ 3245: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { kConstruct } = __nccwpck_require__(109) @@ -13023,7 +12838,6 @@ module.exports = { /***/ 109: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; module.exports = { @@ -13036,7 +12850,6 @@ module.exports = { /***/ 6798: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -13089,7 +12902,6 @@ module.exports = { /***/ 1276: /***/ ((module) => { -"use strict"; // https://wicg.github.io/cookie-store/#cookie-maximum-attribute-value-size @@ -13109,7 +12921,6 @@ module.exports = { /***/ 9061: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { parseSetCookie } = __nccwpck_require__(1978) @@ -13301,7 +13112,6 @@ module.exports = { /***/ 1978: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { maxNameValuePairSize, maxAttributeValueSize } = __nccwpck_require__(1276) @@ -13626,7 +13436,6 @@ module.exports = { /***/ 7797: /***/ ((module) => { -"use strict"; /** @@ -13916,7 +13725,6 @@ module.exports = { /***/ 4031: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Transform } = __nccwpck_require__(7075) const { isASCIINumber, isValidLastEventId } = __nccwpck_require__(4811) @@ -14322,7 +14130,6 @@ module.exports = { /***/ 1238: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { pipeline } = __nccwpck_require__(7075) @@ -14810,7 +14617,6 @@ module.exports = { /***/ 4811: /***/ ((module) => { -"use strict"; /** @@ -14855,7 +14661,6 @@ module.exports = { /***/ 4492: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const util = __nccwpck_require__(3440) @@ -15392,7 +15197,6 @@ module.exports = { /***/ 4495: /***/ ((module) => { -"use strict"; const corsSafeListedMethods = /** @type {const} */ (['GET', 'HEAD', 'POST']) @@ -15524,7 +15328,6 @@ module.exports = { /***/ 1900: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const assert = __nccwpck_require__(4589) @@ -16276,7 +16079,6 @@ module.exports = { /***/ 6653: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { kConnected, kSize } = __nccwpck_require__(6443) @@ -16330,7 +16132,6 @@ module.exports = function () { /***/ 7114: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Blob, File } = __nccwpck_require__(4573) @@ -16464,7 +16265,6 @@ module.exports = { FileLike, isFileLike } /***/ 116: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { isUSVString, bufferToLowerCasedHeaderName } = __nccwpck_require__(3440) @@ -16946,7 +16746,6 @@ module.exports = { /***/ 5910: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { isBlobLike, iteratorMixin } = __nccwpck_require__(3168) @@ -17206,7 +17005,6 @@ module.exports = { FormData, makeEntry } /***/ 1059: /***/ ((module) => { -"use strict"; // In case of breaking changes, increase the version @@ -17254,7 +17052,6 @@ module.exports = { /***/ 660: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; // https://github.com/Ethan-Arrowood/undici-fetch @@ -17949,7 +17746,6 @@ module.exports = { /***/ 4398: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; // https://github.com/Ethan-Arrowood/undici-fetch @@ -20229,7 +20025,6 @@ module.exports = { /***/ 9967: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; /* globals AbortController */ @@ -21274,7 +21069,6 @@ module.exports = { Request, makeRequest, fromInnerRequest, cloneRequest } /***/ 9051: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Headers, HeadersList, fill, getHeadersGuard, setHeadersGuard, setHeadersList } = __nccwpck_require__(660) @@ -21892,7 +21686,6 @@ module.exports = { /***/ 3627: /***/ ((module) => { -"use strict"; module.exports = { @@ -21909,7 +21702,6 @@ module.exports = { /***/ 3168: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Transform } = __nccwpck_require__(7075) @@ -23549,7 +23341,6 @@ module.exports = { /***/ 5893: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { types, inspect } = __nccwpck_require__(7975) @@ -24252,7 +24043,6 @@ module.exports = { /***/ 2607: /***/ ((module) => { -"use strict"; /** @@ -24550,7 +24340,6 @@ module.exports = { /***/ 8355: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -24902,7 +24691,6 @@ module.exports = { /***/ 8573: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { webidl } = __nccwpck_require__(5893) @@ -24988,7 +24776,6 @@ module.exports = { /***/ 961: /***/ ((module) => { -"use strict"; module.exports = { @@ -25006,7 +24793,6 @@ module.exports = { /***/ 3610: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { @@ -25405,7 +25191,6 @@ module.exports = { /***/ 6897: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { uid, states, sentCloseFrameState, emptyBuffer, opcodes } = __nccwpck_require__(736) @@ -25784,7 +25569,6 @@ module.exports = { /***/ 736: /***/ ((module) => { -"use strict"; // This is a Globally Unique Identifier unique used @@ -25858,7 +25642,6 @@ module.exports = { /***/ 5188: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { webidl } = __nccwpck_require__(5893) @@ -26195,7 +25978,6 @@ module.exports = { /***/ 3264: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { maxUnsigned16Bit } = __nccwpck_require__(736) @@ -26299,7 +26081,6 @@ module.exports = { /***/ 9469: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { createInflateRaw, Z_DEFAULT_WINDOWBITS } = __nccwpck_require__(8522) @@ -26407,7 +26188,6 @@ module.exports = { PerMessageDeflate } /***/ 1652: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { Writable } = __nccwpck_require__(7075) @@ -26905,7 +26685,6 @@ module.exports = { /***/ 3900: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { WebsocketFrameSend } = __nccwpck_require__(3264) @@ -27017,7 +26796,6 @@ module.exports = { SendQueue } /***/ 1216: /***/ ((module) => { -"use strict"; module.exports = { @@ -27037,7 +26815,6 @@ module.exports = { /***/ 8625: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { kReadyState, kController, kResponse, kBinaryType, kWebSocketURL } = __nccwpck_require__(1216) @@ -27367,7 +27144,6 @@ module.exports = { /***/ 3726: /***/ ((module, __unused_webpack_exports, __nccwpck_require__) => { -"use strict"; const { webidl } = __nccwpck_require__(5893) @@ -27967,247 +27743,421 @@ module.exports = { /***/ 2613: /***/ ((module) => { -"use strict"; -module.exports = require("assert"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("assert"); /***/ }), /***/ 5317: /***/ ((module) => { -"use strict"; -module.exports = require("child_process"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("child_process"); /***/ }), /***/ 4434: /***/ ((module) => { -"use strict"; -module.exports = require("events"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("events"); /***/ }), /***/ 8611: /***/ ((module) => { -"use strict"; -module.exports = require("http"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("http"); /***/ }), /***/ 5692: /***/ ((module) => { -"use strict"; -module.exports = require("https"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("https"); /***/ }), /***/ 9278: /***/ ((module) => { -"use strict"; -module.exports = require("net"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("net"); /***/ }), /***/ 4589: /***/ ((module) => { -"use strict"; -module.exports = require("node:assert"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:assert"); /***/ }), /***/ 6698: /***/ ((module) => { -"use strict"; -module.exports = require("node:async_hooks"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:async_hooks"); /***/ }), /***/ 4573: /***/ ((module) => { -"use strict"; -module.exports = require("node:buffer"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:buffer"); /***/ }), /***/ 7540: /***/ ((module) => { -"use strict"; -module.exports = require("node:console"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:console"); /***/ }), /***/ 7598: /***/ ((module) => { -"use strict"; -module.exports = require("node:crypto"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:crypto"); /***/ }), /***/ 3053: /***/ ((module) => { -"use strict"; -module.exports = require("node:diagnostics_channel"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:diagnostics_channel"); /***/ }), /***/ 610: /***/ ((module) => { -"use strict"; -module.exports = require("node:dns"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:dns"); /***/ }), /***/ 8474: /***/ ((module) => { -"use strict"; -module.exports = require("node:events"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:events"); /***/ }), /***/ 7067: /***/ ((module) => { -"use strict"; -module.exports = require("node:http"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http"); /***/ }), /***/ 2467: /***/ ((module) => { -"use strict"; -module.exports = require("node:http2"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:http2"); /***/ }), /***/ 7030: /***/ ((module) => { -"use strict"; -module.exports = require("node:net"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:net"); /***/ }), /***/ 643: /***/ ((module) => { -"use strict"; -module.exports = require("node:perf_hooks"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:perf_hooks"); /***/ }), /***/ 1792: /***/ ((module) => { -"use strict"; -module.exports = require("node:querystring"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:querystring"); /***/ }), /***/ 7075: /***/ ((module) => { -"use strict"; -module.exports = require("node:stream"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:stream"); /***/ }), /***/ 1692: /***/ ((module) => { -"use strict"; -module.exports = require("node:tls"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:tls"); /***/ }), /***/ 3136: /***/ ((module) => { -"use strict"; -module.exports = require("node:url"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:url"); /***/ }), /***/ 7975: /***/ ((module) => { -"use strict"; -module.exports = require("node:util"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:util"); /***/ }), /***/ 3429: /***/ ((module) => { -"use strict"; -module.exports = require("node:util/types"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:util/types"); /***/ }), /***/ 5919: /***/ ((module) => { -"use strict"; -module.exports = require("node:worker_threads"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:worker_threads"); /***/ }), /***/ 8522: /***/ ((module) => { -"use strict"; -module.exports = require("node:zlib"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("node:zlib"); /***/ }), /***/ 6928: /***/ ((module) => { -"use strict"; -module.exports = require("path"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("path"); /***/ }), /***/ 3193: /***/ ((module) => { -"use strict"; -module.exports = require("string_decoder"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("string_decoder"); /***/ }), /***/ 4756: /***/ ((module) => { -"use strict"; -module.exports = require("tls"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("tls"); /***/ }), /***/ 9023: /***/ ((module) => { -"use strict"; -module.exports = require("util"); +module.exports = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("util"); + +/***/ }), + +/***/ 5483: +/***/ ((__webpack_module__, __unused_webpack___webpack_exports__, __nccwpck_require__) => { + +__nccwpck_require__.a(__webpack_module__, async (__webpack_handle_async_dependencies__, __webpack_async_result__) => { try { +/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(2698); +/* harmony import */ var _lib_js__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(6932); + + + +try { + await (0,_lib_js__WEBPACK_IMPORTED_MODULE_1__/* .run */ .eF)(); +} catch (error) { + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setFailed */ .C1)(error.message); +} + +__webpack_async_result__(); +} catch(e) { __webpack_async_result__(e); } }, 1); + +/***/ }), + +/***/ 6932: +/***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { + +/* harmony export */ __nccwpck_require__.d(__webpack_exports__, { +/* harmony export */ eF: () => (/* binding */ run) +/* harmony export */ }); +/* unused harmony exports ensureToolchain, runCargoMetadata, parseMetadata, writeOutputs */ +/* harmony import */ var _actions_core__WEBPACK_IMPORTED_MODULE_0__ = __nccwpck_require__(2698); +/* harmony import */ var child_process__WEBPACK_IMPORTED_MODULE_1__ = __nccwpck_require__(5317); +/* harmony import */ var path__WEBPACK_IMPORTED_MODULE_2__ = __nccwpck_require__(6928); + + + + +function ensureToolchain(manifestPath) { + // rustup 1.28+ no longer auto-installs through the cargo/rustc proxy. + // `rustup toolchain install` with no toolchain arg installs the active + // toolchain (whatever rust-toolchain.toml selects), without compiling. + const cwd = (0,path__WEBPACK_IMPORTED_MODULE_2__.dirname)((0,path__WEBPACK_IMPORTED_MODULE_2__.resolve)(manifestPath)); + const result = (0,child_process__WEBPACK_IMPORTED_MODULE_1__.spawnSync)( + "rustup", + ["toolchain", "install", "--no-self-update"], + { cwd, encoding: "utf8" }, + ); + if (result.error && result.error.code === "ENOENT") { + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq)("rustup not found on PATH; skipping toolchain install"); + return; + } + if (result.status !== 0) { + const stderr = (result.stderr || "").trim(); + throw new Error( + `rustup failed to install the active toolchain` + + (stderr ? `: ${stderr}` : ""), + ); + } +} + +function runCargoMetadata(manifestPath) { + // Spawn cargo from the manifest's directory so rustup picks up any + // adjacent rust-toolchain.toml (rustup walks up, not down). Pass the + // absolute manifest path so a relative input doesn't get re-resolved + // against the new cwd. + const absManifestPath = (0,path__WEBPACK_IMPORTED_MODULE_2__.resolve)(manifestPath); + const cwd = (0,path__WEBPACK_IMPORTED_MODULE_2__.dirname)(absManifestPath); + return new Promise((resolve, reject) => { + const cmd = (0,child_process__WEBPACK_IMPORTED_MODULE_1__.spawn)( + "cargo", + [ + "metadata", + "--manifest-path=" + absManifestPath, + "--no-deps", + "--format-version", + "1", + ], + { cwd }, + ); + + const stdoutChunks = []; + const stderrChunks = []; + cmd.stdout.on("data", (chunk) => stdoutChunks.push(chunk)); + cmd.stderr.on("data", (chunk) => stderrChunks.push(chunk)); + + cmd.on("error", (error) => { + reject(new Error(`cargo metadata failed to spawn: ${error.message}`)); + }); + + cmd.on("close", (code) => { + const stderr = Buffer.concat(stderrChunks).toString().trim(); + if (code !== 0) { + reject( + new Error( + `cargo metadata failed (exit code ${code})` + + (stderr ? `: ${stderr}` : ""), + ), + ); + return; + } + if (stderr) { + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .info */ .pq)(stderr); + } + const stdout = Buffer.concat(stdoutChunks).toString(); + try { + resolve(JSON.parse(stdout)); + } catch (err) { + reject( + new Error(`failed to parse cargo metadata output: ${err.message}`), + ); + } + }); + }); +} + +// Cargo's `publish` field semantics: +// null → unrestricted (publishable) +// [] → `publish = false` in Cargo.toml (NOT publishable) +// ["registry"] → restricted to specific registries (still publishable) +function isPublishable(pkg) { + return ( + pkg.publish === null || + (Array.isArray(pkg.publish) && pkg.publish.length > 0) + ); +} + +// Compare two `major.minor[.patch]` strings numerically (so "1.10" > "1.9", +// not the lexicographic opposite). Missing components default to 0. +function compareVersion(a, b) { + const parse = (v) => v.split(".").map((p) => parseInt(p, 10) || 0); + const av = parse(a); + const bv = parse(b); + for (let i = 0; i < 3; i++) { + const diff = (av[i] ?? 0) - (bv[i] ?? 0); + if (diff !== 0) return diff; + } + return 0; +} + +function parseMetadata(metadata) { + const packages = []; + const publish = []; + const matrix = []; + let rustVersion = null; + let edition = null; + for (const pkg of metadata.packages ?? []) { + packages.push(pkg.name); + if (isPublishable(pkg)) { + publish.push(pkg.name); + } + // Sort so matrix output is stable regardless of cargo's feature + // emission order (currently a BTreeMap, but not contractually so). + const features = Object.keys(pkg.features ?? {}).sort(); + if (features.length === 0) { + matrix.push(`--package=${pkg.name}`); + } else { + for (const feature of features) { + matrix.push(`--package=${pkg.name} --features=${feature}`); + } + } + if (pkg.rust_version != null) { + if ( + rustVersion === null || + compareVersion(pkg.rust_version, rustVersion) > 0 + ) { + rustVersion = pkg.rust_version; + } + } + if (pkg.edition != null) { + // Editions are years ("2015", "2018", "2021", "2024") — simple int compare. + if ( + edition === null || + parseInt(pkg.edition, 10) > parseInt(edition, 10) + ) { + edition = pkg.edition; + } + } + } + return { + packages, + publish, + matrix, + rustVersion: rustVersion ?? "", + edition: edition ?? "", + }; +} + +function writeOutputs(metadata) { + const { packages, publish, matrix, rustVersion, edition } = + parseMetadata(metadata); + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("metadata", JSON.stringify(metadata)); + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("packages", JSON.stringify(packages)); + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("publish", JSON.stringify(publish)); + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("matrix", JSON.stringify(matrix)); + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("rust-version", rustVersion); + (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .setOutput */ .uH)("edition", edition); +} + +async function run() { + const manifestPath = (0,_actions_core__WEBPACK_IMPORTED_MODULE_0__/* .getInput */ .V4)("manifest-path"); + ensureToolchain(manifestPath); + const metadata = await runCargoMetadata(manifestPath); + writeOutputs(metadata); +} + /***/ }), /***/ 2698: /***/ ((__unused_webpack___webpack_module__, __webpack_exports__, __nccwpck_require__) => { -"use strict"; // EXPORTS __nccwpck_require__.d(__webpack_exports__, { @@ -28220,7 +28170,7 @@ __nccwpck_require__.d(__webpack_exports__, { // UNUSED EXPORTS: ExitCode, addPath, debug, endGroup, error, exportVariable, getBooleanInput, getIDToken, getMultilineInput, getState, group, isDebug, markdownSummary, notice, platform, saveState, setCommandEcho, setSecret, startGroup, summary, toPlatformPath, toPosixPath, toWin32Path, warning ;// CONCATENATED MODULE: external "os" -const external_os_namespaceObject = require("os"); +const external_os_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("os"); ;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/utils.js // We use any as a valid input type /* eslint-disable @typescript-eslint/no-explicit-any */ @@ -28350,9 +28300,9 @@ function escapeProperty(s) { } //# sourceMappingURL=command.js.map ;// CONCATENATED MODULE: external "crypto" -const external_crypto_namespaceObject = require("crypto"); +const external_crypto_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("crypto"); ;// CONCATENATED MODULE: external "fs" -const external_fs_namespaceObject = require("fs"); +const external_fs_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("fs"); ;// CONCATENATED MODULE: ./node_modules/@actions/core/lib/file-command.js // For internal use, subject to change. // We use any as a valid input type @@ -30112,7 +30062,7 @@ function io_copyFile(srcFile, destFile, force) { } //# sourceMappingURL=io.js.map ;// CONCATENATED MODULE: external "timers" -const external_timers_namespaceObject = require("timers"); +const external_timers_namespaceObject = __WEBPACK_EXTERNAL_createRequire(import.meta.url)("timers"); ;// CONCATENATED MODULE: ./node_modules/@actions/exec/lib/toolrunner.js var toolrunner_awaiter = (undefined && undefined.__awaiter) || function (thisArg, _arguments, P, generator) { function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } @@ -31167,159 +31117,134 @@ function getIDToken(aud) { /***/ }) -/******/ }); +/******/ }); /************************************************************************/ -/******/ // The module cache -/******/ var __webpack_module_cache__ = {}; -/******/ -/******/ // The require function -/******/ function __nccwpck_require__(moduleId) { -/******/ // Check if module is in cache -/******/ var cachedModule = __webpack_module_cache__[moduleId]; -/******/ if (cachedModule !== undefined) { -/******/ return cachedModule.exports; -/******/ } -/******/ // Create a new module (and put it into the cache) -/******/ var module = __webpack_module_cache__[moduleId] = { -/******/ // no module.id needed -/******/ // no module.loaded needed -/******/ exports: {} -/******/ }; -/******/ -/******/ // Execute the module function -/******/ var threw = true; -/******/ try { -/******/ __webpack_modules__[moduleId](module, module.exports, __nccwpck_require__); -/******/ threw = false; -/******/ } finally { -/******/ if(threw) delete __webpack_module_cache__[moduleId]; -/******/ } -/******/ -/******/ // Return the exports of the module -/******/ return module.exports; +/******/ // The module cache +/******/ var __webpack_module_cache__ = {}; +/******/ +/******/ // The require function +/******/ function __nccwpck_require__(moduleId) { +/******/ // Check if module is in cache +/******/ var cachedModule = __webpack_module_cache__[moduleId]; +/******/ if (cachedModule !== undefined) { +/******/ return cachedModule.exports; +/******/ } +/******/ // Create a new module (and put it into the cache) +/******/ var module = __webpack_module_cache__[moduleId] = { +/******/ // no module.id needed +/******/ // no module.loaded needed +/******/ exports: {} +/******/ }; +/******/ +/******/ // Execute the module function +/******/ var threw = true; +/******/ try { +/******/ __webpack_modules__[moduleId](module, module.exports, __nccwpck_require__); +/******/ threw = false; +/******/ } finally { +/******/ if(threw) delete __webpack_module_cache__[moduleId]; /******/ } -/******/ +/******/ +/******/ // Return the exports of the module +/******/ return module.exports; +/******/ } +/******/ /************************************************************************/ -/******/ /* webpack/runtime/async module */ -/******/ (() => { -/******/ var webpackQueues = typeof Symbol === "function" ? Symbol("webpack queues") : "__webpack_queues__"; -/******/ var webpackExports = typeof Symbol === "function" ? Symbol("webpack exports") : "__webpack_exports__"; -/******/ var webpackError = typeof Symbol === "function" ? Symbol("webpack error") : "__webpack_error__"; -/******/ var resolveQueue = (queue) => { -/******/ if(queue && queue.d < 1) { -/******/ queue.d = 1; -/******/ queue.forEach((fn) => (fn.r--)); -/******/ queue.forEach((fn) => (fn.r-- ? fn.r++ : fn())); -/******/ } +/******/ /* webpack/runtime/async module */ +/******/ (() => { +/******/ var webpackQueues = typeof Symbol === "function" ? Symbol("webpack queues") : "__webpack_queues__"; +/******/ var webpackExports = typeof Symbol === "function" ? Symbol("webpack exports") : "__webpack_exports__"; +/******/ var webpackError = typeof Symbol === "function" ? Symbol("webpack error") : "__webpack_error__"; +/******/ var resolveQueue = (queue) => { +/******/ if(queue && queue.d < 1) { +/******/ queue.d = 1; +/******/ queue.forEach((fn) => (fn.r--)); +/******/ queue.forEach((fn) => (fn.r-- ? fn.r++ : fn())); /******/ } -/******/ var wrapDeps = (deps) => (deps.map((dep) => { -/******/ if(dep !== null && typeof dep === "object") { -/******/ if(dep[webpackQueues]) return dep; -/******/ if(dep.then) { -/******/ var queue = []; -/******/ queue.d = 0; -/******/ dep.then((r) => { -/******/ obj[webpackExports] = r; -/******/ resolveQueue(queue); -/******/ }, (e) => { -/******/ obj[webpackError] = e; -/******/ resolveQueue(queue); -/******/ }); -/******/ var obj = {}; -/******/ obj[webpackQueues] = (fn) => (fn(queue)); -/******/ return obj; -/******/ } -/******/ } -/******/ var ret = {}; -/******/ ret[webpackQueues] = x => {}; -/******/ ret[webpackExports] = dep; -/******/ return ret; -/******/ })); -/******/ __nccwpck_require__.a = (module, body, hasAwait) => { -/******/ var queue; -/******/ hasAwait && ((queue = []).d = -1); -/******/ var depQueues = new Set(); -/******/ var exports = module.exports; -/******/ var currentDeps; -/******/ var outerResolve; -/******/ var reject; -/******/ var promise = new Promise((resolve, rej) => { -/******/ reject = rej; -/******/ outerResolve = resolve; -/******/ }); -/******/ promise[webpackExports] = exports; -/******/ promise[webpackQueues] = (fn) => (queue && fn(queue), depQueues.forEach(fn), promise["catch"](x => {})); -/******/ module.exports = promise; -/******/ body((deps) => { -/******/ currentDeps = wrapDeps(deps); -/******/ var fn; -/******/ var getResult = () => (currentDeps.map((d) => { -/******/ if(d[webpackError]) throw d[webpackError]; -/******/ return d[webpackExports]; -/******/ })) -/******/ var promise = new Promise((resolve) => { -/******/ fn = () => (resolve(getResult)); -/******/ fn.r = 0; -/******/ var fnQueue = (q) => (q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn)))); -/******/ currentDeps.map((dep) => (dep[webpackQueues](fnQueue))); +/******/ } +/******/ var wrapDeps = (deps) => (deps.map((dep) => { +/******/ if(dep !== null && typeof dep === "object") { +/******/ if(dep[webpackQueues]) return dep; +/******/ if(dep.then) { +/******/ var queue = []; +/******/ queue.d = 0; +/******/ dep.then((r) => { +/******/ obj[webpackExports] = r; +/******/ resolveQueue(queue); +/******/ }, (e) => { +/******/ obj[webpackError] = e; +/******/ resolveQueue(queue); /******/ }); -/******/ return fn.r ? promise : getResult(); -/******/ }, (err) => ((err ? reject(promise[webpackError] = err) : outerResolve(exports)), resolveQueue(queue))); -/******/ queue && queue.d < 0 && (queue.d = 0); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/compat get default export */ -/******/ (() => { -/******/ // getDefaultExport function for compatibility with non-harmony modules -/******/ __nccwpck_require__.n = (module) => { -/******/ var getter = module && module.__esModule ? -/******/ () => (module['default']) : -/******/ () => (module); -/******/ __nccwpck_require__.d(getter, { a: getter }); -/******/ return getter; -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/define property getters */ -/******/ (() => { -/******/ // define getter functions for harmony exports -/******/ __nccwpck_require__.d = (exports, definition) => { -/******/ for(var key in definition) { -/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { -/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); -/******/ } +/******/ var obj = {}; +/******/ obj[webpackQueues] = (fn) => (fn(queue)); +/******/ return obj; /******/ } -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/hasOwnProperty shorthand */ -/******/ (() => { -/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) -/******/ })(); -/******/ -/******/ /* webpack/runtime/make namespace object */ -/******/ (() => { -/******/ // define __esModule on exports -/******/ __nccwpck_require__.r = (exports) => { -/******/ if(typeof Symbol !== 'undefined' && Symbol.toStringTag) { -/******/ Object.defineProperty(exports, Symbol.toStringTag, { value: 'Module' }); +/******/ } +/******/ var ret = {}; +/******/ ret[webpackQueues] = x => {}; +/******/ ret[webpackExports] = dep; +/******/ return ret; +/******/ })); +/******/ __nccwpck_require__.a = (module, body, hasAwait) => { +/******/ var queue; +/******/ hasAwait && ((queue = []).d = -1); +/******/ var depQueues = new Set(); +/******/ var exports = module.exports; +/******/ var currentDeps; +/******/ var outerResolve; +/******/ var reject; +/******/ var promise = new Promise((resolve, rej) => { +/******/ reject = rej; +/******/ outerResolve = resolve; +/******/ }); +/******/ promise[webpackExports] = exports; +/******/ promise[webpackQueues] = (fn) => (queue && fn(queue), depQueues.forEach(fn), promise["catch"](x => {})); +/******/ module.exports = promise; +/******/ body((deps) => { +/******/ currentDeps = wrapDeps(deps); +/******/ var fn; +/******/ var getResult = () => (currentDeps.map((d) => { +/******/ if(d[webpackError]) throw d[webpackError]; +/******/ return d[webpackExports]; +/******/ })) +/******/ var promise = new Promise((resolve) => { +/******/ fn = () => (resolve(getResult)); +/******/ fn.r = 0; +/******/ var fnQueue = (q) => (q !== queue && !depQueues.has(q) && (depQueues.add(q), q && !q.d && (fn.r++, q.push(fn)))); +/******/ currentDeps.map((dep) => (dep[webpackQueues](fnQueue))); +/******/ }); +/******/ return fn.r ? promise : getResult(); +/******/ }, (err) => ((err ? reject(promise[webpackError] = err) : outerResolve(exports)), resolveQueue(queue))); +/******/ queue && queue.d < 0 && (queue.d = 0); +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/define property getters */ +/******/ (() => { +/******/ // define getter functions for harmony exports +/******/ __nccwpck_require__.d = (exports, definition) => { +/******/ for(var key in definition) { +/******/ if(__nccwpck_require__.o(definition, key) && !__nccwpck_require__.o(exports, key)) { +/******/ Object.defineProperty(exports, key, { enumerable: true, get: definition[key] }); /******/ } -/******/ Object.defineProperty(exports, '__esModule', { value: true }); -/******/ }; -/******/ })(); -/******/ -/******/ /* webpack/runtime/compat */ -/******/ -/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = __dirname + "/"; -/******/ +/******/ } +/******/ }; +/******/ })(); +/******/ +/******/ /* webpack/runtime/hasOwnProperty shorthand */ +/******/ (() => { +/******/ __nccwpck_require__.o = (obj, prop) => (Object.prototype.hasOwnProperty.call(obj, prop)) +/******/ })(); +/******/ +/******/ /* webpack/runtime/compat */ +/******/ +/******/ if (typeof __nccwpck_require__ !== 'undefined') __nccwpck_require__.ab = new URL('.', import.meta.url).pathname.slice(import.meta.url.match(/^file:\/\/\/\w:/) ? 1 : 0, -1) + "/"; +/******/ /************************************************************************/ -/******/ -/******/ // startup -/******/ // Load entry module and return exports -/******/ // This entry module used 'module' so it can't be inlined -/******/ var __webpack_exports__ = __nccwpck_require__(6136); -/******/ module.exports = __webpack_exports__; -/******/ -/******/ })() -; \ No newline at end of file +/******/ +/******/ // startup +/******/ // Load entry module and return exports +/******/ // This entry module used 'module' so it can't be inlined +/******/ var __webpack_exports__ = __nccwpck_require__(5483); +/******/ __webpack_exports__ = await __webpack_exports__; +/******/ diff --git a/dist/package.json b/dist/package.json new file mode 100644 index 0000000..3dbc1ca --- /dev/null +++ b/dist/package.json @@ -0,0 +1,3 @@ +{ + "type": "module" +} diff --git a/index.js b/index.js index f881ac9..e850d2a 100644 --- a/index.js +++ b/index.js @@ -1,111 +1,5 @@ -import { getInput, info, setOutput, setFailed } from "@actions/core"; -import { spawn, spawnSync } from "child_process"; -import { dirname, resolve as resolvePath } from "path"; - -function ensureToolchain(manifestPath) { - // rustup 1.28+ no longer auto-installs the toolchain pinned in - // rust-toolchain.toml when the cargo/rustc proxy is invoked. Running - // `rustup toolchain install` with no toolchain argument installs the - // *active* toolchain (i.e. whatever rust-toolchain.toml in the manifest's - // directory selects), without compiling anything. - const cwd = dirname(resolvePath(manifestPath)); - const result = spawnSync( - "rustup", - ["toolchain", "install", "--no-self-update"], - { cwd, encoding: "utf8" }, - ); - if (result.error && result.error.code === "ENOENT") { - info("rustup not found on PATH; skipping toolchain install"); - return; - } - if (result.status !== 0) { - const stderr = (result.stderr || "").trim(); - throw new Error( - `rustup failed to install the active toolchain` + - (stderr ? `: ${stderr}` : ""), - ); - } -} - -function runCargoMetadata(manifestPath) { - return new Promise((resolve, reject) => { - const cmd = spawn("cargo", [ - "metadata", - "--manifest-path=" + manifestPath, - "--no-deps", - "--format-version", - "1", - ]); - - const stdoutChunks = []; - const stderrChunks = []; - cmd.stdout.on("data", (chunk) => stdoutChunks.push(chunk)); - cmd.stderr.on("data", (chunk) => stderrChunks.push(chunk)); - - cmd.on("error", (error) => { - reject(new Error(`cargo metadata failed to spawn: ${error.message}`)); - }); - - cmd.on("close", (code) => { - const stderr = Buffer.concat(stderrChunks).toString().trim(); - if (code !== 0) { - reject( - new Error( - `cargo metadata failed (exit code ${code})` + - (stderr ? `: ${stderr}` : ""), - ), - ); - return; - } - if (stderr) { - info(stderr); - } - const stdout = Buffer.concat(stdoutChunks).toString(); - try { - resolve(JSON.parse(stdout)); - } catch (err) { - reject( - new Error(`failed to parse cargo metadata output: ${err.message}`), - ); - } - }); - }); -} - -async function run() { - const manifestPath = getInput("manifest-path", { required: true }); - ensureToolchain(manifestPath); - const metadata = await runCargoMetadata(manifestPath); - setActionOutput(metadata); -} - -function setActionOutput(metadata) { - setOutput("metadata", JSON.stringify(metadata)); - if (metadata.hasOwnProperty("packages")) { - let allPackages = []; - let packagesToPublish = []; - let matrix = []; - metadata.packages.forEach((pkg) => { - allPackages.push(pkg.name); - if (pkg.hasOwnProperty("publish") && pkg.publish !== false) { - packagesToPublish.push(pkg.name); - } - if (pkg.hasOwnProperty("features")) { - const names = Object.getOwnPropertyNames(pkg.features); - if (names.length === 0) { - matrix.push(`--package=${pkg.name}`); - } else { - names.forEach((feature) => { - matrix.push(`--package=${pkg.name} --features=${feature}`); - }); - } - } - }); - setOutput("packages", JSON.stringify(allPackages)); - setOutput("publish", JSON.stringify(packagesToPublish)); - setOutput("matrix", JSON.stringify(matrix)); - } -} +import { setFailed } from "@actions/core"; +import { run } from "./lib.js"; try { await run(); diff --git a/lib.js b/lib.js new file mode 100644 index 0000000..14549a8 --- /dev/null +++ b/lib.js @@ -0,0 +1,171 @@ +import { getInput, info, setOutput } from "@actions/core"; +import { spawn, spawnSync } from "child_process"; +import { dirname, resolve as resolvePath } from "path"; + +export function ensureToolchain(manifestPath) { + // rustup 1.28+ no longer auto-installs through the cargo/rustc proxy. + // `rustup toolchain install` with no toolchain arg installs the active + // toolchain (whatever rust-toolchain.toml selects), without compiling. + const cwd = dirname(resolvePath(manifestPath)); + const result = spawnSync( + "rustup", + ["toolchain", "install", "--no-self-update"], + { cwd, encoding: "utf8" }, + ); + if (result.error && result.error.code === "ENOENT") { + info("rustup not found on PATH; skipping toolchain install"); + return; + } + if (result.status !== 0) { + const stderr = (result.stderr || "").trim(); + throw new Error( + `rustup failed to install the active toolchain` + + (stderr ? `: ${stderr}` : ""), + ); + } +} + +export function runCargoMetadata(manifestPath) { + // Spawn cargo from the manifest's directory so rustup picks up any + // adjacent rust-toolchain.toml (rustup walks up, not down). Pass the + // absolute manifest path so a relative input doesn't get re-resolved + // against the new cwd. + const absManifestPath = resolvePath(manifestPath); + const cwd = dirname(absManifestPath); + return new Promise((resolve, reject) => { + const cmd = spawn( + "cargo", + [ + "metadata", + "--manifest-path=" + absManifestPath, + "--no-deps", + "--format-version", + "1", + ], + { cwd }, + ); + + const stdoutChunks = []; + const stderrChunks = []; + cmd.stdout.on("data", (chunk) => stdoutChunks.push(chunk)); + cmd.stderr.on("data", (chunk) => stderrChunks.push(chunk)); + + cmd.on("error", (error) => { + reject(new Error(`cargo metadata failed to spawn: ${error.message}`)); + }); + + cmd.on("close", (code) => { + const stderr = Buffer.concat(stderrChunks).toString().trim(); + if (code !== 0) { + reject( + new Error( + `cargo metadata failed (exit code ${code})` + + (stderr ? `: ${stderr}` : ""), + ), + ); + return; + } + if (stderr) { + info(stderr); + } + const stdout = Buffer.concat(stdoutChunks).toString(); + try { + resolve(JSON.parse(stdout)); + } catch (err) { + reject( + new Error(`failed to parse cargo metadata output: ${err.message}`), + ); + } + }); + }); +} + +// Cargo's `publish` field semantics: +// null → unrestricted (publishable) +// [] → `publish = false` in Cargo.toml (NOT publishable) +// ["registry"] → restricted to specific registries (still publishable) +function isPublishable(pkg) { + return ( + pkg.publish === null || + (Array.isArray(pkg.publish) && pkg.publish.length > 0) + ); +} + +// Compare two `major.minor[.patch]` strings numerically (so "1.10" > "1.9", +// not the lexicographic opposite). Missing components default to 0. +function compareVersion(a, b) { + const parse = (v) => v.split(".").map((p) => parseInt(p, 10) || 0); + const av = parse(a); + const bv = parse(b); + for (let i = 0; i < 3; i++) { + const diff = (av[i] ?? 0) - (bv[i] ?? 0); + if (diff !== 0) return diff; + } + return 0; +} + +export function parseMetadata(metadata) { + const packages = []; + const publish = []; + const matrix = []; + let rustVersion = null; + let edition = null; + for (const pkg of metadata.packages ?? []) { + packages.push(pkg.name); + if (isPublishable(pkg)) { + publish.push(pkg.name); + } + // Sort so matrix output is stable regardless of cargo's feature + // emission order (currently a BTreeMap, but not contractually so). + const features = Object.keys(pkg.features ?? {}).sort(); + if (features.length === 0) { + matrix.push(`--package=${pkg.name}`); + } else { + for (const feature of features) { + matrix.push(`--package=${pkg.name} --features=${feature}`); + } + } + if (pkg.rust_version != null) { + if ( + rustVersion === null || + compareVersion(pkg.rust_version, rustVersion) > 0 + ) { + rustVersion = pkg.rust_version; + } + } + if (pkg.edition != null) { + // Editions are years ("2015", "2018", "2021", "2024") — simple int compare. + if ( + edition === null || + parseInt(pkg.edition, 10) > parseInt(edition, 10) + ) { + edition = pkg.edition; + } + } + } + return { + packages, + publish, + matrix, + rustVersion: rustVersion ?? "", + edition: edition ?? "", + }; +} + +export function writeOutputs(metadata) { + const { packages, publish, matrix, rustVersion, edition } = + parseMetadata(metadata); + setOutput("metadata", JSON.stringify(metadata)); + setOutput("packages", JSON.stringify(packages)); + setOutput("publish", JSON.stringify(publish)); + setOutput("matrix", JSON.stringify(matrix)); + setOutput("rust-version", rustVersion); + setOutput("edition", edition); +} + +export async function run() { + const manifestPath = getInput("manifest-path"); + ensureToolchain(manifestPath); + const metadata = await runCargoMetadata(manifestPath); + writeOutputs(metadata); +} diff --git a/package-lock.json b/package-lock.json index e233fdf..e81b6ba 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { "name": "rust-metadata-action", - "version": "0.3.0", + "version": "1.0.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "rust-metadata-action", - "version": "0.3.0", + "version": "1.0.0", "license": "MIT", "dependencies": { - "@actions/core": "^3.0.1", - "@actions/github": "^9.1.1" + "@actions/core": "^3.0.1" }, "devDependencies": { "@vercel/ncc": "^0.38.4", @@ -46,165 +45,12 @@ "@actions/io": "^3.0.2" } }, - "node_modules/@actions/github": { - "version": "9.1.1", - "resolved": "https://registry.npmjs.org/@actions/github/-/github-9.1.1.tgz", - "integrity": "sha512-tL5JbYOBZHc0ngEnCsaDcryUizIUIlQyIMwy1Wkx93H5HzbBJ7TbiPx2PnFjBwZW0Vh05JmfFZhecE6gglYegA==", - "license": "MIT", - "dependencies": { - "@actions/http-client": "^3.0.2", - "@octokit/core": "^7.0.6", - "@octokit/plugin-paginate-rest": "^14.0.0", - "@octokit/plugin-rest-endpoint-methods": "^17.0.0", - "@octokit/request": "^10.0.7", - "@octokit/request-error": "^7.1.0", - "undici": "^6.23.0" - } - }, - "node_modules/@actions/http-client": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-3.0.2.tgz", - "integrity": "sha512-JP38FYYpyqvUsz+Igqlc/JG6YO9PaKuvqjM3iGvaLqFnJ7TFmcLyy2IDrY0bI0qCQug8E9K+elv5ZNfw62ZJzA==", - "license": "MIT", - "dependencies": { - "tunnel": "^0.0.6", - "undici": "^6.23.0" - } - }, "node_modules/@actions/io": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/@actions/io/-/io-3.0.2.tgz", "integrity": "sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==", "license": "MIT" }, - "node_modules/@octokit/auth-token": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/@octokit/auth-token/-/auth-token-6.0.0.tgz", - "integrity": "sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==", - "license": "MIT", - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/core": { - "version": "7.0.6", - "resolved": "https://registry.npmjs.org/@octokit/core/-/core-7.0.6.tgz", - "integrity": "sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==", - "license": "MIT", - "dependencies": { - "@octokit/auth-token": "^6.0.0", - "@octokit/graphql": "^9.0.3", - "@octokit/request": "^10.0.6", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "before-after-hook": "^4.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/endpoint": { - "version": "11.0.3", - "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-11.0.3.tgz", - "integrity": "sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/graphql": { - "version": "9.0.3", - "resolved": "https://registry.npmjs.org/@octokit/graphql/-/graphql-9.0.3.tgz", - "integrity": "sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==", - "license": "MIT", - "dependencies": { - "@octokit/request": "^10.0.6", - "@octokit/types": "^16.0.0", - "universal-user-agent": "^7.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/openapi-types": { - "version": "27.0.0", - "resolved": "https://registry.npmjs.org/@octokit/openapi-types/-/openapi-types-27.0.0.tgz", - "integrity": "sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==", - "license": "MIT" - }, - "node_modules/@octokit/plugin-paginate-rest": { - "version": "14.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-paginate-rest/-/plugin-paginate-rest-14.0.0.tgz", - "integrity": "sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/plugin-rest-endpoint-methods": { - "version": "17.0.0", - "resolved": "https://registry.npmjs.org/@octokit/plugin-rest-endpoint-methods/-/plugin-rest-endpoint-methods-17.0.0.tgz", - "integrity": "sha512-B5yCyIlOJFPqUUeiD0cnBJwWJO8lkJs5d8+ze9QDP6SvfiXSz1BF+91+0MeI1d2yxgOhU/O+CvtiZ9jSkHhFAw==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - }, - "peerDependencies": { - "@octokit/core": ">=6" - } - }, - "node_modules/@octokit/request": { - "version": "10.0.8", - "resolved": "https://registry.npmjs.org/@octokit/request/-/request-10.0.8.tgz", - "integrity": "sha512-SJZNwY9pur9Agf7l87ywFi14W+Hd9Jg6Ifivsd33+/bGUQIjNujdFiXII2/qSlN2ybqUHfp5xpekMEjIBTjlSw==", - "license": "MIT", - "dependencies": { - "@octokit/endpoint": "^11.0.3", - "@octokit/request-error": "^7.0.2", - "@octokit/types": "^16.0.0", - "fast-content-type-parse": "^3.0.0", - "json-with-bigint": "^3.5.3", - "universal-user-agent": "^7.0.2" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/request-error": { - "version": "7.1.0", - "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-7.1.0.tgz", - "integrity": "sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==", - "license": "MIT", - "dependencies": { - "@octokit/types": "^16.0.0" - }, - "engines": { - "node": ">= 20" - } - }, - "node_modules/@octokit/types": { - "version": "16.0.0", - "resolved": "https://registry.npmjs.org/@octokit/types/-/types-16.0.0.tgz", - "integrity": "sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==", - "license": "MIT", - "dependencies": { - "@octokit/openapi-types": "^27.0.0" - } - }, "node_modules/@vercel/ncc": { "version": "0.38.4", "resolved": "https://registry.npmjs.org/@vercel/ncc/-/ncc-0.38.4.tgz", @@ -215,34 +61,6 @@ "ncc": "dist/ncc/cli.js" } }, - "node_modules/before-after-hook": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/before-after-hook/-/before-after-hook-4.0.0.tgz", - "integrity": "sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==", - "license": "Apache-2.0" - }, - "node_modules/fast-content-type-parse": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/fast-content-type-parse/-/fast-content-type-parse-3.0.0.tgz", - "integrity": "sha512-ZvLdcY8P+N8mGQJahJV5G4U88CSvT1rP8ApL6uETe88MBXrBHAkZlSEySdUlyztF7ccb+Znos3TFqaepHxdhBg==", - "funding": [ - { - "type": "github", - "url": "https://github.com/sponsors/fastify" - }, - { - "type": "opencollective", - "url": "https://opencollective.com/fastify" - } - ], - "license": "MIT" - }, - "node_modules/json-with-bigint": { - "version": "3.5.8", - "resolved": "https://registry.npmjs.org/json-with-bigint/-/json-with-bigint-3.5.8.tgz", - "integrity": "sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==", - "license": "MIT" - }, "node_modules/prettier": { "version": "3.8.3", "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.3.tgz", @@ -274,12 +92,6 @@ "engines": { "node": ">=18.17" } - }, - "node_modules/universal-user-agent": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/universal-user-agent/-/universal-user-agent-7.0.3.tgz", - "integrity": "sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==", - "license": "ISC" } } } diff --git a/package.json b/package.json index 801f5fd..fc16002 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "rust-metadata-action", - "version": "0.3.0", + "version": "1.0.0", "description": "GitHub action to retrieve metadata, including crates, features, and other relevant information of the Rust project", "keywords": [ "GitHub", @@ -9,11 +9,13 @@ "Metadata" ], "main": "index.js", + "type": "module", "scripts": { - "all": "npm run fmt && npm run build", + "all": "npm run fmt && npm run build && npm test", "build": "ncc build index.js --license licenses.txt", - "fmt": "prettier --write index.js package.json action.yml .github/dependabot.yml .github/workflows/*.yml", - "check": "prettier --check index.js package.json action.yml .github/dependabot.yml .github/workflows/*.yml" + "test": "node --test tests/*.test.js", + "fmt": "prettier --write index.js lib.js tests/*.js package.json action.yml .github/dependabot.yml .github/workflows/*.yml", + "check": "prettier --check index.js lib.js tests/*.js package.json action.yml .github/dependabot.yml .github/workflows/*.yml" }, "repository": { "type": "git", @@ -26,8 +28,7 @@ }, "homepage": "https://github.com/sv-tools/rust-metadata-action#readme", "dependencies": { - "@actions/core": "^3.0.1", - "@actions/github": "^9.1.1" + "@actions/core": "^3.0.1" }, "devDependencies": { "@vercel/ncc": "^0.38.4", diff --git a/tests/fixtures/single-private/Cargo.toml b/tests/fixtures/single-private/Cargo.toml new file mode 100644 index 0000000..d949cea --- /dev/null +++ b/tests/fixtures/single-private/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "single-private" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "single-private" +path = "src/main.rs" diff --git a/tests/fixtures/single-private/src/main.rs b/tests/fixtures/single-private/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/fixtures/single-private/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/fixtures/single-public/Cargo.toml b/tests/fixtures/single-public/Cargo.toml new file mode 100644 index 0000000..ec5cf5c --- /dev/null +++ b/tests/fixtures/single-public/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "single-public" +version = "0.1.0" +edition = "2021" +rust-version = "1.85" + +[[bin]] +name = "single-public" +path = "src/main.rs" diff --git a/tests/fixtures/single-public/src/main.rs b/tests/fixtures/single-public/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/fixtures/single-public/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/fixtures/toolchain-pinned/Cargo.toml b/tests/fixtures/toolchain-pinned/Cargo.toml new file mode 100644 index 0000000..9b74b9a --- /dev/null +++ b/tests/fixtures/toolchain-pinned/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "toolchain-pinned" +version = "0.1.0" +edition = "2021" +publish = false + +[[bin]] +name = "toolchain-pinned" +path = "src/main.rs" diff --git a/tests/fixtures/toolchain-pinned/rust-toolchain.toml b/tests/fixtures/toolchain-pinned/rust-toolchain.toml new file mode 100644 index 0000000..3a44591 --- /dev/null +++ b/tests/fixtures/toolchain-pinned/rust-toolchain.toml @@ -0,0 +1,3 @@ +[toolchain] +channel = "1.85.0" +profile = "minimal" diff --git a/tests/fixtures/toolchain-pinned/src/main.rs b/tests/fixtures/toolchain-pinned/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/fixtures/toolchain-pinned/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/fixtures/workspace-mixed/Cargo.toml b/tests/fixtures/workspace-mixed/Cargo.toml new file mode 100644 index 0000000..59b7084 --- /dev/null +++ b/tests/fixtures/workspace-mixed/Cargo.toml @@ -0,0 +1,3 @@ +[workspace] +members = ["app", "private-lib", "feature-lib"] +resolver = "2" diff --git a/tests/fixtures/workspace-mixed/app/Cargo.toml b/tests/fixtures/workspace-mixed/app/Cargo.toml new file mode 100644 index 0000000..72a5bd1 --- /dev/null +++ b/tests/fixtures/workspace-mixed/app/Cargo.toml @@ -0,0 +1,9 @@ +[package] +name = "app" +version = "0.1.0" +edition = "2021" +rust-version = "1.85" + +[[bin]] +name = "app" +path = "src/main.rs" diff --git a/tests/fixtures/workspace-mixed/app/src/main.rs b/tests/fixtures/workspace-mixed/app/src/main.rs new file mode 100644 index 0000000..f328e4d --- /dev/null +++ b/tests/fixtures/workspace-mixed/app/src/main.rs @@ -0,0 +1 @@ +fn main() {} diff --git a/tests/fixtures/workspace-mixed/feature-lib/Cargo.toml b/tests/fixtures/workspace-mixed/feature-lib/Cargo.toml new file mode 100644 index 0000000..fd31b0e --- /dev/null +++ b/tests/fixtures/workspace-mixed/feature-lib/Cargo.toml @@ -0,0 +1,13 @@ +[package] +name = "feature-lib" +version = "0.1.0" +edition = "2024" +rust-version = "1.90" + +[lib] +path = "src/lib.rs" + +[features] +default = ["foo"] +foo = [] +bar = [] diff --git a/tests/fixtures/workspace-mixed/feature-lib/src/lib.rs b/tests/fixtures/workspace-mixed/feature-lib/src/lib.rs new file mode 100644 index 0000000..1e9e17a --- /dev/null +++ b/tests/fixtures/workspace-mixed/feature-lib/src/lib.rs @@ -0,0 +1,14 @@ +#[cfg(feature = "foo")] +pub fn which() -> &'static str { + "foo" +} + +#[cfg(all(feature = "bar", not(feature = "foo")))] +pub fn which() -> &'static str { + "bar" +} + +#[cfg(not(any(feature = "foo", feature = "bar")))] +pub fn which() -> &'static str { + "none" +} diff --git a/tests/fixtures/workspace-mixed/private-lib/Cargo.toml b/tests/fixtures/workspace-mixed/private-lib/Cargo.toml new file mode 100644 index 0000000..ce44778 --- /dev/null +++ b/tests/fixtures/workspace-mixed/private-lib/Cargo.toml @@ -0,0 +1,8 @@ +[package] +name = "private-lib" +version = "0.1.0" +edition = "2021" +publish = false + +[lib] +path = "src/lib.rs" diff --git a/tests/fixtures/workspace-mixed/private-lib/src/lib.rs b/tests/fixtures/workspace-mixed/private-lib/src/lib.rs new file mode 100644 index 0000000..3d91a6a --- /dev/null +++ b/tests/fixtures/workspace-mixed/private-lib/src/lib.rs @@ -0,0 +1,3 @@ +pub fn hi() -> &'static str { + "private" +} diff --git a/tests/parse-metadata.test.js b/tests/parse-metadata.test.js new file mode 100644 index 0000000..e5a1ccf --- /dev/null +++ b/tests/parse-metadata.test.js @@ -0,0 +1,160 @@ +import { test } from "node:test"; +import assert from "node:assert/strict"; +import { parseMetadata } from "../lib.js"; + +const baselinePkg = { + name: "ignored", + publish: null, + features: {}, +}; + +test("empty workspace yields empty arrays", () => { + const out = parseMetadata({ packages: [] }); + assert.deepEqual(out, { + packages: [], + publish: [], + matrix: [], + rustVersion: "", + edition: "", + }); +}); + +test("missing packages key yields empty arrays", () => { + const out = parseMetadata({}); + assert.deepEqual(out, { + packages: [], + publish: [], + matrix: [], + rustVersion: "", + edition: "", + }); +}); + +test("featureless package goes into matrix as bare --package", () => { + const out = parseMetadata({ + packages: [{ ...baselinePkg, name: "foo" }], + }); + assert.deepEqual(out.matrix, ["--package=foo"]); +}); + +test("each feature produces its own matrix entry", () => { + const out = parseMetadata({ + packages: [ + { + ...baselinePkg, + name: "foo", + features: { default: ["a"], full: ["a", "b"] }, + }, + ], + }); + assert.deepEqual(out.matrix, [ + "--package=foo --features=default", + "--package=foo --features=full", + ]); +}); + +test("matrix entries are sorted alphabetically by feature name", () => { + // Deliberate reverse-alphabetical insertion order so we'd notice if + // sorting were dropped (JS preserves Object insertion order). + const out = parseMetadata({ + packages: [ + { + ...baselinePkg, + name: "foo", + features: { zebra: [], mango: [], apple: [] }, + }, + ], + }); + assert.deepEqual(out.matrix, [ + "--package=foo --features=apple", + "--package=foo --features=mango", + "--package=foo --features=zebra", + ]); +}); + +test("publish=null is publishable", () => { + const out = parseMetadata({ + packages: [{ ...baselinePkg, name: "foo", publish: null }], + }); + assert.deepEqual(out.publish, ["foo"]); +}); + +test("publish=[] (i.e. publish = false in Cargo.toml) is NOT publishable", () => { + const out = parseMetadata({ + packages: [{ ...baselinePkg, name: "foo", publish: [] }], + }); + assert.deepEqual(out.publish, []); +}); + +test("publish=['registry'] (restricted) is still publishable", () => { + const out = parseMetadata({ + packages: [{ ...baselinePkg, name: "foo", publish: ["my-registry"] }], + }); + assert.deepEqual(out.publish, ["foo"]); +}); + +test("packages output always contains every package, regardless of publish", () => { + const out = parseMetadata({ + packages: [ + { ...baselinePkg, name: "a", publish: null }, + { ...baselinePkg, name: "b", publish: [] }, + { ...baselinePkg, name: "c", publish: ["my-registry"] }, + ], + }); + assert.deepEqual(out.packages, ["a", "b", "c"]); + assert.deepEqual(out.publish, ["a", "c"]); +}); + +test("rust-version is empty string when no package declares one", () => { + const out = parseMetadata({ + packages: [{ ...baselinePkg, name: "foo", rust_version: null }], + }); + assert.equal(out.rustVersion, ""); +}); + +test("rust-version returns the max across packages", () => { + const out = parseMetadata({ + packages: [ + { ...baselinePkg, name: "a", rust_version: "1.85" }, + { ...baselinePkg, name: "b", rust_version: "1.90" }, + { ...baselinePkg, name: "c", rust_version: null }, + ], + }); + assert.equal(out.rustVersion, "1.90"); +}); + +test("rust-version compares numerically, not lexicographically", () => { + const out = parseMetadata({ + packages: [ + { ...baselinePkg, name: "a", rust_version: "1.9" }, + { ...baselinePkg, name: "b", rust_version: "1.10" }, + ], + }); + assert.equal(out.rustVersion, "1.10"); +}); + +test("rust-version handles patch-level versions", () => { + const out = parseMetadata({ + packages: [ + { ...baselinePkg, name: "a", rust_version: "1.85.0" }, + { ...baselinePkg, name: "b", rust_version: "1.85.5" }, + ], + }); + assert.equal(out.rustVersion, "1.85.5"); +}); + +test("edition returns the max across packages", () => { + const out = parseMetadata({ + packages: [ + { ...baselinePkg, name: "a", edition: "2021" }, + { ...baselinePkg, name: "b", edition: "2024" }, + { ...baselinePkg, name: "c", edition: "2018" }, + ], + }); + assert.equal(out.edition, "2024"); +}); + +test("edition is empty string for empty workspace", () => { + const out = parseMetadata({ packages: [] }); + assert.equal(out.edition, ""); +});