├── .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 | <!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> 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 | <!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> 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 | <!-- This file is automatically added by @npmcli/template-oss. Do not edit. --> 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 | --------------------------------------------------------------------------------