diff --git a/README.md b/README.md index 505998f..a5cb48d 100644 --- a/README.md +++ b/README.md @@ -6,9 +6,10 @@ A collection of reusable GitHub Actions workflows and composite actions for Zote ### Composite actions -- [x] Setup JavaScript (`setup-js`): checkout, then automatically pick the toolchain - - [x] `setup-vp`: [Vite+](https://github.com/voidzero-dev/setup-vp) setup (auto-detected when `vite-plus` is in the devDependencies of `package.json`) - - [x] `setup-node`: Node.js + package manager setup and dependency install +- [x] Setup JavaScript (`setup-js`): checkout, then auto-pick a toolchain + - [x] Vite+ ([voidzero-dev/setup-vp](https://github.com/voidzero-dev/setup-vp)) when `vite-plus` is a devDependency + - [x] [pnpm/setup](https://github.com/pnpm/setup) when pnpm > 11 or version undeclared + - [x] `setup-node` (internal) otherwise (npm / yarn / pnpm ≤ 11) ### Reusable GitHub Actions workflows diff --git a/setup-js/action.yml b/setup-js/action.yml index d7eb2b4..3726cfa 100644 --- a/setup-js/action.yml +++ b/setup-js/action.yml @@ -15,7 +15,7 @@ inputs: description: Whether to fetch all commits and submodules. default: "false" - # --- shared by setup-node and setup-vp --- + # --- shared by setup-node and Vite+ --- auto-install: description: "Whether to automatically install dependencies." default: "true" @@ -25,18 +25,18 @@ inputs: required: false default: "true" - # --- setup-node only (ignored when Vite+ is used) --- + # --- Node.js setup (ignored when Vite+ is used) --- node-version: - description: "Version Spec of the version to use. Examples: 12.x, 10.15.1, >=10.15.0. Ignored when Vite+ is used." + description: "Version Spec of the Node.js version to use. Examples: 12.x, 10.15.1, >=10.15.0. Ignored when Vite+ is used. When pnpm is set up via pnpm/setup (pnpm > 11 or undeclared), this is passed as its `runtime` input, with `lts/*` translated to `lts`." default: "lts/*" package-manager: - description: "Package manager to use. Examples: npm, yarn, pnpm. Leave empty to auto-detect. Ignored when Vite+ is used." + description: "Package manager to use. Examples: npm, yarn, pnpm. Leave empty to auto-detect from lock files. Ignored when Vite+ is used. When pnpm is detected (or forced), pnpm > 11 or an undeclared version uses pnpm/setup@v2; pnpm <= 11 uses setup-node (pnpm/action-setup)." default: "" - # --- setup-vp only --- + # --- Vite+ only --- vp: - description: "Use Vite+ (setup-vp) instead of the classic Node.js setup. 'true'/'false' to force; leave empty to auto-detect from the devDependencies of package.json (vite-plus)." + description: "Use Vite+ (voidzero-dev/setup-vp) instead of the classic Node.js setup. 'true'/'false' to force; leave empty to auto-detect from the devDependencies of package.json (vite-plus)." required: false default: "" @@ -48,6 +48,7 @@ inputs: runs: using: composite steps: + # --- Checkout --- - name: Checkout uses: actions/checkout@v7 with: @@ -56,8 +57,9 @@ runs: fetch-depth: "${{ inputs.fetch-all == 'true' && '0' || '1' }}" submodules: "${{ inputs.fetch-all == 'true' && 'recursive' || false }}" + # --- Detect toolchain and package manager --- - name: Detect Vite+ usage - id: vp + id: detect-vp shell: bash run: | USE_VP="${{ inputs.vp }}" @@ -71,25 +73,88 @@ runs: fi fi echo "use_vp=$USE_VP" >> $GITHUB_OUTPUT - if [[ "$USE_VP" == "true" ]]; then - echo "Using Vite+ (setup-vp)" + echo "Vite+ usage: $USE_VP" + + - name: Detect package manager + id: detect-pm + if: ${{ steps.detect-vp.outputs.use_vp != 'true' }} + shell: bash + run: | + PM="${{ inputs.package-manager }}" + if [[ -z "$PM" ]]; then + if [[ -f "pnpm-lock.yaml" ]]; then + PM="pnpm" + elif [[ -f "yarn.lock" ]]; then + PM="yarn" + elif [[ -f "package-lock.json" ]]; then + PM="npm" + else + echo "No lock file found. Defaulting to pnpm." + PM="pnpm" + fi + fi + echo "pm=$PM" >> $GITHUB_OUTPUT + echo "Package manager: $PM" + + - name: Detect pnpm version and strategy + id: detect-pnpm + if: ${{ steps.detect-vp.outputs.use_vp != 'true' && steps.detect-pm.outputs.pm == 'pnpm' }} + shell: bash + run: | + # pnpm >= 12 (or an undeclared version, which resolves to the latest + # pnpm, always >= 12) can use pnpm/setup@v2, which installs pnpm and a + # Node.js runtime in one step. pnpm <= 11 keeps the classic + # setup-node path (pnpm/action-setup): pnpm/setup only supports + # >= 11, and v11 ships no binary for Intel macOS runners. + USE_NEW=false + PM_VERSION="" + if [[ -f package.json ]]; then + # Top-level "packageManager": "pnpm@12.1.0[+sha256...]" + PM_VERSION=$(sed -n 's/.*"packageManager"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' package.json | head -1) + # Or devEngines.packageManager.version + if [[ -z "$PM_VERSION" ]]; then + PM_VERSION=$(sed -n 's/.*"packageManager"[[:space:]]*:[[:space:]]*{[^}]*"version"[[:space:]]*:[[:space:]]*"\([^"]*\)".*/\1/p' package.json | head -1) + fi + fi + if [[ -n "$PM_VERSION" ]]; then + # Extract the first number (major): handles "12.1.0", "pnpm@12.1.0", + # "12.1.0+sha256...", "^12.0.0", ">=12" and "12.x". + MAJOR=$(echo "$PM_VERSION" | sed 's/[^0-9]*\([0-9]*\).*/\1/') + echo "Detected pnpm version: $PM_VERSION (major: $MAJOR)" + if [[ "$MAJOR" =~ ^[0-9]+$ ]] && [[ "$MAJOR" -gt 11 ]]; then + USE_NEW=true + fi else - echo "Using classic Node.js setup (setup-node)" + echo "No pnpm version declared in package.json" + USE_NEW=true fi + echo "use_new_pnpm=$USE_NEW" >> $GITHUB_OUTPUT + echo "pnpm strategy: use_new_pnpm=$USE_NEW" - - name: Setup Vite+ (setup-vp) - if: ${{ steps.vp.outputs.use_vp == 'true' }} - uses: $/setup-vp + # --- Setup --- + - name: Setup Vite+ (voidzero-dev/setup-vp) + if: ${{ steps.detect-vp.outputs.use_vp == 'true' }} + uses: voidzero-dev/setup-vp@v1 with: version: ${{ inputs.vp-version }} run-install: ${{ inputs.auto-install }} cache: ${{ inputs.cache }} + - name: Setup pnpm + Node.js (pnpm/setup) + if: ${{ steps.detect-vp.outputs.use_vp != 'true' && steps.detect-pnpm.outputs.use_new_pnpm == 'true' }} + uses: pnpm/setup@v2 + with: + # node-version is a setup-node spec (lts/*, 22.x, >=20.0.0); pnpm/setup + # expects its own runtime spec (node@), so translate the default. + runtime: ${{ (inputs.node-version == '' || inputs.node-version == 'lts/*') && 'node@lts' || format('node@{0}', inputs.node-version) }} + install: ${{ inputs.auto-install }} + cache: ${{ inputs.cache }} + - name: Setup Node.js (setup-node) - if: ${{ steps.vp.outputs.use_vp != 'true' }} + if: ${{ steps.detect-vp.outputs.use_vp != 'true' && steps.detect-pnpm.outputs.use_new_pnpm != 'true' }} uses: $/setup-node with: node-version: ${{ inputs.node-version }} auto-install: ${{ inputs.auto-install }} - package-manager: ${{ inputs.package-manager }} + package-manager: ${{ steps.detect-pm.outputs.pm }} cache: ${{ inputs.cache }} diff --git a/setup-js/example.yml b/setup-js/example.yml index 36a8be5..30b2ceb 100644 --- a/setup-js/example.yml +++ b/setup-js/example.yml @@ -9,9 +9,15 @@ jobs: steps: - name: Setup JS (auto-detect Vite+) uses: zotero-plugin-dev/workflows/setup-js@main - # By default, setup-vp is used when `vite-plus` is found in the - # devDependencies of package.json. Set `vp: 'true'` or `vp: 'false'` - # to force a specific setup. + # By default, the Vite+ setup (voidzero-dev/setup-vp) is used when + # `vite-plus` is found in the devDependencies of package.json. Set + # `vp: 'true'` or `vp: 'false'` to force a specific setup. + # + # When pnpm is used, pnpm > 11 (or an undeclared pnpm version) is + # set up via pnpm/setup@v2, which also installs the Node.js runtime + # (`node-version` is passed through, with `lts/*` translated to + # `lts`); pnpm <= 11 keeps the classic setup-node path + # (pnpm/action-setup). # with: # vp: "true" # vp-version: "0.2.0" diff --git a/setup-node/action.yml b/setup-node/action.yml index d2a32c9..2261850 100644 --- a/setup-node/action.yml +++ b/setup-node/action.yml @@ -10,9 +10,10 @@ inputs: description: Whether to automatically install dependencies. default: "true" + # Internal: the package manager is detected by setup-js and passed in here. package-manager: - description: "Package manager to use. Examples: npm, yarn, pnpm. Leave empty to auto-detect." - default: "" + description: "Package manager to use. Examples: npm, yarn, pnpm. Required." + required: true cache: description: "Whether to cache dependencies." @@ -22,40 +23,20 @@ inputs: runs: using: composite steps: - - name: Auto detect package manager - id: detect - shell: bash - run: | - if [[ -n "${{ inputs.package-manager }}" ]]; then - echo "pm=${{ inputs.package-manager }}" >> $GITHUB_OUTPUT - exit 0 - fi - - if [[ -f "pnpm-lock.yaml" ]]; then - echo "pm=pnpm" >> $GITHUB_OUTPUT - elif [[ -f "yarn.lock" ]]; then - echo "pm=yarn" >> $GITHUB_OUTPUT - elif [[ -f "package-lock.json" ]]; then - echo "pm=npm" >> $GITHUB_OUTPUT - else - echo "No lock file found. Defaulting to pnpm." - echo "pm=pnpm" >> $GITHUB_OUTPUT - fi - - name: Install pnpm - if: ${{ steps.detect.outputs.pm == 'pnpm' }} + if: ${{ inputs.package-manager == 'pnpm' }} uses: pnpm/action-setup@v6 - name: Setup node uses: actions/setup-node@v7 with: node-version: ${{ inputs.node-version }} - cache: ${{ inputs.cache == 'true' && steps.detect.outputs.pm || '' }} + cache: ${{ inputs.cache == 'true' && inputs.package-manager || '' }} registry-url: "https://registry.npmjs.org" - name: Install dependencies if: ${{ inputs.auto-install == 'true' }} shell: bash run: | - echo "Using package manager: ${{ steps.detect.outputs.pm }}" - ${{ steps.detect.outputs.pm }} install + echo "Using package manager: ${{ inputs.package-manager }}" + ${{ inputs.package-manager }} install diff --git a/setup-vp/action.yml b/setup-vp/action.yml deleted file mode 100644 index 739eb28..0000000 --- a/setup-vp/action.yml +++ /dev/null @@ -1,28 +0,0 @@ -name: Setup Vite+ (vp) -description: Setup Vite+ (vp) via voidzero-dev/setup-vp - -inputs: - version: - description: "Version of Vite+ to install. Leave empty to auto-detect from the project." - required: false - default: "" - - run-install: - description: "Whether to run `vp install` after setup." - required: false - default: "true" - - cache: - description: "Whether to cache project dependencies." - required: false - default: "true" - -runs: - using: composite - steps: - - name: Setup vp - uses: voidzero-dev/setup-vp@v1 - with: - version: ${{ inputs.version }} - run-install: ${{ inputs.run-install }} - cache: ${{ inputs.cache }}