Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
25ad802
feat: resolve Vite+ version from package.json / catalog
fengmk2 Jul 1, 2026
840f70b
refactor: simplify catalog resolution and fix bun-root dispatch
fengmk2 Jul 1, 2026
f6f4546
fix: preserve v-prefixed dist-tags when resolving from version-file
fengmk2 Jul 1, 2026
a40b027
fix: harden version-file catalog resolution per code review
fengmk2 Jul 1, 2026
b7b5143
feat: auto-detect vite-plus version from package.json by default
fengmk2 Jul 1, 2026
92ad3a1
test: add CI job verifying default version auto-detect logic
fengmk2 Jul 1, 2026
c7df2a6
fix: parse the current 'vp vX' --version output for the version output
fengmk2 Jul 1, 2026
b3c815f
fix: bound catalog walk to the workspace for out-of-workspace manifests
fengmk2 Jul 1, 2026
ebab6f1
fix: reject marker-less semver ranges in version-file resolution
fengmk2 Jul 1, 2026
933b361
fix: drop spurious pnpm-workspace.yml catalog source
fengmk2 Jul 1, 2026
e412bd8
feat: fall back to latest with a warning when version-file can't be r…
fengmk2 Jul 1, 2026
981f91c
feat: resolve vite-plus version from the lockfile for package.json ra…
fengmk2 Jul 1, 2026
08aa79e
refactor: consolidate version resolution and share constants (simplif…
fengmk2 Jul 1, 2026
913dd98
fix: address review comments on version/lockfile resolution
fengmk2 Jul 1, 2026
64e190c
fix: only auto-detect vite-plus from dependencies/devDependencies
fengmk2 Jul 1, 2026
9c8b783
fix: check devDependencies before dependencies for the vite-plus spec
fengmk2 Jul 1, 2026
c88aa65
fix: search upward for the lockfile and de-binary the test fixture
fengmk2 Jul 1, 2026
e24b170
fix: fall back when yarn/bun lockfiles are ambiguous about vite-plus
fengmk2 Jul 1, 2026
455d30e
test: complete node:fs mocks in version/lockfile suites
fengmk2 Jul 1, 2026
9e663b4
fix: tighten lockfile gating to registry-resolvable specs and pnpm im…
fengmk2 Jul 1, 2026
3e95779
fix: only use the hoisted npm entry for lockfile-member packages
fengmk2 Jul 1, 2026
739a762
fix: anchor pnpm packages-scan to unscoped keys
fengmk2 Jul 1, 2026
96a1fb5
fix: correct isWithin/.. path handling and align docs with behavior
fengmk2 Jul 1, 2026
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
93 changes: 93 additions & 0 deletions .github/workflows/test.yml
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,99 @@ jobs:
echo "$ACTUAL" | grep -q "^v${{ matrix.node-version }}\." || (echo "Expected Node.js v${{ matrix.node-version }}.x but got $ACTUAL" && exit 1)
fi

test-default-version:
# End-to-end check of the default version-resolution logic: with no `version`
# or `version-file` input, the action auto-detects the vite-plus version from
# the project's package.json (direct pin or catalog), and falls back to
# `latest` when it can't be resolved to an exact version (e.g. a semver
# range). Pin VP_PR_VERSION to "" so a manual pr_version dispatch can't
# override the version we assert on (a pkg.pr.new build would ignore
# VP_VERSION entirely). Fixtures live under $RUNNER_TEMP, outside the repo,
# so the committed pnpm-workspace.yaml doesn't influence resolution.
env:
VP_PR_VERSION: ""
strategy:
fail-fast: false
matrix:
include:
# Pins an older exact version (not the current `latest`) so a match
# proves the pin was honored rather than a coincidental latest.
- scenario: exact
expected: "0.2.0"
- scenario: catalog
expected: "0.2.0"
# A package.json range resolves to the exact version pinned by the
# lockfile rather than falling back to latest.
- scenario: lockfile-pnpm
expected: "0.2.0"
- scenario: lockfile-npm
expected: "0.2.0"
# A range with no lockfile can't be resolved, so auto-detect falls back
# to `latest` instead of failing the run.
- scenario: fallback-range
expected: latest
runs-on: ubuntu-latest
steps:
- uses: taiki-e/checkout-action@7d1e50e93dc4fb3bba58f85018fadf77898aee8b # v1.4.2

- name: Create ${{ matrix.scenario }} fixture
shell: bash
run: |
DIR="${RUNNER_TEMP//\\//}/default-version-${{ matrix.scenario }}"
mkdir -p "$DIR"
case "${{ matrix.scenario }}" in
exact)
echo '{"name":"fixture","private":true,"devDependencies":{"vite-plus":"0.2.0"}}' > "$DIR/package.json"
;;
catalog)
echo '{"name":"fixture","private":true,"devDependencies":{"vite-plus":"catalog:"}}' > "$DIR/package.json"
printf 'catalog:\n vite-plus: 0.2.0\n' > "$DIR/pnpm-workspace.yaml"
;;
lockfile-pnpm)
echo '{"name":"fixture","private":true,"devDependencies":{"vite-plus":"^0.2.0"}}' > "$DIR/package.json"
printf 'importers:\n .:\n devDependencies:\n vite-plus:\n specifier: ^0.2.0\n version: 0.2.0\n' > "$DIR/pnpm-lock.yaml"
;;
lockfile-npm)
echo '{"name":"fixture","private":true,"devDependencies":{"vite-plus":"^0.2.0"}}' > "$DIR/package.json"
echo '{"name":"fixture","lockfileVersion":3,"packages":{"":{"devDependencies":{"vite-plus":"^0.2.0"}},"node_modules/vite-plus":{"version":"0.2.0"}}}' > "$DIR/package-lock.json"
;;
fallback-range)
echo '{"name":"fixture","private":true,"devDependencies":{"vite-plus":"^0.2.0"}}' > "$DIR/package.json"
;;
esac

