├── .commitlintrc.js
├── .eslintrc.js
├── .github
├── CODEOWNERS
├── ISSUE_TEMPLATE
│ ├── bug.yml
│ └── config.yml
├── actions
│ ├── create-check
│ │ └── action.yml
│ └── install-latest-npm
│ │ └── action.yml
├── dependabot.yml
├── matchers
│ └── tap.json
├── settings.yml
└── workflows
│ ├── audit.yml
│ ├── ci-release.yml
│ ├── ci.yml
│ ├── codeql-analysis.yml
│ ├── post-dependabot.yml
│ ├── pull-request.yml
│ ├── release-integration.yml
│ └── release.yml
├── .gitignore
├── .npmrc
├── .release-please-manifest.json
├── CHANGELOG.md
├── CODE_OF_CONDUCT.md
├── CONTRIBUTING.md
├── LICENSE
├── README.md
├── SECURITY.md
├── lib
├── current-env.js
├── dev-engines.js
└── index.js
├── package.json
├── release-please-config.json
└── test
├── check-dev-engines.js
├── check-engine.js
└── check-platform.js
/.commitlintrc.js:
--------------------------------------------------------------------------------
1 | /* This file is automatically added by @npmcli/template-oss. Do not edit. */
2 |
3 | module.exports = {
4 | extends: ['@commitlint/config-conventional'],
5 | rules: {
6 | 'type-enum': [2, 'always', ['feat', 'fix', 'docs', 'deps', 'chore']],
7 | 'header-max-length': [2, 'always', 80],
8 | 'subject-case': [0],
9 | 'body-max-line-length': [0],
10 | 'footer-max-line-length': [0],
11 | },
12 | }
13 |
--------------------------------------------------------------------------------
/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /* This file is automatically added by @npmcli/template-oss. Do not edit. */
2 |
3 | 'use strict'
4 |
5 | const { readdirSync: readdir } = require('fs')
6 |
7 | const localConfigs = readdir(__dirname)
8 | .filter((file) => file.startsWith('.eslintrc.local.'))
9 | .map((file) => `./${file}`)
10 |
11 | module.exports = {
12 | root: true,
13 | ignorePatterns: [
14 | 'tap-testdir*/',
15 | ],
16 | extends: [
17 | '@npmcli',
18 | ...localConfigs,
19 | ],
20 | }
21 |
--------------------------------------------------------------------------------
/.github/CODEOWNERS:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | * @npm/cli-team
4 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/bug.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Bug
4 | description: File a bug/issue
5 | title: "[BUG]
"
6 | labels: [ Bug, Needs Triage ]
7 |
8 | body:
9 | - type: checkboxes
10 | attributes:
11 | label: Is there an existing issue for this?
12 | description: Please [search here](./issues) to see if an issue already exists for your problem.
13 | options:
14 | - label: I have searched the existing issues
15 | required: true
16 | - type: textarea
17 | attributes:
18 | label: Current Behavior
19 | description: A clear & concise description of what you're experiencing.
20 | validations:
21 | required: false
22 | - type: textarea
23 | attributes:
24 | label: Expected Behavior
25 | description: A clear & concise description of what you expected to happen.
26 | validations:
27 | required: false
28 | - type: textarea
29 | attributes:
30 | label: Steps To Reproduce
31 | description: Steps to reproduce the behavior.
32 | value: |
33 | 1. In this environment...
34 | 2. With this config...
35 | 3. Run '...'
36 | 4. See error...
37 | validations:
38 | required: false
39 | - type: textarea
40 | attributes:
41 | label: Environment
42 | description: |
43 | examples:
44 | - **npm**: 7.6.3
45 | - **Node**: 13.14.0
46 | - **OS**: Ubuntu 20.04
47 | - **platform**: Macbook Pro
48 | value: |
49 | - npm:
50 | - Node:
51 | - OS:
52 | - platform:
53 | validations:
54 | required: false
55 |
--------------------------------------------------------------------------------
/.github/ISSUE_TEMPLATE/config.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | blank_issues_enabled: true
4 |
--------------------------------------------------------------------------------
/.github/actions/create-check/action.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: 'Create Check'
4 | inputs:
5 | name:
6 | required: true
7 | token:
8 | required: true
9 | sha:
10 | required: true
11 | check-name:
12 | default: ''
13 | outputs:
14 | check-id:
15 | value: ${{ steps.create-check.outputs.check_id }}
16 | runs:
17 | using: "composite"
18 | steps:
19 | - name: Get Workflow Job
20 | uses: actions/github-script@v7
21 | id: workflow
22 | env:
23 | JOB_NAME: "${{ inputs.name }}"
24 | SHA: "${{ inputs.sha }}"
25 | with:
26 | result-encoding: string
27 | script: |
28 | const { repo: { owner, repo}, runId, serverUrl } = context
29 | const { JOB_NAME, SHA } = process.env
30 |
31 | const job = await github.rest.actions.listJobsForWorkflowRun({
32 | owner,
33 | repo,
34 | run_id: runId,
35 | per_page: 100
36 | }).then(r => r.data.jobs.find(j => j.name.endsWith(JOB_NAME)))
37 |
38 | return [
39 | `This check is assosciated with ${serverUrl}/${owner}/${repo}/commit/${SHA}.`,
40 | 'Run logs:',
41 | job?.html_url || `could not be found for a job ending with: "${JOB_NAME}"`,
42 | ].join(' ')
43 | - name: Create Check
44 | uses: LouisBrunner/checks-action@v1.6.0
45 | id: create-check
46 | with:
47 | token: ${{ inputs.token }}
48 | sha: ${{ inputs.sha }}
49 | status: in_progress
50 | name: ${{ inputs.check-name || inputs.name }}
51 | output: |
52 | {"summary":"${{ steps.workflow.outputs.result }}"}
53 |
--------------------------------------------------------------------------------
/.github/actions/install-latest-npm/action.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: 'Install Latest npm'
4 | description: 'Install the latest version of npm compatible with the Node version'
5 | inputs:
6 | node:
7 | description: 'Current Node version'
8 | required: true
9 | runs:
10 | using: "composite"
11 | steps:
12 | # node 10/12/14 ship with npm@6, which is known to fail when updating itself in windows
13 | - name: Update Windows npm
14 | if: |
15 | runner.os == 'Windows' && (
16 | startsWith(inputs.node, 'v10.') ||
17 | startsWith(inputs.node, 'v12.') ||
18 | startsWith(inputs.node, 'v14.')
19 | )
20 | shell: cmd
21 | run: |
22 | curl -sO https://registry.npmjs.org/npm/-/npm-7.5.4.tgz
23 | tar xf npm-7.5.4.tgz
24 | cd package
25 | node lib/npm.js install --no-fund --no-audit -g ..\npm-7.5.4.tgz
26 | cd ..
27 | rmdir /s /q package
28 | - name: Install Latest npm
29 | shell: bash
30 | env:
31 | NODE_VERSION: ${{ inputs.node }}
32 | working-directory: ${{ runner.temp }}
33 | run: |
34 | MATCH=""
35 | SPECS=("latest" "next-10" "next-9" "next-8" "next-7" "next-6")
36 |
37 | echo "node@$NODE_VERSION"
38 |
39 | for SPEC in ${SPECS[@]}; do
40 | ENGINES=$(npm view npm@$SPEC --json | jq -r '.engines.node')
41 | echo "Checking if node@$NODE_VERSION satisfies npm@$SPEC ($ENGINES)"
42 |
43 | if npx semver -r "$ENGINES" "$NODE_VERSION" > /dev/null; then
44 | MATCH=$SPEC
45 | echo "Found compatible version: npm@$MATCH"
46 | break
47 | fi
48 | done
49 |
50 | if [ -z $MATCH ]; then
51 | echo "Could not find a compatible version of npm for node@$NODE_VERSION"
52 | exit 1
53 | fi
54 |
55 | npm i --prefer-online --no-fund --no-audit -g npm@$MATCH
56 | - name: npm Version
57 | shell: bash
58 | run: npm -v
59 |
--------------------------------------------------------------------------------
/.github/dependabot.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | version: 2
4 |
5 | updates:
6 | - package-ecosystem: npm
7 | directory: /
8 | schedule:
9 | interval: daily
10 | target-branch: "main"
11 | allow:
12 | - dependency-type: direct
13 | versioning-strategy: increase-if-necessary
14 | commit-message:
15 | prefix: deps
16 | prefix-development: chore
17 | labels:
18 | - "Dependencies"
19 | open-pull-requests-limit: 10
20 |
--------------------------------------------------------------------------------
/.github/matchers/tap.json:
--------------------------------------------------------------------------------
1 | {
2 | "//@npmcli/template-oss": "This file is automatically added by @npmcli/template-oss. Do not edit.",
3 | "problemMatcher": [
4 | {
5 | "owner": "tap",
6 | "pattern": [
7 | {
8 | "regexp": "^\\s*not ok \\d+ - (.*)",
9 | "message": 1
10 | },
11 | {
12 | "regexp": "^\\s*---"
13 | },
14 | {
15 | "regexp": "^\\s*at:"
16 | },
17 | {
18 | "regexp": "^\\s*line:\\s*(\\d+)",
19 | "line": 1
20 | },
21 | {
22 | "regexp": "^\\s*column:\\s*(\\d+)",
23 | "column": 1
24 | },
25 | {
26 | "regexp": "^\\s*file:\\s*(.*)",
27 | "file": 1
28 | }
29 | ]
30 | }
31 | ]
32 | }
33 |
--------------------------------------------------------------------------------
/.github/settings.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | repository:
4 | allow_merge_commit: false
5 | allow_rebase_merge: true
6 | allow_squash_merge: true
7 | squash_merge_commit_title: PR_TITLE
8 | squash_merge_commit_message: PR_BODY
9 | delete_branch_on_merge: true
10 | enable_automated_security_fixes: true
11 | enable_vulnerability_alerts: true
12 |
13 | branches:
14 | - name: main
15 | protection:
16 | required_status_checks: null
17 | enforce_admins: true
18 | block_creations: true
19 | required_pull_request_reviews:
20 | required_approving_review_count: 1
21 | require_code_owner_reviews: true
22 | require_last_push_approval: true
23 | dismiss_stale_reviews: true
24 | restrictions:
25 | apps: []
26 | users: []
27 | teams: [ "cli-team" ]
28 |
--------------------------------------------------------------------------------
/.github/workflows/audit.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Audit
4 |
5 | on:
6 | workflow_dispatch:
7 | schedule:
8 | # "At 08:00 UTC (01:00 PT) on Monday" https://crontab.guru/#0_8_*_*_1
9 | - cron: "0 8 * * 1"
10 |
11 | jobs:
12 | audit:
13 | name: Audit Dependencies
14 | if: github.repository_owner == 'npm'
15 | runs-on: ubuntu-latest
16 | defaults:
17 | run:
18 | shell: bash
19 | steps:
20 | - name: Checkout
21 | uses: actions/checkout@v4
22 | - name: Setup Git User
23 | run: |
24 | git config --global user.email "npm-cli+bot@github.com"
25 | git config --global user.name "npm CLI robot"
26 | - name: Setup Node
27 | uses: actions/setup-node@v4
28 | id: node
29 | with:
30 | node-version: 22.x
31 | check-latest: contains('22.x', '.x')
32 | - name: Install Latest npm
33 | uses: ./.github/actions/install-latest-npm
34 | with:
35 | node: ${{ steps.node.outputs.node-version }}
36 | - name: Install Dependencies
37 | run: npm i --ignore-scripts --no-audit --no-fund --package-lock
38 | - name: Run Production Audit
39 | run: npm audit --omit=dev
40 | - name: Run Full Audit
41 | run: npm audit --audit-level=none
42 |
--------------------------------------------------------------------------------
/.github/workflows/ci-release.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: CI - Release
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | ref:
9 | required: true
10 | type: string
11 | default: main
12 | workflow_call:
13 | inputs:
14 | ref:
15 | required: true
16 | type: string
17 | check-sha:
18 | required: true
19 | type: string
20 |
21 | jobs:
22 | lint-all:
23 | name: Lint All
24 | if: github.repository_owner == 'npm'
25 | runs-on: ubuntu-latest
26 | defaults:
27 | run:
28 | shell: bash
29 | steps:
30 | - name: Checkout
31 | uses: actions/checkout@v4
32 | with:
33 | ref: ${{ inputs.ref }}
34 | - name: Setup Git User
35 | run: |
36 | git config --global user.email "npm-cli+bot@github.com"
37 | git config --global user.name "npm CLI robot"
38 | - name: Create Check
39 | id: create-check
40 | if: ${{ inputs.check-sha }}
41 | uses: ./.github/actions/create-check
42 | with:
43 | name: "Lint All"
44 | token: ${{ secrets.GITHUB_TOKEN }}
45 | sha: ${{ inputs.check-sha }}
46 | - name: Setup Node
47 | uses: actions/setup-node@v4
48 | id: node
49 | with:
50 | node-version: 22.x
51 | check-latest: contains('22.x', '.x')
52 | - name: Install Latest npm
53 | uses: ./.github/actions/install-latest-npm
54 | with:
55 | node: ${{ steps.node.outputs.node-version }}
56 | - name: Install Dependencies
57 | run: npm i --ignore-scripts --no-audit --no-fund
58 | - name: Lint
59 | run: npm run lint --ignore-scripts
60 | - name: Post Lint
61 | run: npm run postlint --ignore-scripts
62 | - name: Conclude Check
63 | uses: LouisBrunner/checks-action@v1.6.0
64 | if: steps.create-check.outputs.check-id && always()
65 | with:
66 | token: ${{ secrets.GITHUB_TOKEN }}
67 | conclusion: ${{ job.status }}
68 | check_id: ${{ steps.create-check.outputs.check-id }}
69 |
70 | test-all:
71 | name: Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }}
72 | if: github.repository_owner == 'npm'
73 | strategy:
74 | fail-fast: false
75 | matrix:
76 | platform:
77 | - name: Linux
78 | os: ubuntu-latest
79 | shell: bash
80 | - name: macOS
81 | os: macos-latest
82 | shell: bash
83 | - name: macOS
84 | os: macos-13
85 | shell: bash
86 | - name: Windows
87 | os: windows-latest
88 | shell: cmd
89 | node-version:
90 | - 18.17.0
91 | - 18.x
92 | - 20.5.0
93 | - 20.x
94 | - 22.x
95 | exclude:
96 | - platform: { name: macOS, os: macos-13, shell: bash }
97 | node-version: 18.17.0
98 | - platform: { name: macOS, os: macos-13, shell: bash }
99 | node-version: 18.x
100 | - platform: { name: macOS, os: macos-13, shell: bash }
101 | node-version: 20.5.0
102 | - platform: { name: macOS, os: macos-13, shell: bash }
103 | node-version: 20.x
104 | - platform: { name: macOS, os: macos-13, shell: bash }
105 | node-version: 22.x
106 | runs-on: ${{ matrix.platform.os }}
107 | defaults:
108 | run:
109 | shell: ${{ matrix.platform.shell }}
110 | steps:
111 | - name: Checkout
112 | uses: actions/checkout@v4
113 | with:
114 | ref: ${{ inputs.ref }}
115 | - name: Setup Git User
116 | run: |
117 | git config --global user.email "npm-cli+bot@github.com"
118 | git config --global user.name "npm CLI robot"
119 | - name: Create Check
120 | id: create-check
121 | if: ${{ inputs.check-sha }}
122 | uses: ./.github/actions/create-check
123 | with:
124 | name: "Test All - ${{ matrix.platform.name }} - ${{ matrix.node-version }}"
125 | token: ${{ secrets.GITHUB_TOKEN }}
126 | sha: ${{ inputs.check-sha }}
127 | - name: Setup Node
128 | uses: actions/setup-node@v4
129 | id: node
130 | with:
131 | node-version: ${{ matrix.node-version }}
132 | check-latest: contains(matrix.node-version, '.x')
133 | - name: Install Latest npm
134 | uses: ./.github/actions/install-latest-npm
135 | with:
136 | node: ${{ steps.node.outputs.node-version }}
137 | - name: Install Dependencies
138 | run: npm i --ignore-scripts --no-audit --no-fund
139 | - name: Add Problem Matcher
140 | run: echo "::add-matcher::.github/matchers/tap.json"
141 | - name: Test
142 | run: npm test --ignore-scripts
143 | - name: Conclude Check
144 | uses: LouisBrunner/checks-action@v1.6.0
145 | if: steps.create-check.outputs.check-id && always()
146 | with:
147 | token: ${{ secrets.GITHUB_TOKEN }}
148 | conclusion: ${{ job.status }}
149 | check_id: ${{ steps.create-check.outputs.check-id }}
150 |
--------------------------------------------------------------------------------
/.github/workflows/ci.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: CI
4 |
5 | on:
6 | workflow_dispatch:
7 | pull_request:
8 | push:
9 | branches:
10 | - main
11 | schedule:
12 | # "At 09:00 UTC (02:00 PT) on Monday" https://crontab.guru/#0_9_*_*_1
13 | - cron: "0 9 * * 1"
14 |
15 | jobs:
16 | lint:
17 | name: Lint
18 | if: github.repository_owner == 'npm'
19 | runs-on: ubuntu-latest
20 | defaults:
21 | run:
22 | shell: bash
23 | steps:
24 | - name: Checkout
25 | uses: actions/checkout@v4
26 | - name: Setup Git User
27 | run: |
28 | git config --global user.email "npm-cli+bot@github.com"
29 | git config --global user.name "npm CLI robot"
30 | - name: Setup Node
31 | uses: actions/setup-node@v4
32 | id: node
33 | with:
34 | node-version: 22.x
35 | check-latest: contains('22.x', '.x')
36 | - name: Install Latest npm
37 | uses: ./.github/actions/install-latest-npm
38 | with:
39 | node: ${{ steps.node.outputs.node-version }}
40 | - name: Install Dependencies
41 | run: npm i --ignore-scripts --no-audit --no-fund
42 | - name: Lint
43 | run: npm run lint --ignore-scripts
44 | - name: Post Lint
45 | run: npm run postlint --ignore-scripts
46 |
47 | test:
48 | name: Test - ${{ matrix.platform.name }} - ${{ matrix.node-version }}
49 | if: github.repository_owner == 'npm'
50 | strategy:
51 | fail-fast: false
52 | matrix:
53 | platform:
54 | - name: Linux
55 | os: ubuntu-latest
56 | shell: bash
57 | - name: macOS
58 | os: macos-latest
59 | shell: bash
60 | - name: macOS
61 | os: macos-13
62 | shell: bash
63 | - name: Windows
64 | os: windows-latest
65 | shell: cmd
66 | node-version:
67 | - 18.17.0
68 | - 18.x
69 | - 20.5.0
70 | - 20.x
71 | - 22.x
72 | exclude:
73 | - platform: { name: macOS, os: macos-13, shell: bash }
74 | node-version: 18.17.0
75 | - platform: { name: macOS, os: macos-13, shell: bash }
76 | node-version: 18.x
77 | - platform: { name: macOS, os: macos-13, shell: bash }
78 | node-version: 20.5.0
79 | - platform: { name: macOS, os: macos-13, shell: bash }
80 | node-version: 20.x
81 | - platform: { name: macOS, os: macos-13, shell: bash }
82 | node-version: 22.x
83 | runs-on: ${{ matrix.platform.os }}
84 | defaults:
85 | run:
86 | shell: ${{ matrix.platform.shell }}
87 | steps:
88 | - name: Checkout
89 | uses: actions/checkout@v4
90 | - name: Setup Git User
91 | run: |
92 | git config --global user.email "npm-cli+bot@github.com"
93 | git config --global user.name "npm CLI robot"
94 | - name: Setup Node
95 | uses: actions/setup-node@v4
96 | id: node
97 | with:
98 | node-version: ${{ matrix.node-version }}
99 | check-latest: contains(matrix.node-version, '.x')
100 | - name: Install Latest npm
101 | uses: ./.github/actions/install-latest-npm
102 | with:
103 | node: ${{ steps.node.outputs.node-version }}
104 | - name: Install Dependencies
105 | run: npm i --ignore-scripts --no-audit --no-fund
106 | - name: Add Problem Matcher
107 | run: echo "::add-matcher::.github/matchers/tap.json"
108 | - name: Test
109 | run: npm test --ignore-scripts
110 |
--------------------------------------------------------------------------------
/.github/workflows/codeql-analysis.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: CodeQL
4 |
5 | on:
6 | push:
7 | branches:
8 | - main
9 | pull_request:
10 | branches:
11 | - main
12 | schedule:
13 | # "At 10:00 UTC (03:00 PT) on Monday" https://crontab.guru/#0_10_*_*_1
14 | - cron: "0 10 * * 1"
15 |
16 | jobs:
17 | analyze:
18 | name: Analyze
19 | runs-on: ubuntu-latest
20 | permissions:
21 | actions: read
22 | contents: read
23 | security-events: write
24 | steps:
25 | - name: Checkout
26 | uses: actions/checkout@v4
27 | - name: Setup Git User
28 | run: |
29 | git config --global user.email "npm-cli+bot@github.com"
30 | git config --global user.name "npm CLI robot"
31 | - name: Initialize CodeQL
32 | uses: github/codeql-action/init@v3
33 | with:
34 | languages: javascript
35 | - name: Perform CodeQL Analysis
36 | uses: github/codeql-action/analyze@v3
37 |
--------------------------------------------------------------------------------
/.github/workflows/post-dependabot.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Post Dependabot
4 |
5 | on: pull_request
6 |
7 | permissions:
8 | contents: write
9 |
10 | jobs:
11 | template-oss:
12 | name: template-oss
13 | if: github.repository_owner == 'npm' && github.actor == 'dependabot[bot]'
14 | runs-on: ubuntu-latest
15 | defaults:
16 | run:
17 | shell: bash
18 | steps:
19 | - name: Checkout
20 | uses: actions/checkout@v4
21 | with:
22 | ref: ${{ github.event.pull_request.head.ref }}
23 | - name: Setup Git User
24 | run: |
25 | git config --global user.email "npm-cli+bot@github.com"
26 | git config --global user.name "npm CLI robot"
27 | - name: Setup Node
28 | uses: actions/setup-node@v4
29 | id: node
30 | with:
31 | node-version: 22.x
32 | check-latest: contains('22.x', '.x')
33 | - name: Install Latest npm
34 | uses: ./.github/actions/install-latest-npm
35 | with:
36 | node: ${{ steps.node.outputs.node-version }}
37 | - name: Install Dependencies
38 | run: npm i --ignore-scripts --no-audit --no-fund
39 | - name: Fetch Dependabot Metadata
40 | id: metadata
41 | uses: dependabot/fetch-metadata@v1
42 | with:
43 | github-token: ${{ secrets.GITHUB_TOKEN }}
44 |
45 | # Dependabot can update multiple directories so we output which directory
46 | # it is acting on so we can run the command for the correct root or workspace
47 | - name: Get Dependabot Directory
48 | if: contains(steps.metadata.outputs.dependency-names, '@npmcli/template-oss')
49 | id: flags
50 | run: |
51 | dependabot_dir="${{ steps.metadata.outputs.directory }}"
52 | if [[ "$dependabot_dir" == "/" || "$dependabot_dir" == "/main" ]]; then
53 | echo "workspace=-iwr" >> $GITHUB_OUTPUT
54 | else
55 | # strip leading slash from directory so it works as a
56 | # a path to the workspace flag
57 | echo "workspace=-w ${dependabot_dir#/}" >> $GITHUB_OUTPUT
58 | fi
59 |
60 | - name: Apply Changes
61 | if: steps.flags.outputs.workspace
62 | id: apply
63 | run: |
64 | npm run template-oss-apply ${{ steps.flags.outputs.workspace }}
65 | if [[ `git status --porcelain` ]]; then
66 | echo "changes=true" >> $GITHUB_OUTPUT
67 | fi
68 | # This only sets the conventional commit prefix. This workflow can't reliably determine
69 | # what the breaking change is though. If a BREAKING CHANGE message is required then
70 | # this PR check will fail and the commit will be amended with stafftools
71 | if [[ "${{ steps.metadata.outputs.update-type }}" == "version-update:semver-major" ]]; then
72 | prefix='feat!'
73 | else
74 | prefix='chore'
75 | fi
76 | echo "message=$prefix: postinstall for dependabot template-oss PR" >> $GITHUB_OUTPUT
77 |
78 | # This step will fail if template-oss has made any workflow updates. It is impossible
79 | # for a workflow to update other workflows. In the case it does fail, we continue
80 | # and then try to apply only a portion of the changes in the next step
81 | - name: Push All Changes
82 | if: steps.apply.outputs.changes
83 | id: push
84 | continue-on-error: true
85 | env:
86 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
87 | run: |
88 | git commit -am "${{ steps.apply.outputs.message }}"
89 | git push
90 |
91 | # If the previous step failed, then reset the commit and remove any workflow changes
92 | # and attempt to commit and push again. This is helpful because we will have a commit
93 | # with the correct prefix that we can then --amend with @npmcli/stafftools later.
94 | - name: Push All Changes Except Workflows
95 | if: steps.apply.outputs.changes && steps.push.outcome == 'failure'
96 | env:
97 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
98 | run: |
99 | git reset HEAD~
100 | git checkout HEAD -- .github/workflows/
101 | git clean -fd .github/workflows/
102 | git commit -am "${{ steps.apply.outputs.message }}"
103 | git push
104 |
105 | # Check if all the necessary template-oss changes were applied. Since we continued
106 | # on errors in one of the previous steps, this check will fail if our follow up
107 | # only applied a portion of the changes and we need to followup manually.
108 | #
109 | # Note that this used to run `lint` and `postlint` but that will fail this action
110 | # if we've also shipped any linting changes separate from template-oss. We do
111 | # linting in another action, so we want to fail this one only if there are
112 | # template-oss changes that could not be applied.
113 | - name: Check Changes
114 | if: steps.apply.outputs.changes
115 | run: |
116 | npm exec --offline ${{ steps.flags.outputs.workspace }} -- template-oss-check
117 |
118 | - name: Fail on Breaking Change
119 | if: steps.apply.outputs.changes && startsWith(steps.apply.outputs.message, 'feat!')
120 | run: |
121 | echo "This PR has a breaking change. Run 'npx -p @npmcli/stafftools gh template-oss-fix'"
122 | echo "for more information on how to fix this with a BREAKING CHANGE footer."
123 | exit 1
124 |
--------------------------------------------------------------------------------
/.github/workflows/pull-request.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Pull Request
4 |
5 | on:
6 | pull_request:
7 | types:
8 | - opened
9 | - reopened
10 | - edited
11 | - synchronize
12 |
13 | jobs:
14 | commitlint:
15 | name: Lint Commits
16 | if: github.repository_owner == 'npm'
17 | runs-on: ubuntu-latest
18 | defaults:
19 | run:
20 | shell: bash
21 | steps:
22 | - name: Checkout
23 | uses: actions/checkout@v4
24 | with:
25 | fetch-depth: 0
26 | - name: Setup Git User
27 | run: |
28 | git config --global user.email "npm-cli+bot@github.com"
29 | git config --global user.name "npm CLI robot"
30 | - name: Setup Node
31 | uses: actions/setup-node@v4
32 | id: node
33 | with:
34 | node-version: 22.x
35 | check-latest: contains('22.x', '.x')
36 | - name: Install Latest npm
37 | uses: ./.github/actions/install-latest-npm
38 | with:
39 | node: ${{ steps.node.outputs.node-version }}
40 | - name: Install Dependencies
41 | run: npm i --ignore-scripts --no-audit --no-fund
42 | - name: Run Commitlint on Commits
43 | id: commit
44 | continue-on-error: true
45 | run: npx --offline commitlint -V --from 'origin/${{ github.base_ref }}' --to ${{ github.event.pull_request.head.sha }}
46 | - name: Run Commitlint on PR Title
47 | if: steps.commit.outcome == 'failure'
48 | env:
49 | PR_TITLE: ${{ github.event.pull_request.title }}
50 | run: echo "$PR_TITLE" | npx --offline commitlint -V
51 |
--------------------------------------------------------------------------------
/.github/workflows/release-integration.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Release Integration
4 |
5 | on:
6 | workflow_dispatch:
7 | inputs:
8 | releases:
9 | required: true
10 | type: string
11 | description: 'A json array of releases. Required fields: publish: tagName, publishTag. publish check: pkgName, version'
12 | workflow_call:
13 | inputs:
14 | releases:
15 | required: true
16 | type: string
17 | description: 'A json array of releases. Required fields: publish: tagName, publishTag. publish check: pkgName, version'
18 | secrets:
19 | PUBLISH_TOKEN:
20 | required: true
21 |
22 | jobs:
23 | publish:
24 | name: Publish
25 | runs-on: ubuntu-latest
26 | defaults:
27 | run:
28 | shell: bash
29 | permissions:
30 | id-token: write
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v4
34 | with:
35 | ref: ${{ fromJSON(inputs.releases)[0].tagName }}
36 | - name: Setup Git User
37 | run: |
38 | git config --global user.email "npm-cli+bot@github.com"
39 | git config --global user.name "npm CLI robot"
40 | - name: Setup Node
41 | uses: actions/setup-node@v4
42 | id: node
43 | with:
44 | node-version: 22.x
45 | check-latest: contains('22.x', '.x')
46 | - name: Install Latest npm
47 | uses: ./.github/actions/install-latest-npm
48 | with:
49 | node: ${{ steps.node.outputs.node-version }}
50 | - name: Install Dependencies
51 | run: npm i --ignore-scripts --no-audit --no-fund
52 | - name: Set npm authToken
53 | run: npm config set '//registry.npmjs.org/:_authToken'=\${PUBLISH_TOKEN}
54 | - name: Publish
55 | env:
56 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
57 | RELEASES: ${{ inputs.releases }}
58 | run: |
59 | EXIT_CODE=0
60 |
61 | for release in $(echo $RELEASES | jq -r '.[] | @base64'); do
62 | PUBLISH_TAG=$(echo "$release" | base64 --decode | jq -r .publishTag)
63 | npm publish --provenance --tag="$PUBLISH_TAG"
64 | STATUS=$?
65 | if [[ "$STATUS" -eq 1 ]]; then
66 | EXIT_CODE=$STATUS
67 | fi
68 | done
69 |
70 | exit $EXIT_CODE
71 |
--------------------------------------------------------------------------------
/.github/workflows/release.yml:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | name: Release
4 |
5 | on:
6 | push:
7 | branches:
8 | - main
9 |
10 | permissions:
11 | contents: write
12 | pull-requests: write
13 | checks: write
14 |
15 | jobs:
16 | release:
17 | outputs:
18 | pr: ${{ steps.release.outputs.pr }}
19 | pr-branch: ${{ steps.release.outputs.pr-branch }}
20 | pr-number: ${{ steps.release.outputs.pr-number }}
21 | pr-sha: ${{ steps.release.outputs.pr-sha }}
22 | releases: ${{ steps.release.outputs.releases }}
23 | comment-id: ${{ steps.create-comment.outputs.comment-id || steps.update-comment.outputs.comment-id }}
24 | check-id: ${{ steps.create-check.outputs.check-id }}
25 | name: Release
26 | if: github.repository_owner == 'npm'
27 | runs-on: ubuntu-latest
28 | defaults:
29 | run:
30 | shell: bash
31 | steps:
32 | - name: Checkout
33 | uses: actions/checkout@v4
34 | - name: Setup Git User
35 | run: |
36 | git config --global user.email "npm-cli+bot@github.com"
37 | git config --global user.name "npm CLI robot"
38 | - name: Setup Node
39 | uses: actions/setup-node@v4
40 | id: node
41 | with:
42 | node-version: 22.x
43 | check-latest: contains('22.x', '.x')
44 | - name: Install Latest npm
45 | uses: ./.github/actions/install-latest-npm
46 | with:
47 | node: ${{ steps.node.outputs.node-version }}
48 | - name: Install Dependencies
49 | run: npm i --ignore-scripts --no-audit --no-fund
50 | - name: Release Please
51 | id: release
52 | env:
53 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
54 | run: npx --offline template-oss-release-please --branch="${{ github.ref_name }}" --backport="" --defaultTag="latest"
55 | - name: Create Release Manager Comment Text
56 | if: steps.release.outputs.pr-number
57 | uses: actions/github-script@v7
58 | id: comment-text
59 | with:
60 | result-encoding: string
61 | script: |
62 | const { runId, repo: { owner, repo } } = context
63 | const { data: workflow } = await github.rest.actions.getWorkflowRun({ owner, repo, run_id: runId })
64 | return['## Release Manager', `Release workflow run: ${workflow.html_url}`].join('\n\n')
65 | - name: Find Release Manager Comment
66 | uses: peter-evans/find-comment@v2
67 | if: steps.release.outputs.pr-number
68 | id: found-comment
69 | with:
70 | issue-number: ${{ steps.release.outputs.pr-number }}
71 | comment-author: 'github-actions[bot]'
72 | body-includes: '## Release Manager'
73 | - name: Create Release Manager Comment
74 | id: create-comment
75 | if: steps.release.outputs.pr-number && !steps.found-comment.outputs.comment-id
76 | uses: peter-evans/create-or-update-comment@v3
77 | with:
78 | issue-number: ${{ steps.release.outputs.pr-number }}
79 | body: ${{ steps.comment-text.outputs.result }}
80 | - name: Update Release Manager Comment
81 | id: update-comment
82 | if: steps.release.outputs.pr-number && steps.found-comment.outputs.comment-id
83 | uses: peter-evans/create-or-update-comment@v3
84 | with:
85 | comment-id: ${{ steps.found-comment.outputs.comment-id }}
86 | body: ${{ steps.comment-text.outputs.result }}
87 | edit-mode: 'replace'
88 | - name: Create Check
89 | id: create-check
90 | uses: ./.github/actions/create-check
91 | if: steps.release.outputs.pr-sha
92 | with:
93 | name: "Release"
94 | token: ${{ secrets.GITHUB_TOKEN }}
95 | sha: ${{ steps.release.outputs.pr-sha }}
96 |
97 | update:
98 | needs: release
99 | outputs:
100 | sha: ${{ steps.commit.outputs.sha }}
101 | check-id: ${{ steps.create-check.outputs.check-id }}
102 | name: Update - Release
103 | if: github.repository_owner == 'npm' && needs.release.outputs.pr
104 | runs-on: ubuntu-latest
105 | defaults:
106 | run:
107 | shell: bash
108 | steps:
109 | - name: Checkout
110 | uses: actions/checkout@v4
111 | with:
112 | fetch-depth: 0
113 | ref: ${{ needs.release.outputs.pr-branch }}
114 | - name: Setup Git User
115 | run: |
116 | git config --global user.email "npm-cli+bot@github.com"
117 | git config --global user.name "npm CLI robot"
118 | - name: Setup Node
119 | uses: actions/setup-node@v4
120 | id: node
121 | with:
122 | node-version: 22.x
123 | check-latest: contains('22.x', '.x')
124 | - name: Install Latest npm
125 | uses: ./.github/actions/install-latest-npm
126 | with:
127 | node: ${{ steps.node.outputs.node-version }}
128 | - name: Install Dependencies
129 | run: npm i --ignore-scripts --no-audit --no-fund
130 | - name: Create Release Manager Checklist Text
131 | id: comment-text
132 | env:
133 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
134 | run: npm exec --offline -- template-oss-release-manager --pr="${{ needs.release.outputs.pr-number }}" --backport="" --defaultTag="latest" --publish
135 | - name: Append Release Manager Comment
136 | uses: peter-evans/create-or-update-comment@v3
137 | with:
138 | comment-id: ${{ needs.release.outputs.comment-id }}
139 | body: ${{ steps.comment-text.outputs.result }}
140 | edit-mode: 'append'
141 | - name: Run Post Pull Request Actions
142 | env:
143 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
144 | run: npm run rp-pull-request --ignore-scripts --if-present -- --pr="${{ needs.release.outputs.pr-number }}" --commentId="${{ needs.release.outputs.comment-id }}"
145 | - name: Commit
146 | id: commit
147 | env:
148 | GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
149 | run: |
150 | git commit --all --amend --no-edit || true
151 | git push --force-with-lease
152 | echo "sha=$(git rev-parse HEAD)" >> $GITHUB_OUTPUT
153 | - name: Create Check
154 | id: create-check
155 | uses: ./.github/actions/create-check
156 | with:
157 | name: "Update - Release"
158 | check-name: "Release"
159 | token: ${{ secrets.GITHUB_TOKEN }}
160 | sha: ${{ steps.commit.outputs.sha }}
161 | - name: Conclude Check
162 | uses: LouisBrunner/checks-action@v1.6.0
163 | with:
164 | token: ${{ secrets.GITHUB_TOKEN }}
165 | conclusion: ${{ job.status }}
166 | check_id: ${{ needs.release.outputs.check-id }}
167 |
168 | ci:
169 | name: CI - Release
170 | needs: [ release, update ]
171 | if: needs.release.outputs.pr
172 | uses: ./.github/workflows/ci-release.yml
173 | with:
174 | ref: ${{ needs.release.outputs.pr-branch }}
175 | check-sha: ${{ needs.update.outputs.sha }}
176 |
177 | post-ci:
178 | needs: [ release, update, ci ]
179 | name: Post CI - Release
180 | if: github.repository_owner == 'npm' && needs.release.outputs.pr && always()
181 | runs-on: ubuntu-latest
182 | defaults:
183 | run:
184 | shell: bash
185 | steps:
186 | - name: Get CI Conclusion
187 | id: conclusion
188 | run: |
189 | result=""
190 | if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
191 | result="failure"
192 | elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
193 | result="cancelled"
194 | else
195 | result="success"
196 | fi
197 | echo "result=$result" >> $GITHUB_OUTPUT
198 | - name: Conclude Check
199 | uses: LouisBrunner/checks-action@v1.6.0
200 | with:
201 | token: ${{ secrets.GITHUB_TOKEN }}
202 | conclusion: ${{ steps.conclusion.outputs.result }}
203 | check_id: ${{ needs.update.outputs.check-id }}
204 |
205 | post-release:
206 | needs: release
207 | outputs:
208 | comment-id: ${{ steps.create-comment.outputs.comment-id }}
209 | name: Post Release - Release
210 | if: github.repository_owner == 'npm' && needs.release.outputs.releases
211 | runs-on: ubuntu-latest
212 | defaults:
213 | run:
214 | shell: bash
215 | steps:
216 | - name: Create Release PR Comment Text
217 | id: comment-text
218 | uses: actions/github-script@v7
219 | env:
220 | RELEASES: ${{ needs.release.outputs.releases }}
221 | with:
222 | result-encoding: string
223 | script: |
224 | const releases = JSON.parse(process.env.RELEASES)
225 | const { runId, repo: { owner, repo } } = context
226 | const issue_number = releases[0].prNumber
227 | const runUrl = `https://github.com/${owner}/${repo}/actions/runs/${runId}`
228 |
229 | return [
230 | '## Release Workflow\n',
231 | ...releases.map(r => `- \`${r.pkgName}@${r.version}\` ${r.url}`),
232 | `- Workflow run: :arrows_counterclockwise: ${runUrl}`,
233 | ].join('\n')
234 | - name: Create Release PR Comment
235 | id: create-comment
236 | uses: peter-evans/create-or-update-comment@v3
237 | with:
238 | issue-number: ${{ fromJSON(needs.release.outputs.releases)[0].prNumber }}
239 | body: ${{ steps.comment-text.outputs.result }}
240 |
241 | release-integration:
242 | needs: release
243 | name: Release Integration
244 | if: needs.release.outputs.releases
245 | uses: ./.github/workflows/release-integration.yml
246 | permissions:
247 | id-token: write
248 | secrets:
249 | PUBLISH_TOKEN: ${{ secrets.PUBLISH_TOKEN }}
250 | with:
251 | releases: ${{ needs.release.outputs.releases }}
252 |
253 | post-release-integration:
254 | needs: [ release, release-integration, post-release ]
255 | name: Post Release Integration - Release
256 | if: github.repository_owner == 'npm' && needs.release.outputs.releases && always()
257 | runs-on: ubuntu-latest
258 | defaults:
259 | run:
260 | shell: bash
261 | steps:
262 | - name: Get Post Release Conclusion
263 | id: conclusion
264 | run: |
265 | if [[ "${{ contains(needs.*.result, 'failure') }}" == "true" ]]; then
266 | result="x"
267 | elif [[ "${{ contains(needs.*.result, 'cancelled') }}" == "true" ]]; then
268 | result="heavy_multiplication_x"
269 | else
270 | result="white_check_mark"
271 | fi
272 | echo "result=$result" >> $GITHUB_OUTPUT
273 | - name: Find Release PR Comment
274 | uses: peter-evans/find-comment@v2
275 | id: found-comment
276 | with:
277 | issue-number: ${{ fromJSON(needs.release.outputs.releases)[0].prNumber }}
278 | comment-author: 'github-actions[bot]'
279 | body-includes: '## Release Workflow'
280 | - name: Create Release PR Comment Text
281 | id: comment-text
282 | if: steps.found-comment.outputs.comment-id
283 | uses: actions/github-script@v7
284 | env:
285 | RESULT: ${{ steps.conclusion.outputs.result }}
286 | BODY: ${{ steps.found-comment.outputs.comment-body }}
287 | with:
288 | result-encoding: string
289 | script: |
290 | const { RESULT, BODY } = process.env
291 | const body = [BODY.replace(/(Workflow run: :)[a-z_]+(:)/, `$1${RESULT}$2`)]
292 | if (RESULT !== 'white_check_mark') {
293 | body.push(':rotating_light::rotating_light::rotating_light:')
294 | body.push([
295 | '@npm/cli-team: The post-release workflow failed for this release.',
296 | 'Manual steps may need to be taken after examining the workflow output.'
297 | ].join(' '))
298 | body.push(':rotating_light::rotating_light::rotating_light:')
299 | }
300 | return body.join('\n\n').trim()
301 | - name: Update Release PR Comment
302 | if: steps.comment-text.outputs.result
303 | uses: peter-evans/create-or-update-comment@v3
304 | with:
305 | comment-id: ${{ steps.found-comment.outputs.comment-id }}
306 | body: ${{ steps.comment-text.outputs.result }}
307 | edit-mode: 'replace'
308 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | # ignore everything in the root
4 | /*
5 |
6 | !**/.gitignore
7 | !/.commitlintrc.js
8 | !/.eslint.config.js
9 | !/.eslintrc.js
10 | !/.eslintrc.local.*
11 | !/.git-blame-ignore-revs
12 | !/.github/
13 | !/.gitignore
14 | !/.npmrc
15 | !/.prettierignore
16 | !/.prettierrc.js
17 | !/.release-please-manifest.json
18 | !/bin/
19 | !/CHANGELOG*
20 | !/CODE_OF_CONDUCT.md
21 | !/CONTRIBUTING.md
22 | !/docs/
23 | !/lib/
24 | !/LICENSE*
25 | !/map.js
26 | !/package.json
27 | !/README*
28 | !/release-please-config.json
29 | !/scripts/
30 | !/SECURITY.md
31 | !/tap-snapshots/
32 | !/test/
33 | !/tsconfig.json
34 | tap-testdir*/
35 |
--------------------------------------------------------------------------------
/.npmrc:
--------------------------------------------------------------------------------
1 | ; This file is automatically added by @npmcli/template-oss. Do not edit.
2 |
3 | package-lock=false
4 |
--------------------------------------------------------------------------------
/.release-please-manifest.json:
--------------------------------------------------------------------------------
1 | {
2 | ".": "7.1.1"
3 | }
4 |
--------------------------------------------------------------------------------
/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Changelog
2 |
3 | ## [7.1.1](https://github.com/npm/npm-install-checks/compare/v7.1.0...v7.1.1) (2024-11-21)
4 | ### Bug Fixes
5 | * [`acf64a7`](https://github.com/npm/npm-install-checks/commit/acf64a7cffae5bd568f63f4c1b24f7852b62c26e) [#120](https://github.com/npm/npm-install-checks/pull/120) cache local environment values (#120) (@Tofandel)
6 | ### Chores
7 | * [`fca8f29`](https://github.com/npm/npm-install-checks/commit/fca8f29147879b37fa35f6468fecec25e693428b) [#119](https://github.com/npm/npm-install-checks/pull/119) bump @npmcli/template-oss from 4.23.3 to 4.23.4 (#119) (@dependabot[bot], @npm-cli-bot)
8 |
9 | ## [7.1.0](https://github.com/npm/npm-install-checks/compare/v7.0.0...v7.1.0) (2024-09-11)
10 | ### Features
11 | * [`ebf9b9f`](https://github.com/npm/npm-install-checks/commit/ebf9b9f4c08035d8e2c41cd9bc3302cd8bdc9184) [#116](https://github.com/npm/npm-install-checks/pull/116) adds checkDevEngines (#116) (@reggi)
12 | ### Bug Fixes
13 | * [`ec4066b`](https://github.com/npm/npm-install-checks/commit/ec4066b768075cc84270d9ee7c0a76b011f1555a) [#118](https://github.com/npm/npm-install-checks/pull/118) skip network requests in report.getReport() (#118) (@wraithgar)
14 |
15 | ## [7.0.0](https://github.com/npm/npm-install-checks/compare/v6.3.0...v7.0.0) (2024-09-03)
16 | ### ⚠️ BREAKING CHANGES
17 | * `npm-install-checks` now supports node `^18.17.0 || >=20.5.0`
18 | ### Bug Fixes
19 | * [`0ac00b5`](https://github.com/npm/npm-install-checks/commit/0ac00b53862de606e7163d8ca2b8d4dda8476a89) [#114](https://github.com/npm/npm-install-checks/pull/114) align to npm 10 node engine range (@hashtagchris)
20 | ### Chores
21 | * [`771bc19`](https://github.com/npm/npm-install-checks/commit/771bc19142bc83639810a157e9dfe9ca83caab77) [#114](https://github.com/npm/npm-install-checks/pull/114) run template-oss-apply (@hashtagchris)
22 | * [`d7cf1dc`](https://github.com/npm/npm-install-checks/commit/d7cf1dcf963aec10a75a8af31894038bd8afba24) [#112](https://github.com/npm/npm-install-checks/pull/112) bump @npmcli/eslint-config from 4.0.5 to 5.0.0 (@dependabot[bot])
23 | * [`98057a0`](https://github.com/npm/npm-install-checks/commit/98057a03bbb985741a512e9161690ea22a1c62e5) [#98](https://github.com/npm/npm-install-checks/pull/98) linting: no-unused-vars (@lukekarrys)
24 | * [`f1670ca`](https://github.com/npm/npm-install-checks/commit/f1670ca26af7823224c8bed36eb321a98206a5d5) [#98](https://github.com/npm/npm-install-checks/pull/98) bump @npmcli/template-oss to 4.22.0 (@lukekarrys)
25 | * [`06fdf5e`](https://github.com/npm/npm-install-checks/commit/06fdf5e89cfabe38af510bb6f929f1756826e706) [#113](https://github.com/npm/npm-install-checks/pull/113) postinstall for dependabot template-oss PR (@hashtagchris)
26 | * [`8b7cd81`](https://github.com/npm/npm-install-checks/commit/8b7cd81b9ad0168a81d294aad501e5c203626782) [#113](https://github.com/npm/npm-install-checks/pull/113) bump @npmcli/template-oss from 4.23.1 to 4.23.3 (@dependabot[bot])
27 |
28 | ## [6.3.0](https://github.com/npm/npm-install-checks/compare/v6.2.0...v6.3.0) (2023-10-06)
29 |
30 | ### Features
31 |
32 | * [`0419751`](https://github.com/npm/npm-install-checks/commit/04197512179c508abb55fa528d293ee669c19b91) [#71](https://github.com/npm/npm-install-checks/pull/71) allow checkPlatform to override execution libc (#71) (@Brooooooklyn)
33 |
34 | ## [6.2.0](https://github.com/npm/npm-install-checks/compare/v6.1.1...v6.2.0) (2023-08-07)
35 |
36 | ### Features
37 |
38 | * [`7a3f5ed`](https://github.com/npm/npm-install-checks/commit/7a3f5ed9ea21d99915e5d30f9d4eba01ac8af319) [#65](https://github.com/npm/npm-install-checks/pull/65) allow checkPlatform to override execution environment (#65) (@yukukotani)
39 |
40 | ## [6.1.1](https://github.com/npm/npm-install-checks/compare/v6.1.0...v6.1.1) (2023-04-12)
41 |
42 | ### Bug Fixes
43 |
44 | * [`7a93622`](https://github.com/npm/npm-install-checks/commit/7a936221e4bd9db38b5be2746b514cceff3574f6) [#57](https://github.com/npm/npm-install-checks/pull/57) typo glibcRuntimeVersion glibcVersionRuntime (#57) (@snyamathi)
45 |
46 | ## [6.1.0](https://github.com/npm/npm-install-checks/compare/v6.0.0...v6.1.0) (2023-03-21)
47 |
48 | ### Features
49 |
50 | * [`1b6f3e4`](https://github.com/npm/npm-install-checks/commit/1b6f3e48e2fa7dda70850a16726cd58be826baf7) [#54](https://github.com/npm/npm-install-checks/pull/54) support libc field checks (#54) (@nlf)
51 |
52 | ## [6.0.0](https://github.com/npm/npm-install-checks/compare/v5.0.0...v6.0.0) (2022-10-10)
53 |
54 | ### ⚠️ BREAKING CHANGES
55 |
56 | * `npm-install-checks` is now compatible with the following semver range for node: `^14.17.0 || ^16.13.0 || >=18.0.0`
57 |
58 | ### Features
59 |
60 | * [`f7c1276`](https://github.com/npm/npm-install-checks/commit/f7c12765c0d2c4066af38819ada408ef71ed9bd4) [#38](https://github.com/npm/npm-install-checks/pull/38) postinstall for dependabot template-oss PR (@lukekarrys)
61 |
62 | ## [5.0.0](https://github.com/npm/npm-install-checks/compare/v4.0.0...v5.0.0) (2022-04-05)
63 |
64 |
65 | ### ⚠ BREAKING CHANGES
66 |
67 | * this drops support for node 10 and non-LTS versions of node 12 and node 14
68 |
69 | ### Dependencies
70 |
71 | * @npmcli/template-oss@3.2.2 ([45e7fd5](https://github.com/npm/npm-install-checks/commit/45e7fd5dee0c5137825c75acbc62eacc7d0c0d08))
72 |
73 | ## v4.0
74 |
75 | * Remove `checkCycle` and `checkGit`, as they are no longer used in npm v7
76 | * Synchronous throw-or-return API instead of taking a callback needlessly
77 | * Modernize code and drop support for node versions less than 10
78 |
79 | ## v3 2016-01-12
80 |
81 | * Change error messages to be more informative.
82 | * checkEngine, when not in strict mode, now calls back with the error
83 | object as the second argument instead of warning.
84 | * checkCycle no longer logs when cycle errors are found.
85 |
86 | ## v2 2015-01-20
87 |
88 | * Remove checking of engineStrict in the package.json
89 |
--------------------------------------------------------------------------------
/CODE_OF_CONDUCT.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | All interactions in this repo are covered by the [npm Code of
4 | Conduct](https://docs.npmjs.com/policies/conduct)
5 |
6 | The npm cli team may, at its own discretion, moderate, remove, or edit
7 | any interactions such as pull requests, issues, and comments.
8 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | # Contributing
4 |
5 | ## Code of Conduct
6 |
7 | All interactions in the **npm** organization on GitHub are considered to be covered by our standard [Code of Conduct](https://docs.npmjs.com/policies/conduct).
8 |
9 | ## Reporting Bugs
10 |
11 | Before submitting a new bug report please search for an existing or similar report.
12 |
13 | Use one of our existing issue templates if you believe you've come across a unique problem.
14 |
15 | Duplicate issues, or issues that don't use one of our templates may get closed without a response.
16 |
17 | ## Pull Request Conventions
18 |
19 | ### Commits
20 |
21 | We use [Conventional Commits](https://www.conventionalcommits.org/en/v1.0.0/).
22 |
23 | When opening a pull request please be sure that either the pull request title, or each commit in the pull request, has one of the following prefixes:
24 |
25 | - `feat`: For when introducing a new feature. The result will be a new semver minor version of the package when it is next published.
26 | - `fix`: For bug fixes. The result will be a new semver patch version of the package when it is next published.
27 | - `docs`: For documentation updates. The result will be a new semver patch version of the package when it is next published.
28 | - `chore`: For changes that do not affect the published module. Often these are changes to tests. The result will be *no* change to the version of the package when it is next published (as the commit does not affect the published version).
29 |
30 | ### Test Coverage
31 |
32 | Pull requests made against this repo will run `npm test` automatically. Please make sure tests pass locally before submitting a PR.
33 |
34 | Every new feature or bug fix should come with a corresponding test or tests that validate the solutions. Testing also reports on code coverage and will fail if code coverage drops.
35 |
36 | ### Linting
37 |
38 | Linting is also done automatically once tests pass. `npm run lintfix` will fix most linting errors automatically.
39 |
40 | Please make sure linting passes before submitting a PR.
41 |
42 | ## What _not_ to contribute?
43 |
44 | ### Dependencies
45 |
46 | It should be noted that our team does not accept third-party dependency updates/PRs. If you submit a PR trying to update our dependencies we will close it with or without a reference to these contribution guidelines.
47 |
48 | ### Tools/Automation
49 |
50 | Our core team is responsible for the maintenance of the tooling/automation in this project and we ask contributors to not make changes to these when contributing (e.g. `.github/*`, `.eslintrc.json`, `.licensee.json`). Most of those files also have a header at the top to remind folks they are automatically generated. Pull requests that alter these will not be accepted.
51 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | Copyright (c) Robert Kowalski and Isaac Z. Schlueter ("Authors")
2 | All rights reserved.
3 |
4 | The BSD License
5 |
6 | Redistribution and use in source and binary forms, with or without
7 | modification, are permitted provided that the following conditions
8 | are met:
9 |
10 | 1. Redistributions of source code must retain the above copyright
11 | notice, this list of conditions and the following disclaimer.
12 |
13 | 2. Redistributions in binary form must reproduce the above copyright
14 | notice, this list of conditions and the following disclaimer in the
15 | documentation and/or other materials provided with the distribution.
16 |
17 | THIS SOFTWARE IS PROVIDED BY THE AUTHORS AND CONTRIBUTORS ``AS IS'' AND
18 | ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
19 | IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
20 | PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHORS OR CONTRIBUTORS
21 | BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
22 | CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
23 | SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
24 | BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
25 | WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
26 | OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
27 | IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
28 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # npm-install-checks
2 |
3 | Check the engines and platform fields in package.json
4 |
5 | ## API
6 |
7 | Both functions will throw an error if the check fails, or return
8 | `undefined` if everything is ok.
9 |
10 | Errors have a `required` and `current` fields.
11 |
12 | ### .checkEngine(pkg, npmVer, nodeVer, force = false)
13 |
14 | Check if a package's `engines.node` and `engines.npm` match the running system.
15 |
16 | `force` argument will override the node version check, but not the npm
17 | version check, as this typically would indicate that the current version of
18 | npm is unable to install the package properly for some reason.
19 |
20 | Error code: 'EBADENGINE'
21 |
22 | ### .checkPlatform(pkg, force, environment)
23 |
24 | Check if a package's `os`, `cpu` and `libc` match the running system.
25 |
26 | `force` argument skips all checks.
27 |
28 | `environment` overrides the execution environment which comes from `process.platform` `process.arch` and current `libc` environment by default. `environment.os` `environment.cpu` and `environment.libc` are available.
29 |
30 | Error code: 'EBADPLATFORM'
31 |
32 |
33 | ### .checkDevEngines(wanted, current, opts)
34 |
35 | Check if a package's `devEngines` property matches the current system environment.
36 |
37 | Returns an array of `Error` objects, some of which may be warnings, this can be checked with `.isError` and `.isWarn`. Errors correspond to an error for a given "engine" failure, reasons for each engine "dependency" failure can be found within `.errors`.
--------------------------------------------------------------------------------
/SECURITY.md:
--------------------------------------------------------------------------------
1 |
2 |
3 | GitHub takes the security of our software products and services seriously, including the open source code repositories managed through our GitHub organizations, such as [GitHub](https://github.com/GitHub).
4 |
5 | If you believe you have found a security vulnerability in this GitHub-owned open source repository, you can report it to us in one of two ways.
6 |
7 | If the vulnerability you have found is *not* [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) or if you do not wish to be considered for a bounty reward, please report the issue to us directly through [opensource-security@github.com](mailto:opensource-security@github.com).
8 |
9 | If the vulnerability you have found is [in scope for the GitHub Bug Bounty Program](https://bounty.github.com/#scope) and you would like for your finding to be considered for a bounty reward, please submit the vulnerability to us through [HackerOne](https://hackerone.com/github) in order to be eligible to receive a bounty award.
10 |
11 | **Please do not report security vulnerabilities through public GitHub issues, discussions, or pull requests.**
12 |
13 | Thanks for helping make GitHub safe for everyone.
14 |
--------------------------------------------------------------------------------
/lib/current-env.js:
--------------------------------------------------------------------------------
1 | const process = require('node:process')
2 | const nodeOs = require('node:os')
3 | const fs = require('node:fs')
4 |
5 | function isMusl (file) {
6 | return file.includes('libc.musl-') || file.includes('ld-musl-')
7 | }
8 |
9 | function os () {
10 | return process.platform
11 | }
12 |
13 | function cpu () {
14 | return process.arch
15 | }
16 |
17 | const LDD_PATH = '/usr/bin/ldd'
18 | function getFamilyFromFilesystem () {
19 | try {
20 | const content = fs.readFileSync(LDD_PATH, 'utf-8')
21 | if (content.includes('musl')) {
22 | return 'musl'
23 | }
24 | if (content.includes('GNU C Library')) {
25 | return 'glibc'
26 | }
27 | return null
28 | } catch {
29 | return undefined
30 | }
31 | }
32 |
33 | function getFamilyFromReport () {
34 | const originalExclude = process.report.excludeNetwork
35 | process.report.excludeNetwork = true
36 | const report = process.report.getReport()
37 | process.report.excludeNetwork = originalExclude
38 | if (report.header?.glibcVersionRuntime) {
39 | family = 'glibc'
40 | } else if (Array.isArray(report.sharedObjects) && report.sharedObjects.some(isMusl)) {
41 | family = 'musl'
42 | } else {
43 | family = null
44 | }
45 | return family
46 | }
47 |
48 | let family
49 | function libc (osName) {
50 | if (osName !== 'linux') {
51 | return undefined
52 | }
53 | if (family === undefined) {
54 | family = getFamilyFromFilesystem()
55 | if (family === undefined) {
56 | family = getFamilyFromReport()
57 | }
58 | }
59 | return family
60 | }
61 |
62 | function devEngines (env = {}) {
63 | const osName = env.os || os()
64 | return {
65 | cpu: {
66 | name: env.cpu || cpu(),
67 | },
68 | libc: {
69 | name: env.libc || libc(osName),
70 | },
71 | os: {
72 | name: osName,
73 | version: env.osVersion || nodeOs.release(),
74 | },
75 | packageManager: {
76 | name: 'npm',
77 | version: env.npmVersion,
78 | },
79 | runtime: {
80 | name: 'node',
81 | version: env.nodeVersion || process.version,
82 | },
83 | }
84 | }
85 |
86 | module.exports = {
87 | cpu,
88 | libc,
89 | os,
90 | devEngines,
91 | }
92 |
--------------------------------------------------------------------------------
/lib/dev-engines.js:
--------------------------------------------------------------------------------
1 | const satisfies = require('semver/functions/satisfies')
2 | const validRange = require('semver/ranges/valid')
3 |
4 | const recognizedOnFail = [
5 | 'ignore',
6 | 'warn',
7 | 'error',
8 | 'download',
9 | ]
10 |
11 | const recognizedProperties = [
12 | 'name',
13 | 'version',
14 | 'onFail',
15 | ]
16 |
17 | const recognizedEngines = [
18 | 'packageManager',
19 | 'runtime',
20 | 'cpu',
21 | 'libc',
22 | 'os',
23 | ]
24 |
25 | /** checks a devEngine dependency */
26 | function checkDependency (wanted, current, opts) {
27 | const { engine } = opts
28 |
29 | if ((typeof wanted !== 'object' || wanted === null) || Array.isArray(wanted)) {
30 | throw new Error(`Invalid non-object value for "${engine}"`)
31 | }
32 |
33 | const properties = Object.keys(wanted)
34 |
35 | for (const prop of properties) {
36 | if (!recognizedProperties.includes(prop)) {
37 | throw new Error(`Invalid property "${prop}" for "${engine}"`)
38 | }
39 | }
40 |
41 | if (!properties.includes('name')) {
42 | throw new Error(`Missing "name" property for "${engine}"`)
43 | }
44 |
45 | if (typeof wanted.name !== 'string') {
46 | throw new Error(`Invalid non-string value for "name" within "${engine}"`)
47 | }
48 |
49 | if (typeof current.name !== 'string' || current.name === '') {
50 | throw new Error(`Unable to determine "name" for "${engine}"`)
51 | }
52 |
53 | if (properties.includes('onFail')) {
54 | if (typeof wanted.onFail !== 'string') {
55 | throw new Error(`Invalid non-string value for "onFail" within "${engine}"`)
56 | }
57 | if (!recognizedOnFail.includes(wanted.onFail)) {
58 | throw new Error(`Invalid onFail value "${wanted.onFail}" for "${engine}"`)
59 | }
60 | }
61 |
62 | if (wanted.name !== current.name) {
63 | return new Error(
64 | `Invalid name "${wanted.name}" does not match "${current.name}" for "${engine}"`
65 | )
66 | }
67 |
68 | if (properties.includes('version')) {
69 | if (typeof wanted.version !== 'string') {
70 | throw new Error(`Invalid non-string value for "version" within "${engine}"`)
71 | }
72 | if (typeof current.version !== 'string' || current.version === '') {
73 | throw new Error(`Unable to determine "version" for "${engine}" "${wanted.name}"`)
74 | }
75 | if (validRange(wanted.version)) {
76 | if (!satisfies(current.version, wanted.version, opts.semver)) {
77 | return new Error(
78 | // eslint-disable-next-line max-len
79 | `Invalid semver version "${wanted.version}" does not match "${current.version}" for "${engine}"`
80 | )
81 | }
82 | } else if (wanted.version !== current.version) {
83 | return new Error(
84 | `Invalid version "${wanted.version}" does not match "${current.version}" for "${engine}"`
85 | )
86 | }
87 | }
88 | }
89 |
90 | /** checks devEngines package property and returns array of warnings / errors */
91 | function checkDevEngines (wanted, current = {}, opts = {}) {
92 | if ((typeof wanted !== 'object' || wanted === null) || Array.isArray(wanted)) {
93 | throw new Error(`Invalid non-object value for devEngines`)
94 | }
95 |
96 | const errors = []
97 |
98 | for (const engine of Object.keys(wanted)) {
99 | if (!recognizedEngines.includes(engine)) {
100 | throw new Error(`Invalid property "${engine}"`)
101 | }
102 | const dependencyAsAuthored = wanted[engine]
103 | const dependencies = [dependencyAsAuthored].flat()
104 | const currentEngine = current[engine] || {}
105 |
106 | // this accounts for empty array eg { runtime: [] } and ignores it
107 | if (dependencies.length === 0) {
108 | continue
109 | }
110 |
111 | const depErrors = []
112 | for (const dep of dependencies) {
113 | const result = checkDependency(dep, currentEngine, { ...opts, engine })
114 | if (result) {
115 | depErrors.push(result)
116 | }
117 | }
118 |
119 | const invalid = depErrors.length === dependencies.length
120 |
121 | if (invalid) {
122 | const lastDependency = dependencies[dependencies.length - 1]
123 | let onFail = lastDependency.onFail || 'error'
124 | if (onFail === 'download') {
125 | onFail = 'error'
126 | }
127 |
128 | const err = Object.assign(new Error(`Invalid engine "${engine}"`), {
129 | errors: depErrors,
130 | engine,
131 | isWarn: onFail === 'warn',
132 | isError: onFail === 'error',
133 | current: currentEngine,
134 | required: dependencyAsAuthored,
135 | })
136 |
137 | errors.push(err)
138 | }
139 | }
140 | return errors
141 | }
142 |
143 | module.exports = {
144 | checkDevEngines,
145 | }
146 |
--------------------------------------------------------------------------------
/lib/index.js:
--------------------------------------------------------------------------------
1 | const semver = require('semver')
2 | const currentEnv = require('./current-env')
3 | const { checkDevEngines } = require('./dev-engines')
4 |
5 | const checkEngine = (target, npmVer, nodeVer, force = false) => {
6 | const nodev = force ? null : nodeVer
7 | const eng = target.engines
8 | const opt = { includePrerelease: true }
9 | if (!eng) {
10 | return
11 | }
12 |
13 | const nodeFail = nodev && eng.node && !semver.satisfies(nodev, eng.node, opt)
14 | const npmFail = npmVer && eng.npm && !semver.satisfies(npmVer, eng.npm, opt)
15 | if (nodeFail || npmFail) {
16 | throw Object.assign(new Error('Unsupported engine'), {
17 | pkgid: target._id,
18 | current: { node: nodeVer, npm: npmVer },
19 | required: eng,
20 | code: 'EBADENGINE',
21 | })
22 | }
23 | }
24 |
25 | const checkPlatform = (target, force = false, environment = {}) => {
26 | if (force) {
27 | return
28 | }
29 |
30 | const os = environment.os || currentEnv.os()
31 | const cpu = environment.cpu || currentEnv.cpu()
32 | const libc = environment.libc || currentEnv.libc(os)
33 |
34 | const osOk = target.os ? checkList(os, target.os) : true
35 | const cpuOk = target.cpu ? checkList(cpu, target.cpu) : true
36 | let libcOk = target.libc ? checkList(libc, target.libc) : true
37 | if (target.libc && !libc) {
38 | libcOk = false
39 | }
40 |
41 | if (!osOk || !cpuOk || !libcOk) {
42 | throw Object.assign(new Error('Unsupported platform'), {
43 | pkgid: target._id,
44 | current: {
45 | os,
46 | cpu,
47 | libc,
48 | },
49 | required: {
50 | os: target.os,
51 | cpu: target.cpu,
52 | libc: target.libc,
53 | },
54 | code: 'EBADPLATFORM',
55 | })
56 | }
57 | }
58 |
59 | const checkList = (value, list) => {
60 | if (typeof list === 'string') {
61 | list = [list]
62 | }
63 | if (list.length === 1 && list[0] === 'any') {
64 | return true
65 | }
66 | // match none of the negated values, and at least one of the
67 | // non-negated values, if any are present.
68 | let negated = 0
69 | let match = false
70 | for (const entry of list) {
71 | const negate = entry.charAt(0) === '!'
72 | const test = negate ? entry.slice(1) : entry
73 | if (negate) {
74 | negated++
75 | if (value === test) {
76 | return false
77 | }
78 | } else {
79 | match = match || value === test
80 | }
81 | }
82 | return match || negated === list.length
83 | }
84 |
85 | module.exports = {
86 | checkEngine,
87 | checkPlatform,
88 | checkDevEngines,
89 | currentEnv,
90 | }
91 |
--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "npm-install-checks",
3 | "version": "7.1.1",
4 | "description": "Check the engines and platform fields in package.json",
5 | "main": "lib/index.js",
6 | "dependencies": {
7 | "semver": "^7.1.1"
8 | },
9 | "devDependencies": {
10 | "@npmcli/eslint-config": "^5.0.0",
11 | "@npmcli/template-oss": "4.24.3",
12 | "tap": "^16.0.1"
13 | },
14 | "scripts": {
15 | "test": "tap",
16 | "lint": "npm run eslint",
17 | "postlint": "template-oss-check",
18 | "template-oss-apply": "template-oss-apply --force",
19 | "lintfix": "npm run eslint -- --fix",
20 | "snap": "tap",
21 | "posttest": "npm run lint",
22 | "eslint": "eslint \"**/*.{js,cjs,ts,mjs,jsx,tsx}\""
23 | },
24 | "repository": {
25 | "type": "git",
26 | "url": "git+https://github.com/npm/npm-install-checks.git"
27 | },
28 | "keywords": [
29 | "npm,",
30 | "install"
31 | ],
32 | "license": "BSD-2-Clause",
33 | "files": [
34 | "bin/",
35 | "lib/"
36 | ],
37 | "engines": {
38 | "node": "^18.17.0 || >=20.5.0"
39 | },
40 | "author": "GitHub Inc.",
41 | "templateOSS": {
42 | "//@npmcli/template-oss": "This file is partially managed by @npmcli/template-oss. Edits may be overwritten.",
43 | "version": "4.24.3",
44 | "publish": "true"
45 | },
46 | "tap": {
47 | "nyc-arg": [
48 | "--exclude",
49 | "tap-snapshots/**"
50 | ]
51 | }
52 | }
53 |
--------------------------------------------------------------------------------
/release-please-config.json:
--------------------------------------------------------------------------------
1 | {
2 | "group-pull-request-title-pattern": "chore: release ${version}",
3 | "pull-request-title-pattern": "chore: release${component} ${version}",
4 | "changelog-sections": [
5 | {
6 | "type": "feat",
7 | "section": "Features",
8 | "hidden": false
9 | },
10 | {
11 | "type": "fix",
12 | "section": "Bug Fixes",
13 | "hidden": false
14 | },
15 | {
16 | "type": "docs",
17 | "section": "Documentation",
18 | "hidden": false
19 | },
20 | {
21 | "type": "deps",
22 | "section": "Dependencies",
23 | "hidden": false
24 | },
25 | {
26 | "type": "chore",
27 | "section": "Chores",
28 | "hidden": true
29 | }
30 | ],
31 | "packages": {
32 | ".": {
33 | "package-name": ""
34 | }
35 | },
36 | "prerelease-type": "pre.0"
37 | }
38 |
--------------------------------------------------------------------------------
/test/check-dev-engines.js:
--------------------------------------------------------------------------------
1 | const t = require('tap')
2 | const { checkDevEngines, currentEnv } = require('..')
3 |
4 | t.test('noop options', async t => {
5 | t.same(checkDevEngines({
6 | runtime: [],
7 | }, currentEnv.devEngines()), [])
8 | })
9 |
10 | t.test('unrecognized property', async t => {
11 | const wanted = { name: `alpha`, version: '1' }
12 | const current = { name: `alpha` }
13 | t.throws(
14 | () => checkDevEngines({ unrecognized: wanted }, { os: current }),
15 | new Error('Invalid property "unrecognized"')
16 | )
17 | })
18 |
19 | t.test('empty devEngines', async t => {
20 | t.same(checkDevEngines({ }, { os: { name: `darwin` } }), [])
21 | })
22 |
23 | t.test('invalid name', async t => {
24 | const wanted = { name: `alpha`, onFail: 'download' }
25 | const current = { name: `beta` }
26 | t.same(checkDevEngines({ os: wanted }, { os: current }), [
27 | Object.assign(new Error(`Invalid engine "os"`), {
28 | errors: [
29 | new Error(`Invalid name "alpha" does not match "beta" for "os"`),
30 | ],
31 | engine: 'os',
32 | isWarn: false,
33 | isError: true,
34 | current,
35 | required: wanted,
36 | }),
37 | ])
38 | })
39 |
40 | t.test('default options', async t => {
41 | t.same(checkDevEngines({}, currentEnv.devEngines()), [])
42 | })
43 |
44 | t.test('tests non-object', async t => {
45 | const core = [1, true, false, null, undefined]
46 | for (const nonObject of [...core, [[]], ...core.map(v => [v])]) {
47 | t.test('invalid devEngines', async t => {
48 | t.throws(
49 | () => checkDevEngines(nonObject, {
50 | runtime: {
51 | name: 'nondescript',
52 | version: '14',
53 | },
54 | }),
55 | new Error(`Invalid non-object value for devEngines`)
56 | )
57 | })
58 |
59 | t.test('invalid engine property', async t => {
60 | t.throws(
61 | () => checkDevEngines({
62 | runtime: nonObject,
63 | }, {
64 | runtime: {
65 | name: 'nondescript',
66 | version: '14',
67 | },
68 | }),
69 | new Error(`Invalid non-object value for "runtime"`)
70 | )
71 | })
72 | }
73 | })
74 |
75 | t.test('tests non-string ', async t => {
76 | for (const nonString of [1, true, false, null, undefined, {}, []]) {
77 | t.test('invalid name value', async t => {
78 | t.throws(
79 | () => checkDevEngines({
80 | runtime: {
81 | name: nonString,
82 | version: '14',
83 | },
84 | }, {
85 | runtime: {
86 | name: 'nondescript',
87 | version: '14',
88 | },
89 | }),
90 | new Error(`Invalid non-string value for "name" within "runtime"`)
91 | )
92 | })
93 | t.test('invalid version value', async t => {
94 | t.throws(
95 | () => checkDevEngines({
96 | runtime: {
97 | name: 'nondescript',
98 | version: nonString,
99 | },
100 | }, {
101 | runtime: {
102 | name: 'nondescript',
103 | version: '14',
104 | },
105 | }),
106 | new Error(`Invalid non-string value for "version" within "runtime"`)
107 | )
108 | })
109 | t.test('invalid onFail value', async t => {
110 | t.throws(
111 | () => checkDevEngines({
112 | runtime: {
113 | name: 'nondescript',
114 | version: '14',
115 | onFail: nonString,
116 | },
117 | }, {
118 | runtime: {
119 | name: 'nondescript',
120 | version: '14',
121 | },
122 | }),
123 | new Error(`Invalid non-string value for "onFail" within "runtime"`)
124 | )
125 | })
126 | }
127 | })
128 |
129 | t.test('tests all the right fields', async t => {
130 | for (const env of ['packageManager', 'runtime', 'cpu', 'libc', 'os']) {
131 | t.test(`field - ${env}`, async t => {
132 | t.test('current name does not match, wanted has extra attribute', async t => {
133 | const wanted = { name: `test-${env}-wanted`, extra: `test-${env}-extra` }
134 | const current = { name: `test-${env}-current` }
135 | t.throws(
136 | () => checkDevEngines({ [env]: wanted }, { [env]: current }),
137 | new Error(`Invalid property "extra" for "${env}"`)
138 | )
139 | })
140 | t.test('current is not given', async t => {
141 | const wanted = { name: `test-${env}-wanted` }
142 | t.throws(
143 | () => checkDevEngines({ [env]: wanted }),
144 | new Error(`Unable to determine "name" for "${env}"`)
145 | )
146 | })
147 | t.test('name only', async t => {
148 | const wanted = { name: 'test-name' }
149 | const current = { name: 'test-name' }
150 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [])
151 | })
152 | t.test('non-semver version is not the same', async t => {
153 | const wanted = { name: `test-name`, version: 'test-version-wanted' }
154 | const current = { name: `test-name`, version: 'test-version-current' }
155 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [
156 | Object.assign(new Error(`Invalid engine "${env}"`), {
157 | errors: [
158 | // eslint-disable-next-line max-len
159 | new Error(`Invalid version "test-version-wanted" does not match "test-version-current" for "${env}"`),
160 | ],
161 | engine: env,
162 | isWarn: false,
163 | isError: true,
164 | current: { name: `test-name`, version: 'test-version-current' },
165 | required: { name: `test-name`, version: 'test-version-wanted' },
166 | }),
167 | ])
168 | })
169 | t.test('non-semver version is the same', async t => {
170 | const wanted = { name: `test-name`, version: 'test-version' }
171 | const current = { name: `test-name`, version: 'test-version' }
172 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [])
173 | })
174 | t.test('semver version is not in range', async t => {
175 | const wanted = { name: `test-name`, version: '^1.0.0' }
176 | const current = { name: `test-name`, version: '2.0.0' }
177 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [
178 | Object.assign(new Error(`Invalid engine "${env}"`), {
179 | errors: [
180 | // eslint-disable-next-line max-len
181 | new Error(`Invalid semver version "^1.0.0" does not match "2.0.0" for "${env}"`),
182 | ],
183 | engine: env,
184 | isWarn: false,
185 | isError: true,
186 | current: { name: `test-name`, version: '2.0.0' },
187 | required: { name: `test-name`, version: '^1.0.0' },
188 | }),
189 | ])
190 | })
191 | t.test('semver version is in range', async t => {
192 | const wanted = { name: `test-name`, version: '^1.0.0' }
193 | const current = { name: `test-name`, version: '1.0.0' }
194 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [])
195 | })
196 | t.test('returns the last failure', async t => {
197 | const wanted = [
198 | { name: `test-name`, version: 'test-version-one' },
199 | { name: `test-name`, version: 'test-version-two' },
200 | ]
201 | const current = { name: `test-name`, version: 'test-version-three' }
202 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [
203 | Object.assign(new Error(`Invalid engine "${env}"`), {
204 | errors: [
205 | // eslint-disable-next-line max-len
206 | new Error(`Invalid version "test-version-one" does not match "test-version-three" for "${env}"`),
207 | // eslint-disable-next-line max-len
208 | new Error(`Invalid version "test-version-two" does not match "test-version-three" for "${env}"`),
209 | ],
210 | engine: env,
211 | isWarn: false,
212 | isError: true,
213 | current: { name: `test-name`, version: 'test-version-three' },
214 | required: [
215 | { name: `test-name`, version: 'test-version-one' },
216 | { name: `test-name`, version: 'test-version-two' },
217 | ],
218 | }),
219 | ])
220 | })
221 | t.test('unrecognized onFail', async t => {
222 | const wanted = { name: `test-name`, version: '^1.0.0', onFail: 'unrecognized' }
223 | const current = { name: `test-name`, version: '1.0.0' }
224 | t.throws(
225 | () => checkDevEngines({ [env]: wanted }, { [env]: current }),
226 | new Error(`Invalid onFail value "unrecognized" for "${env}"`)
227 | )
228 | })
229 | t.test('missing name', async t => {
230 | const wanted = { version: '^1.0.0' }
231 | const current = { name: `test-name`, version: '1.0.0' }
232 | t.throws(
233 | () => checkDevEngines({ [env]: wanted }, { [env]: current }),
234 | new Error(`Missing "name" property for "${env}"`)
235 | )
236 | })
237 | t.test('invalid name', async t => {
238 | const wanted = { name: `alpha` }
239 | const current = { name: `beta` }
240 | t.same(checkDevEngines({ [env]: wanted }, { [env]: current }), [
241 | Object.assign(new Error(`Invalid engine "${env}"`), {
242 | errors: [
243 | new Error(`Invalid name "alpha" does not match "beta" for "${env}"`),
244 | ],
245 | engine: env,
246 | isWarn: false,
247 | isError: true,
248 | current,
249 | required: wanted,
250 | }),
251 | ])
252 | })
253 | t.test('missing version', async t => {
254 | const wanted = { name: `alpha`, version: '1' }
255 | const current = { name: `alpha` }
256 | t.throws(
257 | () => checkDevEngines({ [env]: wanted }, { [env]: current }),
258 | new Error(`Unable to determine "version" for "${env}" "alpha"`)
259 | )
260 | })
261 | })
262 | }
263 | })
264 |
265 | t.test('spec 1', async t => {
266 | const example = {
267 | runtime: {
268 | name: 'node',
269 | version: '>= 20.0.0',
270 | onFail: 'error',
271 | },
272 | packageManager: {
273 | name: 'yarn',
274 | version: '3.2.3',
275 | onFail: 'download',
276 | },
277 | }
278 |
279 | t.same(checkDevEngines(example, {
280 | os: { name: 'darwin', version: '23.0.0' },
281 | cpu: { name: 'arm' },
282 | libc: { name: 'glibc' },
283 | runtime: { name: 'node', version: '20.0.0' },
284 | packageManager: { name: 'yarn', version: '3.2.3' },
285 | }), [])
286 | })
287 |
288 | t.test('spec 2', async t => {
289 | const example = {
290 | os: {
291 | name: 'darwin',
292 | version: '>= 23.0.0',
293 | },
294 | cpu: [
295 | {
296 | name: 'arm',
297 | },
298 | {
299 | name: 'x86',
300 | },
301 | ],
302 | libc: {
303 | name: 'glibc',
304 | },
305 | runtime: [
306 | {
307 | name: 'bun',
308 | version: '>= 1.0.0',
309 | onFail: 'ignore',
310 | },
311 | {
312 | name: 'node',
313 | version: '>= 20.0.0',
314 | onFail: 'error',
315 | },
316 | ],
317 | packageManager: [
318 | {
319 | name: 'bun',
320 | version: '>= 1.0.0',
321 | onFail: 'ignore',
322 | },
323 | {
324 | name: 'yarn',
325 | version: '3.2.3',
326 | onFail: 'download',
327 | },
328 | ],
329 | }
330 |
331 | t.same(checkDevEngines(example, {
332 | os: { name: 'darwin', version: '23.0.0' },
333 | cpu: { name: 'arm' },
334 | libc: { name: 'glibc' },
335 | runtime: { name: 'node', version: '20.0.0' },
336 | packageManager: { name: 'yarn', version: '3.2.3' },
337 | }), [])
338 |
339 | t.same(checkDevEngines(example, {
340 | os: { name: 'darwin', version: '10.0.0' },
341 | cpu: { name: 'arm' },
342 | libc: { name: 'glibc' },
343 | runtime: { name: 'node', version: '20.0.0' },
344 | packageManager: { name: 'yarn', version: '3.2.3' },
345 | }), [
346 | Object.assign(new Error(`Invalid engine "os"`), {
347 | errors: [
348 | // eslint-disable-next-line max-len
349 | new Error(`Invalid semver version ">= 23.0.0" does not match "10.0.0" for "os"`),
350 | ],
351 | engine: 'os',
352 | isWarn: false,
353 | isError: true,
354 | current: { name: 'darwin', version: '10.0.0' },
355 | required: {
356 | name: 'darwin',
357 | version: '>= 23.0.0',
358 | },
359 | }),
360 | ])
361 |
362 | t.same(checkDevEngines(example, {
363 | os: { name: 'darwin', version: '23.0.0' },
364 | cpu: { name: 'arm' },
365 | libc: { name: 'glibc' },
366 | runtime: { name: 'nondescript', version: '20.0.0' },
367 | packageManager: { name: 'yarn', version: '3.2.3' },
368 | }), [
369 | Object.assign(new Error(`Invalid engine "runtime"`), {
370 | errors: [
371 | // eslint-disable-next-line max-len
372 | new Error(`Invalid name "bun" does not match "nondescript" for "runtime"`),
373 | // eslint-disable-next-line max-len
374 | new Error(`Invalid name "node" does not match "nondescript" for "runtime"`),
375 | ],
376 | engine: 'runtime',
377 | isWarn: false,
378 | isError: true,
379 | current: { name: 'nondescript', version: '20.0.0' },
380 | required: [
381 | {
382 | name: 'bun',
383 | version: '>= 1.0.0',
384 | onFail: 'ignore',
385 | },
386 | {
387 | name: 'node',
388 | version: '>= 20.0.0',
389 | onFail: 'error',
390 | },
391 | ],
392 | }),
393 | ])
394 | })
395 |
396 | t.test('empty array along side error', async t => {
397 | t.same(checkDevEngines({
398 | cpu: [],
399 | runtime: {
400 | name: 'bun',
401 | onFail: 'error',
402 | },
403 | }, {
404 | cpu: { name: 'arm' },
405 | runtime: { name: 'node', version: '20.0.0' },
406 | }), [Object.assign(new Error(`Invalid engine "runtime"`), {
407 | errors: [
408 | new Error(`Invalid name "bun" does not match "node" for "runtime"`),
409 | ],
410 | engine: 'runtime',
411 | isWarn: false,
412 | isError: true,
413 | current: { name: 'node', version: '20.0.0' },
414 | required: {
415 | name: 'bun',
416 | onFail: 'error',
417 | },
418 | })])
419 | })
420 |
--------------------------------------------------------------------------------
/test/check-engine.js:
--------------------------------------------------------------------------------
1 | const t = require('tap')
2 | const { checkEngine } = require('..')
3 |
4 | const e = (npm, node, _id = 'pkg@1.2.3') => {
5 | const engines = {}
6 | if (npm) {
7 | engines.npm = npm
8 | }
9 | if (node) {
10 | engines.node = node
11 | }
12 | return { engines, _id }
13 | }
14 |
15 | t.test('no engine', async () =>
16 | checkEngine({}, '1.3.2', '0.2.1'))
17 |
18 | t.test('empty engine object', async () =>
19 | checkEngine(e(), '1.1.2', '0.2.1'))
20 |
21 | t.test('node version too old', async t =>
22 | t.throws(() => checkEngine(e(null, '0.10.24'), '1.1.2', '0.10.18'), {
23 | required: { node: '0.10.24' },
24 | code: 'EBADENGINE',
25 | }))
26 |
27 | t.test('npm version too old', async t =>
28 | t.throws(() => checkEngine(e('^1.4.6'), '1.3.2', '0.2.1'), {
29 | required: { npm: '^1.4.6' },
30 | code: 'EBADENGINE',
31 | }))
32 |
33 | t.test('force node version too old', async () =>
34 | checkEngine(e(null, '0.1.0', 'test@1.0.0'), '1.3.2', '0.2.1', true))
35 |
36 | t.test('cannot force when npm version too old', async t =>
37 | t.throws(() => checkEngine(e('^1.4.6', null, 'test@1.0.0'), '1.3.2', '0.2.1'), {
38 | code: 'EBADENGINE',
39 | }))
40 |
41 | t.test('npm prerelease', async () =>
42 | checkEngine(e('>=1.2.3', '>=0.8'), '69.420.0-yolo', '69.420.0-yolo'))
43 |
44 | t.test('node prerelease', async () =>
45 | checkEngine(e('>=1.2.3', '>=0.8'), '1.2.3', '69.420.0-yolo'))
46 |
47 | t.test('no node version', async () =>
48 | checkEngine(e('>=1.2.3', '>=0.8'), '1.2.3', null))
49 |
50 | t.test('no npm version', async () =>
51 | checkEngine(e('>=1.2.3', '>=0.8'), null, '1.2.3'))
52 |
--------------------------------------------------------------------------------
/test/check-platform.js:
--------------------------------------------------------------------------------
1 | const t = require('tap')
2 | const { checkPlatform } = require('..')
3 |
4 | t.test('target cpu wrong', async t =>
5 | t.throws(() => checkPlatform({
6 | cpu: 'enten-cpu',
7 | os: 'any',
8 | }), { code: 'EBADPLATFORM' }))
9 |
10 | t.test('os wrong', async t =>
11 | t.throws(() => checkPlatform({
12 | cpu: 'any',
13 | os: 'enten-os',
14 | }), { code: 'EBADPLATFORM' }))
15 |
16 | t.test('nothing wrong', async () =>
17 | checkPlatform({ cpu: 'any', os: 'any' }))
18 |
19 | t.test('force', async () =>
20 | checkPlatform({ cpu: 'enten-cpu', os: 'any' }, true))
21 |
22 | t.test('no opinions', async () =>
23 | checkPlatform({}))
24 |
25 | t.test('only target cpu wrong', async t =>
26 | t.throws(() => checkPlatform({ cpu: 'enten-cpu' }), { code: 'EBADPLATFORM' }))
27 |
28 | t.test('only os wrong', async t =>
29 | t.throws(() => checkPlatform({ os: 'enten-os' }), { code: 'EBADPLATFORM' }))
30 |
31 | t.test('everything wrong w/arrays', async t =>
32 | t.throws(() => checkPlatform({
33 | cpu: ['enten-cpu'],
34 | os: ['enten-os'],
35 | }), { code: 'EBADPLATFORM' }))
36 |
37 | t.test('os wrong (negation)', async t =>
38 | t.throws(() => checkPlatform({
39 | cpu: 'any',
40 | os: '!' + process.platform,
41 | }), { code: 'EBADPLATFORM' }))
42 |
43 | t.test('nothing wrong (negation)', async () =>
44 | checkPlatform({ cpu: '!enten-cpu', os: '!enten-os' }))
45 |
46 | t.test('nothing wrong with overridden os', async () =>
47 | checkPlatform({
48 | cpu: 'any',
49 | os: 'enten-os',
50 | }, false, {
51 | os: 'enten-os',
52 | }))
53 |
54 | t.test('nothing wrong with overridden cpu', async () =>
55 | checkPlatform({
56 | cpu: 'enten-cpu',
57 | os: 'any',
58 | }, false, {
59 | cpu: 'enten-cpu',
60 | }))
61 |
62 | t.test('nothing wrong with overridden libc', async () =>
63 | checkPlatform({
64 | cpu: 'enten-cpu',
65 | libc: 'enten-libc',
66 | }, false, {
67 | cpu: 'enten-cpu',
68 | libc: 'enten-libc',
69 | }))
70 |
71 | t.test('wrong os with overridden os', async t =>
72 | t.throws(() => checkPlatform({
73 | cpu: 'any',
74 | os: 'enten-os',
75 | }, false, {
76 | os: 'another-os',
77 | }), { code: 'EBADPLATFORM' }))
78 |
79 | t.test('wrong cpu with overridden cpu', async t =>
80 | t.throws(() => checkPlatform({
81 | cpu: 'enten-cpu',
82 | os: 'any',
83 | }, false, {
84 | cpu: 'another-cpu',
85 | }), { code: 'EBADPLATFORM' }))
86 |
87 | t.test('wrong libc with overridden libc', async t =>
88 | t.throws(() => checkPlatform({
89 | cpu: 'enten-cpu',
90 | os: 'any',
91 | libc: 'enten-libc',
92 | }, false, {
93 | libc: 'another-libc',
94 | }), { code: 'EBADPLATFORM' }))
95 |
96 | t.test('libc', (t) => {
97 | let noCacheChckPtfm
98 | let PLATFORM = 'linux'
99 | let REPORT = {}
100 | let readFileSync
101 | let noCache = true
102 |
103 | function withCache (cb) {
104 | noCache = false
105 | cb()
106 | noCache = true
107 | withoutLibcCache()
108 | }
109 |
110 | function withoutLibcCache () {
111 | readFileSync = () => {
112 | throw new Error('File not found')
113 | }
114 | const original = t.mock('..', {
115 | '../lib/current-env': t.mock('../lib/current-env', {
116 | 'node:fs': {
117 | readFileSync: () => {
118 | return readFileSync()
119 | },
120 | },
121 | 'node:process': Object.defineProperty({
122 | report: {
123 | getReport: () => REPORT,
124 | },
125 | }, 'platform', {
126 | enumerable: true,
127 | get: () => PLATFORM,
128 | }),
129 | }),
130 | }).checkPlatform
131 | noCacheChckPtfm = (...args) => {
132 | try {
133 | original(...args)
134 | } finally {
135 | if (noCache) {
136 | withoutLibcCache()
137 | }
138 | }
139 | }
140 | }
141 |
142 | withoutLibcCache()
143 |
144 | t.test('fails when not in linux', (t) => {
145 | PLATFORM = 'darwin'
146 |
147 | t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
148 | 'fails for glibc when not in linux')
149 | t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
150 | 'fails for musl when not in linux')
151 | t.end()
152 | })
153 |
154 | t.test('glibc', (t) => {
155 | PLATFORM = 'linux'
156 |
157 | withCache(() => {
158 | readFileSync = () => 'this ldd file contains GNU C Library'
159 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }),
160 | 'allows glibc on glibc from ldd file')
161 |
162 | readFileSync = () => {
163 | throw new Error('File not found')
164 | }
165 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc from ldd file cache')
166 | })
167 |
168 | REPORT = {}
169 | t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
170 | 'fails when report is missing header property')
171 |
172 | REPORT = { header: {} }
173 | t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
174 | 'fails when header is missing glibcVersionRuntime property')
175 |
176 | withCache(() => {
177 | REPORT = { header: { glibcVersionRuntime: '1' } }
178 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc on glibc')
179 |
180 | REPORT = {}
181 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'glibc' }), 'allows glibc from report cache')
182 | })
183 |
184 | readFileSync = () => 'this ldd file is unsupported'
185 | t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
186 | 'fails when ldd file exists but is not something known')
187 |
188 | t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
189 | 'does not allow musl on glibc')
190 |
191 | t.end()
192 | })
193 |
194 | t.test('musl', (t) => {
195 | PLATFORM = 'linux'
196 |
197 | readFileSync = () => 'this ldd file contains musl'
198 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl from ldd file')
199 |
200 | REPORT = {}
201 | t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
202 | 'fails when report is missing sharedObjects property')
203 |
204 | REPORT = { sharedObjects: {} }
205 | t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
206 | 'fails when sharedObjects property is not an array')
207 |
208 | REPORT = { sharedObjects: [] }
209 | t.throws(() => noCacheChckPtfm({ libc: 'musl' }), { code: 'EBADPLATFORM' },
210 | 'fails when sharedObjects does not contain musl')
211 |
212 | REPORT = { sharedObjects: ['ld-musl-foo'] }
213 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as ld-musl-')
214 |
215 | REPORT = { sharedObjects: ['libc.musl-'] }
216 | t.doesNotThrow(() => noCacheChckPtfm({ libc: 'musl' }), 'allows musl on musl as libc.musl-')
217 |
218 | t.throws(() => noCacheChckPtfm({ libc: 'glibc' }), { code: 'EBADPLATFORM' },
219 | 'does not allow glibc on musl')
220 |
221 | t.end()
222 | })
223 |
224 | t.end()
225 | })
226 |
--------------------------------------------------------------------------------