mirror of
https://github.com/pnpm/action-setup.git
synced 2024-12-23 08:26:29 +08:00
feat: add version_file_path option
Using the same logic as `actions/setup-node`, we can support `asdf`, `volta` or other version management techniques.
This commit is contained in:
parent
ac5bf11548
commit
6217a9d8c4
116
.github/workflows/test.yaml
vendored
116
.github/workflows/test.yaml
vendored
|
@ -35,6 +35,122 @@ jobs:
|
||||||
- name: 'Test: install'
|
- name: 'Test: install'
|
||||||
run: pnpm install
|
run: pnpm install
|
||||||
|
|
||||||
|
test_version_file_asdf:
|
||||||
|
name: Test with version file (asdf)
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pnpm:
|
||||||
|
- 4.11.1
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macos-latest
|
||||||
|
- windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Run the action
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version_file_path: test/.tool-versions
|
||||||
|
|
||||||
|
- name: 'Test: which'
|
||||||
|
run: which pnpm; which pnpx
|
||||||
|
|
||||||
|
- name: 'Test: install'
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
test_version_file_engines:
|
||||||
|
name: Test with version file (package.json engines)
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pnpm:
|
||||||
|
- 4.11.1
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macos-latest
|
||||||
|
- windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Run the action
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version_file_path: test/package.engines.json
|
||||||
|
|
||||||
|
- name: 'Test: which'
|
||||||
|
run: which pnpm; which pnpx
|
||||||
|
|
||||||
|
- name: 'Test: install'
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
test_version_file_volta:
|
||||||
|
name: Test with version file (volta)
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pnpm:
|
||||||
|
- 4.11.1
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macos-latest
|
||||||
|
- windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Run the action
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version_file_path: test/package.volta.json
|
||||||
|
|
||||||
|
- name: 'Test: which'
|
||||||
|
run: which pnpm; which pnpx
|
||||||
|
|
||||||
|
- name: 'Test: install'
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
|
test_version_file_volta_extends:
|
||||||
|
name: Test with version file (volta extends)
|
||||||
|
|
||||||
|
runs-on: ${{ matrix.os }}
|
||||||
|
|
||||||
|
strategy:
|
||||||
|
fail-fast: false
|
||||||
|
matrix:
|
||||||
|
pnpm:
|
||||||
|
- 4.11.1
|
||||||
|
os:
|
||||||
|
- ubuntu-latest
|
||||||
|
- macos-latest
|
||||||
|
- windows-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v4
|
||||||
|
|
||||||
|
- name: Run the action
|
||||||
|
uses: ./
|
||||||
|
with:
|
||||||
|
version_file_path: test/package.volta-extends.json
|
||||||
|
|
||||||
|
- name: 'Test: which'
|
||||||
|
run: which pnpm; which pnpx
|
||||||
|
|
||||||
|
- name: 'Test: install'
|
||||||
|
run: pnpm install
|
||||||
|
|
||||||
test_dest:
|
test_dest:
|
||||||
name: Test with dest
|
name: Test with dest
|
||||||
|
|
||||||
|
|
26
README.md
26
README.md
|
@ -16,6 +16,12 @@ Version of pnpm to install.
|
||||||
|
|
||||||
otherwise, this field is **required** It supports npm versioning scheme, it could be an exact version (such as `6.24.1`), or a version range (such as `6`, `6.x.x`, `6.24.x`, `^6.24.1`, `*`, etc.), or `latest`.
|
otherwise, this field is **required** It supports npm versioning scheme, it could be an exact version (such as `6.24.1`), or a version range (such as `6`, `6.x.x`, `6.24.x`, `^6.24.1`, `*`, etc.), or `latest`.
|
||||||
|
|
||||||
|
### `version_file_path`
|
||||||
|
|
||||||
|
The `version_file_path` input accepts a path to a file containing the version of pnpm to be used. For example `.tool-versions` (if you use `asdf`), or package.json (if you use the engines property or [`volta`](https://volta.sh) instead of corepack).
|
||||||
|
|
||||||
|
The action will search for the version file relative to the repository root.
|
||||||
|
|
||||||
### `dest`
|
### `dest`
|
||||||
|
|
||||||
**Optional** Where to store pnpm files.
|
**Optional** Where to store pnpm files.
|
||||||
|
@ -83,6 +89,26 @@ jobs:
|
||||||
version: 9
|
version: 9
|
||||||
```
|
```
|
||||||
|
|
||||||
|
### Install only pnpm with a version file
|
||||||
|
|
||||||
|
This works when you use `volta`, `asdf`, etc.
|
||||||
|
|
||||||
|
```yaml
|
||||||
|
on:
|
||||||
|
- push
|
||||||
|
- pull_request
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
install:
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- uses: pnpm/action-setup@v4
|
||||||
|
with:
|
||||||
|
version_file_path: ".tool-versions" # with asdf
|
||||||
|
# version_file_path: "package.json" # with volta
|
||||||
|
```
|
||||||
|
|
||||||
### Install only pnpm with `packageManager`
|
### Install only pnpm with `packageManager`
|
||||||
|
|
||||||
Omit `version` input to use the version in the [`packageManager` field in the `package.json`](https://nodejs.org/api/corepack.html).
|
Omit `version` input to use the version in the [`packageManager` field in the `package.json`](https://nodejs.org/api/corepack.html).
|
||||||
|
|
|
@ -7,6 +7,9 @@ inputs:
|
||||||
version:
|
version:
|
||||||
description: Version of pnpm to install
|
description: Version of pnpm to install
|
||||||
required: false
|
required: false
|
||||||
|
version_file_path:
|
||||||
|
description: "Path to a version file. Eg: '.tool-versions' for asdf or 'package.json' for volta"
|
||||||
|
required: false
|
||||||
dest:
|
dest:
|
||||||
description: Where to store pnpm files
|
description: Where to store pnpm files
|
||||||
required: false
|
required: false
|
||||||
|
|
BIN
dist/index.js
vendored
BIN
dist/index.js
vendored
Binary file not shown.
|
@ -4,6 +4,7 @@ import { RunInstall, parseRunInstall } from './run-install'
|
||||||
|
|
||||||
export interface Inputs {
|
export interface Inputs {
|
||||||
readonly version?: string
|
readonly version?: string
|
||||||
|
readonly versionFilePath?: string
|
||||||
readonly dest: string
|
readonly dest: string
|
||||||
readonly runInstall: RunInstall[]
|
readonly runInstall: RunInstall[]
|
||||||
readonly packageJsonFile: string
|
readonly packageJsonFile: string
|
||||||
|
@ -18,6 +19,7 @@ const parseInputPath = (name: string) => expandTilde(getInput(name, options))
|
||||||
|
|
||||||
export const getInputs = (): Inputs => ({
|
export const getInputs = (): Inputs => ({
|
||||||
version: getInput('version'),
|
version: getInput('version'),
|
||||||
|
versionFilePath: getInput('version_file_path'),
|
||||||
dest: parseInputPath('dest'),
|
dest: parseInputPath('dest'),
|
||||||
runInstall: parseRunInstall('run_install'),
|
runInstall: parseRunInstall('run_install'),
|
||||||
packageJsonFile: parseInputPath('package_json_file'),
|
packageJsonFile: parseInputPath('package_json_file'),
|
||||||
|
|
|
@ -1,14 +1,14 @@
|
||||||
import { addPath, exportVariable } from '@actions/core'
|
import { addPath, exportVariable } from '@actions/core'
|
||||||
import { spawn } from 'child_process'
|
import { spawn } from 'child_process'
|
||||||
import { rm, writeFile, mkdir } from 'fs/promises'
|
import { rm, writeFile, mkdir } from 'fs/promises'
|
||||||
import { readFileSync } from 'fs'
|
import { readFileSync, existsSync } from 'fs'
|
||||||
import path from 'path'
|
import path from 'path'
|
||||||
import { execPath } from 'process'
|
import { execPath } from 'process'
|
||||||
import util from 'util'
|
import util from 'util'
|
||||||
import { Inputs } from '../inputs'
|
import { Inputs } from '../inputs'
|
||||||
|
|
||||||
export async function runSelfInstaller(inputs: Inputs): Promise<number> {
|
export async function runSelfInstaller(inputs: Inputs): Promise<number> {
|
||||||
const { version, dest, packageJsonFile, standalone } = inputs
|
const { version, versionFilePath, dest, packageJsonFile, standalone } = inputs
|
||||||
|
|
||||||
// prepare self install
|
// prepare self install
|
||||||
await rm(dest, { recursive: true, force: true })
|
await rm(dest, { recursive: true, force: true })
|
||||||
|
@ -19,7 +19,7 @@ export async function runSelfInstaller(inputs: Inputs): Promise<number> {
|
||||||
await writeFile(pkgJson, JSON.stringify({ private: true }))
|
await writeFile(pkgJson, JSON.stringify({ private: true }))
|
||||||
|
|
||||||
// prepare target pnpm
|
// prepare target pnpm
|
||||||
const target = await readTarget({ version, packageJsonFile, standalone })
|
const target = await readTarget({ version, versionFilePath, packageJsonFile, standalone })
|
||||||
const cp = spawn(execPath, [path.join(__dirname, 'pnpm.cjs'), 'install', target, '--no-lockfile'], {
|
const cp = spawn(execPath, [path.join(__dirname, 'pnpm.cjs'), 'install', target, '--no-lockfile'], {
|
||||||
cwd: dest,
|
cwd: dest,
|
||||||
stdio: ['pipe', 'inherit', 'inherit'],
|
stdio: ['pipe', 'inherit', 'inherit'],
|
||||||
|
@ -37,12 +37,71 @@ export async function runSelfInstaller(inputs: Inputs): Promise<number> {
|
||||||
return exitCode
|
return exitCode
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Nearly identical to the function `actions/setup-node` uses.
|
||||||
|
// See https://github.com/actions/setup-node/blob/39370e3970a6d050c480ffad4ff0ed4d3fdee5af/src/util.ts#L8
|
||||||
|
function getPnpmVersionFromFile(versionFilePath: string) {
|
||||||
|
if (!existsSync(versionFilePath)) {
|
||||||
|
throw new Error(
|
||||||
|
`The specified pnpm version file at: ${versionFilePath} does not exist`
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
const contents = readFileSync(versionFilePath, 'utf8')
|
||||||
|
|
||||||
|
// Try parsing the file as a `package.json` file.
|
||||||
|
try {
|
||||||
|
const manifest = JSON.parse(contents)
|
||||||
|
|
||||||
|
// Presume package.json file.
|
||||||
|
if (typeof manifest === 'object' && !!manifest) {
|
||||||
|
// Support Volta.
|
||||||
|
// See https://docs.volta.sh/guide/understanding#managing-your-project
|
||||||
|
if (manifest.volta?.pnpm) {
|
||||||
|
return manifest.volta.pnpm as string
|
||||||
|
}
|
||||||
|
|
||||||
|
if (manifest.engines?.pnpm) {
|
||||||
|
return manifest.engines.pnpm as string
|
||||||
|
}
|
||||||
|
|
||||||
|
// Support Volta workspaces.
|
||||||
|
// See https://docs.volta.sh/advanced/workspaces
|
||||||
|
if (manifest.volta?.extends) {
|
||||||
|
const extendedFilePath = path.resolve(
|
||||||
|
path.dirname(versionFilePath),
|
||||||
|
manifest.volta.extends
|
||||||
|
)
|
||||||
|
console.info('Resolving pnpm version from ' + extendedFilePath)
|
||||||
|
return getPnpmVersionFromFile(extendedFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
|
// If contents are an object, we parsed JSON
|
||||||
|
// this can happen if pnpm-version-file is a package.json
|
||||||
|
// yet contains no volta.pnpm or engines.pnpm
|
||||||
|
//
|
||||||
|
// If pnpm-version file is _not_ JSON, control flow
|
||||||
|
// will not have reached these lines.
|
||||||
|
//
|
||||||
|
// And because we've reached here, we know the contents
|
||||||
|
// *are* JSON, so no further string parsing makes sense.
|
||||||
|
return
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
console.info('pnpm version file is not JSON file')
|
||||||
|
}
|
||||||
|
|
||||||
|
const found = contents.match(/^(?:pnpm\s+)?v?(?<version>[^\s]+)$/m)
|
||||||
|
return found?.groups?.version ?? contents.trim()
|
||||||
|
}
|
||||||
|
|
||||||
async function readTarget(opts: {
|
async function readTarget(opts: {
|
||||||
readonly version?: string | undefined
|
readonly version?: string | undefined
|
||||||
|
readonly versionFilePath?: string | undefined
|
||||||
readonly packageJsonFile: string
|
readonly packageJsonFile: string
|
||||||
readonly standalone: boolean
|
readonly standalone: boolean
|
||||||
}) {
|
}) {
|
||||||
const { version, packageJsonFile, standalone } = opts
|
const { versionFilePath, packageJsonFile, standalone } = opts
|
||||||
|
let { version } = opts
|
||||||
const { GITHUB_WORKSPACE } = process.env
|
const { GITHUB_WORKSPACE } = process.env
|
||||||
|
|
||||||
let packageManager
|
let packageManager
|
||||||
|
@ -56,6 +115,14 @@ async function readTarget(opts: {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof versionFilePath === "string" && typeof version === "string") {
|
||||||
|
throw new Error("Multiple version determination methods specified: 'version' and 'version_file_path'. Please specify only one.")
|
||||||
|
}
|
||||||
|
|
||||||
|
if (versionFilePath) {
|
||||||
|
version = getPnpmVersionFromFile(versionFilePath)
|
||||||
|
}
|
||||||
|
|
||||||
if (version) {
|
if (version) {
|
||||||
if (
|
if (
|
||||||
typeof packageManager === 'string' &&
|
typeof packageManager === 'string' &&
|
||||||
|
|
1
test/.tool-versions
Normal file
1
test/.tool-versions
Normal file
|
@ -0,0 +1 @@
|
||||||
|
pnpm 4.11.1
|
1
test/package.engines.json
Normal file
1
test/package.engines.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"engines":{"pnpm":"4.11.1"}}
|
1
test/package.volta-extends.json
Normal file
1
test/package.volta-extends.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"volta":{"extends":"./package.volta.json"}}
|
1
test/package.volta.json
Normal file
1
test/package.volta.json
Normal file
|
@ -0,0 +1 @@
|
||||||
|
{"volta":{"pnpm":"4.11.1"}}
|
Loading…
Reference in New Issue
Block a user