- name: Setup Vite+ (auto-detect, no version input)
uses: ./
id: setup
with:
working-directory: ${{ runner.temp }}/default-version-${{ matrix.scenario }}
run-install: false
cache: false

- name: Assert resolved version
shell: bash
env:
EXPECTED: ${{ matrix.expected }}
ACTUAL: ${{ steps.setup.outputs.version }}
run: |
echo "scenario=${{ matrix.scenario }} expected=$EXPECTED actual=$ACTUAL"
if [ -z "$ACTUAL" ] || [ "$ACTUAL" = "unknown" ]; then
echo "::error::action did not report an installed version (got '$ACTUAL')"
exit 1
fi
if [ "$EXPECTED" = "latest" ]; then
# `latest` moves, so require a concrete semver (the output is the
# resolved global version, never the tag) rather than a literal.
echo "$ACTUAL" | grep -Eq '^[0-9]+\.[0-9]+\.[0-9]+' || {
echo "::error::fallback did not resolve to a semver version: '$ACTUAL'"
exit 1
}
elif [ "$ACTUAL" != "$EXPECTED" ]; then
echo "::error::expected vite-plus $EXPECTED but the action installed $ACTUAL"
exit 1
fi
echo "OK: ${{ matrix.scenario }} resolved to $ACTUAL"

test-cache-pnpm:
runs-on: ubuntu-latest
steps:
Expand Down
114 changes: 100 additions & 14 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,91 @@ steps:
cache: true
```

### Version from `package.json` / Catalog

Keep a single source of truth for the Vite+ version by resolving it from the
checked-out project instead of duplicating it in the workflow.

By default (when neither `version` nor `version-file` is set), the action reads
the `vite-plus` entry from the project's `package.json` and installs that
version. When that entry is a semver range like `^0.2.0` (which can't be
installed directly), it is resolved to the exact version recorded in the
lockfile (`pnpm-lock.yaml`, `package-lock.json`, `npm-shrinkwrap.json`,
`yarn.lock`, or `bun.lock`; the binary `bun.lockb` can't be read). It falls back
to `latest` only when nothing pins a resolvable version. So a project that pins
`vite-plus` needs no extra configuration:

```yaml
steps:
- uses: actions/checkout@v6
- uses: voidzero-dev/setup-vp@v1
with:
cache: true
```

To resolve from a specific file, set `version-file` explicitly. Like the
auto-detect default, an explicit `version-file` that can't be resolved logs a
warning and falls back to `latest` (it does not fail the run); the warning is
worth watching for, since it means the pinned version was not applied:

```yaml
steps:
- uses: actions/checkout@v6
- uses: voidzero-dev/setup-vp@v1
with:
version-file: package.json
cache: true
```

When the `package.json` entry is `catalog:` / `catalog:<name>`, it is resolved
through the nearest catalog source (searching upward from the manifest),
covering every package manager that implements the `catalog:` protocol:

```jsonc
// package.json
{
"devDependencies": {
"vite-plus": "catalog:",
},
}
```

- **pnpm**: `pnpm-workspace.yaml`
Comment thread
fengmk2 marked this conversation as resolved.

```yaml
catalog:
vite-plus: 0.2.0
```

- **yarn** (>= 4.10): `.yarnrc.yml`

```yaml
catalog:
vite-plus: 0.2.0
```

- **bun**: root `package.json` (`catalog`/`catalogs`, top-level or under `workspaces`)

```jsonc
{
"workspaces": {
"packages": ["packages/*"],
"catalog": { "vite-plus": "0.2.0" },
},
}
```

For **npm** (no catalog feature) or any project that pins the version directly,
just declare an exact version (`"vite-plus": "0.2.0"`) and it is used as-is.

You can also point `version-file` straight at `pnpm-workspace.yaml` or
`.yarnrc.yml` to read its default catalog entry. An explicit `version` always
Comment thread
fengmk2 marked this conversation as resolved.
takes precedence over `version-file`. A resolved value must be an exact version
or dist-tag: when an explicit `version-file` yields a semver range (e.g.
`^0.2.0`) or an alias (`npm:` / `git:`), it can't be installed directly, so the
action warns and falls back to `latest`. (Auto-detection instead resolves a
`package.json` range through the lockfile, as described above.)

### Advanced Run Install

```yaml
Expand Down Expand Up @@ -211,20 +296,21 @@ jobs:

