npq 3.9.1 → 3.10.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.github/workflows/automerge.yml +39 -0
- package/README.md +15 -4
- package/__tests__/cli.parser.test.js +121 -0
- package/__tests__/env-var-integration.test.js +109 -0
- package/__tests__/packageManager.test.js +19 -5
- package/lib/cli.js +1 -1
- package/lib/packageManager.js +6 -2
- package/package.json +5 -5
- package/.github/mergify.yml +0 -17
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
name: automerge
|
|
2
|
+
on:
|
|
3
|
+
pull_request:
|
|
4
|
+
types:
|
|
5
|
+
- labeled
|
|
6
|
+
- unlabeled
|
|
7
|
+
- synchronize
|
|
8
|
+
- opened
|
|
9
|
+
- edited
|
|
10
|
+
- ready_for_review
|
|
11
|
+
- reopened
|
|
12
|
+
- unlocked
|
|
13
|
+
pull_request_review:
|
|
14
|
+
types:
|
|
15
|
+
- submitted
|
|
16
|
+
check_suite:
|
|
17
|
+
types:
|
|
18
|
+
- completed
|
|
19
|
+
status: {}
|
|
20
|
+
jobs:
|
|
21
|
+
automerge:
|
|
22
|
+
runs-on: ubuntu-latest
|
|
23
|
+
permissions:
|
|
24
|
+
contents: write
|
|
25
|
+
pull-requests: write
|
|
26
|
+
steps:
|
|
27
|
+
- id: automerge
|
|
28
|
+
name: automerge
|
|
29
|
+
uses: "pascalgn/automerge-action@v0.16.4"
|
|
30
|
+
env:
|
|
31
|
+
GITHUB_TOKEN: "${{ secrets.GITHUB_TOKEN }}"
|
|
32
|
+
# we only merge PRs with labels of "dependencies"
|
|
33
|
+
MERGE_LABELS: "automerge"
|
|
34
|
+
MERGE_REMOVE_LABELS: "automerge"
|
|
35
|
+
MERGE_METHOD: "squash"
|
|
36
|
+
MERGE_COMMIT_MESSAGE: "automatic"
|
|
37
|
+
MERGE_FORKS: "false"
|
|
38
|
+
MERGE_REQUIRED_APPROVALS: "0"
|
|
39
|
+
UPDATE_METHOD: "rebase"
|
package/README.md
CHANGED
|
@@ -37,7 +37,7 @@ npq install express
|
|
|
37
37
|
* Package has a LICENSE file
|
|
38
38
|
* Package has pre/post install scripts
|
|
39
39
|
|
|
40
|
-
If npq is prompted to continue with the install, it simply hands over the actual package install job to the package manager (npm by default).
|
|
40
|
+
If npq is prompted to continue with the install, it simply hands over the actual package install job to the package manager (npm by default, or as specified via the `NPQ_PKG_MGR` environment variable). Note that if a package manager is specified via command-line options, it will override the `NPQ_PKG_MGR` environment variable.
|
|
41
41
|
|
|
42
42
|
DISCLAIMER: there's no guaranteed absolute safety; a malicious or vulnerable package could still exist that has no security vulnerabilities publicly disclosed and passes npq's checks.
|
|
43
43
|
|
|
@@ -71,15 +71,26 @@ alias npm='npq-hero'
|
|
|
71
71
|
|
|
72
72
|
### Offload to package managers
|
|
73
73
|
|
|
74
|
-
If you're using `yarn`, or generally want to explicitly tell npq which package manager to use you can specify an environment variable: `NPQ_PKG_MGR
|
|
74
|
+
If you're using `yarn`, `pnpm`, or generally want to explicitly tell npq which package manager to use you can specify an environment variable: `NPQ_PKG_MGR=<package-manager>`
|
|
75
75
|
|
|
76
|
-
|
|
76
|
+
Examples:
|
|
77
77
|
|
|
78
|
+
**Using yarn:**
|
|
78
79
|
```bash
|
|
79
80
|
alias yarn="NPQ_PKG_MGR=yarn npq-hero"
|
|
80
81
|
```
|
|
81
82
|
|
|
82
|
-
|
|
83
|
+
**Using pnpm:**
|
|
84
|
+
```bash
|
|
85
|
+
NPQ_PKG_MGR=pnpm npx npq install fastify
|
|
86
|
+
```
|
|
87
|
+
|
|
88
|
+
**Using pnpm with alias:**
|
|
89
|
+
```bash
|
|
90
|
+
alias pnpm="NPQ_PKG_MGR=pnpm npq-hero"
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
Note: `npq` by default will offload all commands and their arguments to the `npm` (or other package manager as specified) after it finished its due-diligence checks for the respective packages.
|
|
83
94
|
|
|
84
95
|
## Marshalls
|
|
85
96
|
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// __tests__/env-var-integration.test.js
|
|
2
|
+
|
|
3
|
+
describe('NPQ_PKG_MGR Environment Variable Integration', () => {
|
|
4
|
+
let originalArgv
|
|
5
|
+
let originalNPQ_PKG_MGR
|
|
6
|
+
|
|
7
|
+
beforeEach(() => {
|
|
8
|
+
// Save original values
|
|
9
|
+
originalArgv = process.argv
|
|
10
|
+
originalNPQ_PKG_MGR = process.env.NPQ_PKG_MGR
|
|
11
|
+
|
|
12
|
+
// Clear environment variable
|
|
13
|
+
delete process.env.NPQ_PKG_MGR
|
|
14
|
+
|
|
15
|
+
// Mock console methods to avoid output during tests
|
|
16
|
+
jest.spyOn(console, 'log').mockImplementation(() => {})
|
|
17
|
+
jest.spyOn(console, 'error').mockImplementation(() => {})
|
|
18
|
+
jest.spyOn(process, 'exit').mockImplementation(() => {})
|
|
19
|
+
})
|
|
20
|
+
|
|
21
|
+
afterEach(() => {
|
|
22
|
+
// Restore original values
|
|
23
|
+
process.argv = originalArgv
|
|
24
|
+
if (originalNPQ_PKG_MGR !== undefined) {
|
|
25
|
+
process.env.NPQ_PKG_MGR = originalNPQ_PKG_MGR
|
|
26
|
+
} else {
|
|
27
|
+
delete process.env.NPQ_PKG_MGR
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
// Restore mocked methods
|
|
31
|
+
jest.restoreAllMocks()
|
|
32
|
+
})
|
|
33
|
+
|
|
34
|
+
test.only('should prioritize NPQ_PKG_MGR environment variable over command line options', () => {
|
|
35
|
+
// This test verifies the core functionality:
|
|
36
|
+
// process.env.NPQ_PKG_MGR || values.packageManager || values.pkgMgr || 'npm'
|
|
37
|
+
|
|
38
|
+
process.env.NPQ_PKG_MGR = 'pnpm'
|
|
39
|
+
|
|
40
|
+
// Dynamically require to get fresh instance with environment variable set
|
|
41
|
+
jest.resetModules()
|
|
42
|
+
const { CliParser } = require('../lib/cli')
|
|
43
|
+
|
|
44
|
+
// Mock parseArgs to simulate command line input with --packageManager
|
|
45
|
+
const originalParseArgs = require('node:util').parseArgs
|
|
46
|
+
require('node:util').parseArgs = jest.fn().mockReturnValue({
|
|
47
|
+
values: { packageManager: 'yarn' }, // CLI says yarn
|
|
48
|
+
positionals: ['install', 'express']
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const result = CliParser.parseArgsFull()
|
|
52
|
+
|
|
53
|
+
// Environment variable should win over CLI option
|
|
54
|
+
expect(result.packageManager).toBe('pnpm')
|
|
55
|
+
|
|
56
|
+
// Restore
|
|
57
|
+
require('node:util').parseArgs = originalParseArgs
|
|
58
|
+
})
|
|
59
|
+
|
|
60
|
+
test('should fall back to command line option when NPQ_PKG_MGR is not set', () => {
|
|
61
|
+
// Ensure environment variable is not set
|
|
62
|
+
delete process.env.NPQ_PKG_MGR
|
|
63
|
+
|
|
64
|
+
jest.resetModules()
|
|
65
|
+
const { CliParser } = require('../lib/cli')
|
|
66
|
+
|
|
67
|
+
const originalParseArgs = require('node:util').parseArgs
|
|
68
|
+
require('node:util').parseArgs = jest.fn().mockReturnValue({
|
|
69
|
+
values: { packageManager: 'yarn' },
|
|
70
|
+
positionals: ['install', 'express']
|
|
71
|
+
})
|
|
72
|
+
|
|
73
|
+
const result = CliParser.parseArgsFull()
|
|
74
|
+
|
|
75
|
+
expect(result.packageManager).toBe('yarn')
|
|
76
|
+
|
|
77
|
+
// Restore
|
|
78
|
+
require('node:util').parseArgs = originalParseArgs
|
|
79
|
+
})
|
|
80
|
+
|
|
81
|
+
test('should fall back to npm default when neither env var nor CLI option provided', () => {
|
|
82
|
+
delete process.env.NPQ_PKG_MGR
|
|
83
|
+
|
|
84
|
+
jest.resetModules()
|
|
85
|
+
const { CliParser } = require('../lib/cli')
|
|
86
|
+
|
|
87
|
+
const originalParseArgs = require('node:util').parseArgs
|
|
88
|
+
require('node:util').parseArgs = jest.fn().mockReturnValue({
|
|
89
|
+
values: {}, // No package manager specified
|
|
90
|
+
positionals: ['install', 'express']
|
|
91
|
+
})
|
|
92
|
+
|
|
93
|
+
const result = CliParser.parseArgsFull()
|
|
94
|
+
|
|
95
|
+
expect(result.packageManager).toBe('npm')
|
|
96
|
+
|
|
97
|
+
// Restore
|
|
98
|
+
require('node:util').parseArgs = originalParseArgs
|
|
99
|
+
})
|
|
100
|
+
|
|
101
|
+
test('should handle empty NPQ_PKG_MGR environment variable', () => {
|
|
102
|
+
process.env.NPQ_PKG_MGR = '' // Empty string
|
|
103
|
+
|
|
104
|
+
jest.resetModules()
|
|
105
|
+
const { CliParser } = require('../lib/cli')
|
|
106
|
+
|
|
107
|
+
const originalParseArgs = require('node:util').parseArgs
|
|
108
|
+
require('node:util').parseArgs = jest.fn().mockReturnValue({
|
|
109
|
+
values: { packageManager: 'yarn' },
|
|
110
|
+
positionals: ['install', 'express']
|
|
111
|
+
})
|
|
112
|
+
|
|
113
|
+
const result = CliParser.parseArgsFull()
|
|
114
|
+
|
|
115
|
+
// Empty string should be falsy, so fall back to CLI option
|
|
116
|
+
expect(result.packageManager).toBe('yarn')
|
|
117
|
+
|
|
118
|
+
// Restore
|
|
119
|
+
require('node:util').parseArgs = originalParseArgs
|
|
120
|
+
})
|
|
121
|
+
})
|
|
@@ -0,0 +1,109 @@
|
|
|
1
|
+
// __tests__/env-var-integration.test.js
|
|
2
|
+
|
|
3
|
+
// Test the NPQ_PKG_MGR environment variable functionality
|
|
4
|
+
// This tests the key logic change: process.env.NPQ_PKG_MGR || values.packageManager || values.pkgMgr || 'npm'
|
|
5
|
+
|
|
6
|
+
const packageManager = require('../lib/packageManager')
|
|
7
|
+
|
|
8
|
+
const childProcess = require('child_process')
|
|
9
|
+
|
|
10
|
+
jest.mock('child_process', () => {
|
|
11
|
+
return {
|
|
12
|
+
spawn: jest.fn((cmd, options) => {
|
|
13
|
+
return { pid: 12345 }
|
|
14
|
+
})
|
|
15
|
+
}
|
|
16
|
+
})
|
|
17
|
+
|
|
18
|
+
describe('NPQ_PKG_MGR Environment Variable Integration', () => {
|
|
19
|
+
let originalNPQ_PKG_MGR
|
|
20
|
+
|
|
21
|
+
beforeEach(() => {
|
|
22
|
+
originalNPQ_PKG_MGR = process.env.NPQ_PKG_MGR
|
|
23
|
+
delete process.env.NPQ_PKG_MGR
|
|
24
|
+
childProcess.spawn.mockClear()
|
|
25
|
+
})
|
|
26
|
+
|
|
27
|
+
afterEach(() => {
|
|
28
|
+
if (originalNPQ_PKG_MGR !== undefined) {
|
|
29
|
+
process.env.NPQ_PKG_MGR = originalNPQ_PKG_MGR
|
|
30
|
+
} else {
|
|
31
|
+
delete process.env.NPQ_PKG_MGR
|
|
32
|
+
}
|
|
33
|
+
})
|
|
34
|
+
|
|
35
|
+
test('package manager process should handle pnpm correctly', async () => {
|
|
36
|
+
process.argv = ['node', 'npq', 'install', 'fastify']
|
|
37
|
+
|
|
38
|
+
// Test that the package manager can spawn pnpm (which would be passed from CLI parsing)
|
|
39
|
+
await packageManager.process('pnpm')
|
|
40
|
+
|
|
41
|
+
expect(childProcess.spawn).toHaveBeenCalledWith('pnpm install fastify', {
|
|
42
|
+
stdio: 'inherit',
|
|
43
|
+
shell: true
|
|
44
|
+
})
|
|
45
|
+
})
|
|
46
|
+
|
|
47
|
+
test('package manager process should handle yarn correctly', async () => {
|
|
48
|
+
process.argv = ['node', 'npq', 'install', 'express', 'lodash']
|
|
49
|
+
|
|
50
|
+
await packageManager.process('yarn')
|
|
51
|
+
|
|
52
|
+
expect(childProcess.spawn).toHaveBeenCalledWith('yarn install express lodash', {
|
|
53
|
+
stdio: 'inherit',
|
|
54
|
+
shell: true
|
|
55
|
+
})
|
|
56
|
+
})
|
|
57
|
+
|
|
58
|
+
test('package manager process should handle various package managers', async () => {
|
|
59
|
+
const packageManagers = ['npm', 'yarn', 'pnpm', 'bun']
|
|
60
|
+
|
|
61
|
+
for (const pm of packageManagers) {
|
|
62
|
+
process.argv = ['node', 'npq', 'install', 'test-package']
|
|
63
|
+
|
|
64
|
+
await packageManager.process(pm)
|
|
65
|
+
|
|
66
|
+
expect(childProcess.spawn).toHaveBeenCalledWith(`${pm} install test-package`, {
|
|
67
|
+
stdio: 'inherit',
|
|
68
|
+
shell: true
|
|
69
|
+
})
|
|
70
|
+
|
|
71
|
+
childProcess.spawn.mockClear()
|
|
72
|
+
}
|
|
73
|
+
})
|
|
74
|
+
|
|
75
|
+
test('should demonstrate NPQ_PKG_MGR environment variable precedence logic', () => {
|
|
76
|
+
// This test documents the specific logic that was implemented:
|
|
77
|
+
// packageManager: process.env.NPQ_PKG_MGR || values.packageManager || values.pkgMgr || 'npm'
|
|
78
|
+
|
|
79
|
+
// Test 1: Environment variable takes precedence
|
|
80
|
+
process.env.NPQ_PKG_MGR = 'pnpm'
|
|
81
|
+
const values1 = { packageManager: 'yarn', pkgMgr: 'npm' }
|
|
82
|
+
const result1 = process.env.NPQ_PKG_MGR || values1.packageManager || values1.pkgMgr || 'npm'
|
|
83
|
+
expect(result1).toBe('pnpm')
|
|
84
|
+
|
|
85
|
+
// Test 2: Falls back to packageManager when env var not set
|
|
86
|
+
delete process.env.NPQ_PKG_MGR
|
|
87
|
+
const values2 = { packageManager: 'yarn', pkgMgr: 'npm' }
|
|
88
|
+
const result2 = process.env.NPQ_PKG_MGR || values2.packageManager || values2.pkgMgr || 'npm'
|
|
89
|
+
expect(result2).toBe('yarn')
|
|
90
|
+
|
|
91
|
+
// Test 3: Falls back to pkgMgr when env var and packageManager not set
|
|
92
|
+
delete process.env.NPQ_PKG_MGR
|
|
93
|
+
const values3 = { pkgMgr: 'pnpm' }
|
|
94
|
+
const result3 = process.env.NPQ_PKG_MGR || values3.packageManager || values3.pkgMgr || 'npm'
|
|
95
|
+
expect(result3).toBe('pnpm')
|
|
96
|
+
|
|
97
|
+
// Test 4: Falls back to npm default when nothing is set
|
|
98
|
+
delete process.env.NPQ_PKG_MGR
|
|
99
|
+
const values4 = {}
|
|
100
|
+
const result4 = process.env.NPQ_PKG_MGR || values4.packageManager || values4.pkgMgr || 'npm'
|
|
101
|
+
expect(result4).toBe('npm')
|
|
102
|
+
|
|
103
|
+
// Test 5: Empty string environment variable falls back to CLI options
|
|
104
|
+
process.env.NPQ_PKG_MGR = ''
|
|
105
|
+
const values5 = { packageManager: 'yarn' }
|
|
106
|
+
const result5 = process.env.NPQ_PKG_MGR || values5.packageManager || values5.pkgMgr || 'npm'
|
|
107
|
+
expect(result5).toBe('yarn')
|
|
108
|
+
})
|
|
109
|
+
})
|
|
@@ -53,9 +53,7 @@ test('package manager spawns successfully when provided array of packages to han
|
|
|
53
53
|
await packageManager.process('npm')
|
|
54
54
|
expect(childProcess.spawn).toHaveBeenCalled()
|
|
55
55
|
expect(childProcess.spawn.mock.calls.length).toBe(1)
|
|
56
|
-
expect(childProcess.spawn.mock.calls[0][0]).
|
|
57
|
-
|
|
58
|
-
expect(childProcess.spawn.mock.calls[0][1]).toEqual(['install', 'semver', 'express'])
|
|
56
|
+
expect(childProcess.spawn.mock.calls[0][0]).toEqual('npm install semver express')
|
|
59
57
|
childProcess.spawn.mockReset()
|
|
60
58
|
})
|
|
61
59
|
|
|
@@ -72,8 +70,24 @@ test("package manager spawns successfully and ignore npq's own internal commands
|
|
|
72
70
|
await packageManager.process('npm')
|
|
73
71
|
expect(childProcess.spawn).toHaveBeenCalled()
|
|
74
72
|
expect(childProcess.spawn.mock.calls.length).toBe(1)
|
|
75
|
-
expect(childProcess.spawn.mock.calls[0][0]).
|
|
73
|
+
expect(childProcess.spawn.mock.calls[0][0]).toEqual('npm install semver express')
|
|
74
|
+
childProcess.spawn.mockReset()
|
|
75
|
+
})
|
|
76
|
+
|
|
77
|
+
test('package manager spawns with yarn when provided as parameter', async () => {
|
|
78
|
+
process.argv = ['node', 'script name', 'install', 'express']
|
|
79
|
+
await packageManager.process('yarn')
|
|
80
|
+
expect(childProcess.spawn).toHaveBeenCalled()
|
|
81
|
+
expect(childProcess.spawn.mock.calls.length).toBe(1)
|
|
82
|
+
expect(childProcess.spawn.mock.calls[0][0]).toEqual('yarn install express')
|
|
83
|
+
childProcess.spawn.mockReset()
|
|
84
|
+
})
|
|
76
85
|
|
|
77
|
-
|
|
86
|
+
test('package manager spawns with pnpm when provided as parameter', async () => {
|
|
87
|
+
process.argv = ['node', 'script name', 'install', 'lodash']
|
|
88
|
+
await packageManager.process('pnpm')
|
|
89
|
+
expect(childProcess.spawn).toHaveBeenCalled()
|
|
90
|
+
expect(childProcess.spawn.mock.calls.length).toBe(1)
|
|
91
|
+
expect(childProcess.spawn.mock.calls[0][0]).toEqual('pnpm install lodash')
|
|
78
92
|
childProcess.spawn.mockReset()
|
|
79
93
|
})
|
package/lib/cli.js
CHANGED
|
@@ -109,7 +109,7 @@ curated by Liran Tal at https://github.com/lirantal/npq`)
|
|
|
109
109
|
|
|
110
110
|
return {
|
|
111
111
|
packages: normalizedPackages,
|
|
112
|
-
packageManager: values.packageManager || values.pkgMgr || 'npm',
|
|
112
|
+
packageManager: values.packageManager || values.pkgMgr || process.env.NPQ_PKG_MGR || 'npm',
|
|
113
113
|
dryRun: values['dry-run'] || false,
|
|
114
114
|
plain: values.plain || false
|
|
115
115
|
}
|
package/lib/packageManager.js
CHANGED
|
@@ -12,7 +12,6 @@ class packageManager {
|
|
|
12
12
|
|
|
13
13
|
static spawnPackageManager(packageManagerOption) {
|
|
14
14
|
let args = []
|
|
15
|
-
|
|
16
15
|
args = args.concat(process.argv.slice(2)).filter((item) => {
|
|
17
16
|
switch (item) {
|
|
18
17
|
case '--packageManager':
|
|
@@ -24,7 +23,12 @@ class packageManager {
|
|
|
24
23
|
}
|
|
25
24
|
})
|
|
26
25
|
|
|
27
|
-
|
|
26
|
+
let cmd = `${packageManagerOption}`
|
|
27
|
+
if (args.length > 0) {
|
|
28
|
+
cmd += ` ${args.join(' ')}`
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
const child = childProcess.spawn(cmd, {
|
|
28
32
|
stdio: 'inherit',
|
|
29
33
|
shell: true
|
|
30
34
|
})
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "npq",
|
|
3
|
-
"version": "3.
|
|
3
|
+
"version": "3.10.1",
|
|
4
4
|
"description": "marshall your npm/npm package installs with high quality and class 🎖",
|
|
5
5
|
"bin": {
|
|
6
6
|
"npq": "./bin/npq.js",
|
|
@@ -72,10 +72,10 @@
|
|
|
72
72
|
"collectCoverage": true,
|
|
73
73
|
"coverageThreshold": {
|
|
74
74
|
"global": {
|
|
75
|
-
"branches":
|
|
76
|
-
"functions":
|
|
77
|
-
"lines":
|
|
78
|
-
"statements":
|
|
75
|
+
"branches": 75,
|
|
76
|
+
"functions": 75,
|
|
77
|
+
"lines": 75,
|
|
78
|
+
"statements": 75
|
|
79
79
|
},
|
|
80
80
|
"scripts/*": {
|
|
81
81
|
"branches": 60,
|
package/.github/mergify.yml
DELETED
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
queue_rules:
|
|
2
|
-
- name: Snyk PRs Queue
|
|
3
|
-
conditions:
|
|
4
|
-
- "check-success ~= .*"
|
|
5
|
-
|
|
6
|
-
pull_request_rules:
|
|
7
|
-
- name: Automatic merge Snyk PRs on Status Checks passing
|
|
8
|
-
conditions:
|
|
9
|
-
- title~=^\[Snyk\]
|
|
10
|
-
- head~=^snyk-fix
|
|
11
|
-
- base=main
|
|
12
|
-
actions:
|
|
13
|
-
queue:
|
|
14
|
-
name: "Snyk PRs Queue"
|
|
15
|
-
label:
|
|
16
|
-
add:
|
|
17
|
-
- "auto-merge"
|