## Inputs

| Input | Description | Required | Default |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | -------- | -------------- |
| `version` | Version of Vite+ to install | No | `latest` |
| `node-version` | Node.js version to install via `vp env use` | No | Latest LTS |
| `node-version-file` | Path to file containing Node.js version (`.nvmrc`, `.node-version`, `.tool-versions`, `package.json`) | No | |
| `working-directory` | Project directory used for relative paths, lockfile auto-detection, environment checks, and default install | No | Workspace root |
| `run-install` | Run `vp install` after setup. Accepts boolean or YAML object with `cwd`/`args` | No | `true` |
| `sfw` | Wrap `vp install` with [Socket Firewall Free](https://docs.socket.dev/docs/socket-firewall-free) (`sfw`) | No | `false` |
| `cache` | Enable caching of project dependencies | No | `false` |
| `cache-dependency-path` | Path to lock file for cache key generation | No | Auto-detected |
| `registry-url` | Optional registry to set up for auth. Sets the registry in `.npmrc` and reads auth from `NODE_AUTH_TOKEN` | No | |
| `scope` | Optional scope for scoped registries. Falls back to repo owner for GitHub Packages | No | |

When `working-directory` is set, relative `run-install.cwd`, `node-version-file`, and `cache-dependency-path` values are resolved from that directory.
| Input | Description | Required | Default |
| ----------------------- | ----------------------------------------------------------------------------------------------------------- | -------- | --------------- |
| `version` | Version of Vite+ to install. Takes precedence over `version-file` | No | auto / `latest` |
| `version-file` | Path to a file to resolve the Vite+ version from (`package.json`, `pnpm-workspace.yaml`, or `.yarnrc.yml`) | No | |
Comment thread
fengmk2 marked this conversation as resolved.
| `node-version` | Node.js version to install via `vp env use` | No | Latest LTS |
| `node-version-file` | Path to file containing Node.js version (`.nvmrc`, `.node-version`, `.tool-versions`, `package.json`) | No | |
| `working-directory` | Project directory used for relative paths, lockfile auto-detection, environment checks, and default install | No | Workspace root |
| `run-install` | Run `vp install` after setup. Accepts boolean or YAML object with `cwd`/`args` | No | `true` |
| `sfw` | Wrap `vp install` with [Socket Firewall Free](https://docs.socket.dev/docs/socket-firewall-free) (`sfw`) | No | `false` |
| `cache` | Enable caching of project dependencies | No | `false` |
| `cache-dependency-path` | Path to lock file for cache key generation | No | Auto-detected |
| `registry-url` | Optional registry to set up for auth. Sets the registry in `.npmrc` and reads auth from `NODE_AUTH_TOKEN` | No | |
| `scope` | Optional scope for scoped registries. Falls back to repo owner for GitHub Packages | No | |

When `working-directory` is set, relative `run-install.cwd`, `node-version-file`, `version-file`, and `cache-dependency-path` values are resolved from that directory.

## Outputs

Expand Down
7 changes: 5 additions & 2 deletions action.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,9 +7,12 @@ branding:

inputs:
version:
description: "Version of Vite+ to install"
description: "Version of Vite+ to install. Takes precedence over version-file. When neither is set, the version is auto-detected from the project's package.json (its vite-plus dependency or catalog entry; a semver range is resolved to the exact version in the lockfile), falling back to the latest release."
required: false
default: ""
version-file:
description: "Path to a file to resolve the Vite+ version from (package.json, pnpm-workspace.yaml, or .yarnrc.yml). A package.json `vite-plus` entry of `catalog:`/`catalog:<name>` is resolved through the nearest catalog source: pnpm-workspace.yaml (pnpm), .yarnrc.yml (yarn), or a root package.json `catalog`/`catalogs` (bun). Ignored when version is specified."
Comment thread
fengmk2 marked this conversation as resolved.
required: false
default: "latest"
run-install:
description: "Run `vp install` after setup. Accepts boolean or YAML object with cwd/args."
required: false
Expand Down
Loading
Loading