periapsis 1.0.4 → 1.0.6

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/.codex ADDED
File without changes
@@ -63,7 +63,7 @@ jobs:
63
63
  exit 1
64
64
  fi
65
65
 
66
- VIOLATION_COUNT=$(node -e "const fs=require('fs');const d=JSON.parse(fs.readFileSync('sbom-violations.json','utf8'));const count=Array.isArray(d)?d.length:(Array.isArray(d?.violations)?d.violations.length:0);process.stdout.write(String(count));")
66
+ VIOLATION_COUNT="$(node -e 'const fs = require("fs"); const data = JSON.parse(fs.readFileSync("sbom-violations.json", "utf8")); const count = Array.isArray(data) ? data.length : Array.isArray(data?.violations) ? data.violations.length : 0; process.stdout.write(String(count));')"
67
67
  echo "Detected violations: ${VIOLATION_COUNT}"
68
68
 
69
69
  if [ "${VIOLATION_COUNT}" -gt 0 ]; then
@@ -0,0 +1,168 @@
1
+ name: Prepare Package Release
2
+
3
+ on:
4
+ workflow_dispatch:
5
+ inputs:
6
+ release_type:
7
+ description: Semver increment to prepare
8
+ required: true
9
+ type: choice
10
+ default: patch
11
+ options:
12
+ - patch
13
+ - minor
14
+ - major
15
+
16
+ permissions:
17
+ contents: read
18
+
19
+ concurrency:
20
+ group: prepare-package-release
21
+ cancel-in-progress: false
22
+
23
+ jobs:
24
+ authorize-release:
25
+ name: Authorize Release
26
+ runs-on: ubuntu-latest
27
+ steps:
28
+ - name: Require main branch
29
+ run: |
30
+ if [ "${GITHUB_REF_NAME}" != "main" ]; then
31
+ echo "Manual release preparation is only allowed from main."
32
+ exit 1
33
+ fi
34
+
35
+ - name: Require repository admin permission
36
+ uses: actions/github-script@v7
37
+ with:
38
+ script: |
39
+ const owner = context.repo.owner;
40
+ const repo = context.repo.repo;
41
+ const actor = context.actor;
42
+
43
+ if (owner.toLowerCase() === actor.toLowerCase()) {
44
+ core.info(`Actor ${actor} matches the repository owner and is treated as admin.`);
45
+ return;
46
+ }
47
+
48
+ const response = await github.rest.repos.getCollaboratorPermissionLevel({
49
+ owner,
50
+ repo,
51
+ username: actor,
52
+ });
53
+
54
+ const { permission, role_name: roleName } = response.data;
55
+ core.info(`Actor ${actor} permission=${permission}, role_name=${roleName}`);
56
+
57
+ if (permission !== "admin" && roleName !== "admin") {
58
+ core.setFailed(`Only repository admins can prepare package releases. ${actor} has ${roleName || permission}.`);
59
+ }
60
+
61
+ prepare-release:
62
+ name: Prepare Release Pull Request
63
+ needs: authorize-release
64
+ runs-on: ubuntu-latest
65
+ permissions:
66
+ # Required because this workflow pushes a release branch and opens a PR.
67
+ contents: write
68
+ pull-requests: write
69
+ steps:
70
+ - name: Checkout
71
+ uses: actions/checkout@v4
72
+ with:
73
+ fetch-depth: 0
74
+
75
+ - name: Setup Node
76
+ uses: actions/setup-node@v4
77
+ with:
78
+ node-version: 24
79
+ cache: npm
80
+
81
+ - name: Install dependencies
82
+ run: npm ci
83
+
84
+ - name: Run automated test suite
85
+ run: npm run test:ci
86
+
87
+ - name: Bump package version
88
+ run: npm version "${{ inputs.release_type }}" --no-git-tag-version
89
+
90
+ - name: Read new version
91
+ id: version
92
+ run: |
93
+ VERSION="$(node -e 'process.stdout.write(require("./package.json").version)')"
94
+ echo "value=${VERSION}" >> "$GITHUB_OUTPUT"
95
+ echo "branch=release/v${VERSION}" >> "$GITHUB_OUTPUT"
96
+
97
+ - name: Ensure release branch is not already open
98
+ env:
99
+ RELEASE_BRANCH: ${{ steps.version.outputs.branch }}
100
+ run: |
101
+ if git ls-remote --exit-code --heads origin "${RELEASE_BRANCH}" >/dev/null 2>&1; then
102
+ echo "Release branch ${RELEASE_BRANCH} already exists on origin."
103
+ exit 1
104
+ fi
105
+
106
+ - name: Create release branch
107
+ env:
108
+ RELEASE_BRANCH: ${{ steps.version.outputs.branch }}
109
+ run: git switch -c "${RELEASE_BRANCH}"
110
+
111
+ - name: Configure git author
112
+ run: |
113
+ git config user.name "github-actions[bot]"
114
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
115
+
116
+ - name: Commit release version
117
+ env:
118
+ VERSION: ${{ steps.version.outputs.value }}
119
+ run: |
120
+ git add package.json package-lock.json
121
+ git commit -m "chore(release): v${VERSION}"
122
+
123
+ - name: Push release branch
124
+ env:
125
+ RELEASE_BRANCH: ${{ steps.version.outputs.branch }}
126
+ run: git push --set-upstream origin "${RELEASE_BRANCH}"
127
+
128
+ - name: Open release pull request
129
+ id: pull-request
130
+ uses: actions/github-script@v7
131
+ env:
132
+ RELEASE_BRANCH: ${{ steps.version.outputs.branch }}
133
+ VERSION: ${{ steps.version.outputs.value }}
134
+ with:
135
+ script: |
136
+ const owner = context.repo.owner;
137
+ const repo = context.repo.repo;
138
+ const head = process.env.RELEASE_BRANCH;
139
+ const version = process.env.VERSION;
140
+
141
+ const pr = await github.rest.pulls.create({
142
+ owner,
143
+ repo,
144
+ title: `chore(release): v${version}`,
145
+ head,
146
+ base: "main",
147
+ body: [
148
+ `Prepares \`v${version}\` for npm publication.`,
149
+ "",
150
+ "After this pull request merges into `main`, the publish workflow will release the package to npm.",
151
+ ].join("\n"),
152
+ });
153
+
154
+ core.setOutput("url", pr.data.html_url);
155
+
156
+ - name: Write job summary
157
+ env:
158
+ VERSION: ${{ steps.version.outputs.value }}
159
+ RELEASE_BRANCH: ${{ steps.version.outputs.branch }}
160
+ PR_URL: ${{ steps.pull-request.outputs.url }}
161
+ run: |
162
+ {
163
+ echo "## Release PR created"
164
+ echo
165
+ echo "- Version: \`${VERSION}\`"
166
+ echo "- Release branch: \`${RELEASE_BRANCH}\`"
167
+ echo "- Pull request: ${PR_URL}"
168
+ } >> "$GITHUB_STEP_SUMMARY"
@@ -1,86 +1,75 @@
1
1
  name: Publish Package
2
2
 
3
3
  on:
4
+ push:
5
+ branches:
6
+ - main
7
+ paths:
8
+ - package.json
9
+ - package-lock.json
4
10
  workflow_dispatch:
5
- inputs:
6
- release_type:
7
- description: Semver increment to publish
8
- required: true
9
- type: choice
10
- default: patch
11
- options:
12
- - patch
13
- - minor
14
- - major
15
11
 
16
12
  permissions:
17
- contents: read
13
+ contents: write
14
+ id-token: write
18
15
 
19
16
  concurrency:
20
17
  group: publish-package
21
18
  cancel-in-progress: false
22
19
 
23
20
  jobs:
24
- authorize-release:
25
- name: Authorize Release
26
- runs-on: ubuntu-latest
27
- steps:
28
- - name: Require default branch
29
- env:
30
- DEFAULT_BRANCH: ${{ github.event.repository.default_branch }}
31
- run: |
32
- if [ "${GITHUB_REF_NAME}" != "${DEFAULT_BRANCH}" ]; then
33
- echo "Manual publishing is only allowed from ${DEFAULT_BRANCH}."
34
- exit 1
35
- fi
36
-
37
- - name: Require repository admin permission
38
- uses: actions/github-script@v7
39
- with:
40
- script: |
41
- const owner = context.repo.owner;
42
- const repo = context.repo.repo;
43
- const actor = context.actor;
44
-
45
- if (owner.toLowerCase() === actor.toLowerCase()) {
46
- core.info(`Actor ${actor} matches the repository owner and is treated as admin.`);
47
- return;
48
- }
49
-
50
- const response = await github.rest.repos.getCollaboratorPermissionLevel({
51
- owner,
52
- repo,
53
- username: actor,
54
- });
55
-
56
- const { permission, role_name: roleName } = response.data;
57
- core.info(`Actor ${actor} permission=${permission}, role_name=${roleName}`);
58
-
59
- if (permission !== "admin" && roleName !== "admin") {
60
- core.setFailed(`Only repository admins can publish packages. ${actor} has ${roleName || permission}.`);
61
- }
62
-
63
21
  publish-package:
64
22
  name: Publish Package
65
- needs: authorize-release
66
23
  runs-on: ubuntu-latest
67
24
  environment:
68
- # Configure this environment in GitHub with required reviewers for an
69
- # extra approval gate, and use the same name in npm trusted publisher
70
- # settings if you want npm to bind publishing to this environment.
25
+ # Use the same environment name in npm trusted publisher settings if you
26
+ # want npm to bind publishing to this protected environment.
71
27
  name: package-publish
72
- permissions:
73
- # Required because this workflow commits the bumped version, creates a
74
- # release tag, and pushes both back to the repository.
75
- contents: write
76
- id-token: write
77
28
  steps:
78
29
  - name: Checkout
79
30
  uses: actions/checkout@v4
80
31
  with:
81
32
  fetch-depth: 0
82
33
 
34
+ - name: Detect release version
35
+ id: release
36
+ env:
37
+ BEFORE_SHA: ${{ github.event.before }}
38
+ EVENT_NAME: ${{ github.event_name }}
39
+ REF_NAME: ${{ github.ref_name }}
40
+ run: |
41
+ VERSION="$(node -e 'process.stdout.write(require("./package.json").version)')"
42
+ PACKAGE_NAME="$(node -e 'process.stdout.write(require("./package.json").name)')"
43
+ echo "version=${VERSION}" >> "$GITHUB_OUTPUT"
44
+ echo "package_name=${PACKAGE_NAME}" >> "$GITHUB_OUTPUT"
45
+
46
+ if [ "${EVENT_NAME}" = "workflow_dispatch" ]; then
47
+ if [ "${REF_NAME}" != "main" ]; then
48
+ echo "Manual publishing is only allowed from main."
49
+ exit 1
50
+ fi
51
+
52
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
53
+ exit 0
54
+ fi
55
+
56
+ if [ -z "${BEFORE_SHA}" ] || [ "${BEFORE_SHA}" = "0000000000000000000000000000000000000000" ]; then
57
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
58
+ exit 0
59
+ fi
60
+
61
+ PREVIOUS_VERSION="$(git show "${BEFORE_SHA}:package.json" | node -e 'let data=""; process.stdin.on("data", chunk => data += chunk); process.stdin.on("end", () => process.stdout.write(JSON.parse(data).version));')"
62
+
63
+ if [ "${PREVIOUS_VERSION}" = "${VERSION}" ]; then
64
+ echo "Version is unchanged at ${VERSION}; skipping publish."
65
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
66
+ else
67
+ echo "Version changed from ${PREVIOUS_VERSION} to ${VERSION}."
68
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
69
+ fi
70
+
83
71
  - name: Setup Node
72
+ if: steps.release.outputs.should_publish == 'true'
84
73
  uses: actions/setup-node@v4
85
74
  with:
86
75
  node-version: 24
@@ -88,36 +77,57 @@ jobs:
88
77
  registry-url: https://registry.npmjs.org
89
78
 
90
79
  - name: Install dependencies
80
+ if: steps.release.outputs.should_publish == 'true'
91
81
  run: npm ci
92
82
 
93
83
  - name: Run automated test suite
84
+ if: steps.release.outputs.should_publish == 'true'
94
85
  run: npm run test:ci
95
86
 
96
- - name: Bump package version
97
- run: npm version "${{ inputs.release_type }}" --no-git-tag-version
98
-
99
- - name: Read new version
100
- id: version
87
+ - name: Check whether version already exists on npm
88
+ if: steps.release.outputs.should_publish == 'true'
89
+ id: npm-check
90
+ env:
91
+ PACKAGE_NAME: ${{ steps.release.outputs.package_name }}
92
+ VERSION: ${{ steps.release.outputs.version }}
101
93
  run: |
102
- echo "value=$(node -p 'require(\"./package.json\").version')" >> "$GITHUB_OUTPUT"
94
+ if npm view "${PACKAGE_NAME}@${VERSION}" version >/dev/null 2>&1; then
95
+ echo "should_publish=false" >> "$GITHUB_OUTPUT"
96
+ echo "Version ${VERSION} is already published to npm."
97
+ else
98
+ echo "should_publish=true" >> "$GITHUB_OUTPUT"
99
+ echo "Version ${VERSION} is not yet published to npm."
100
+ fi
103
101
 
104
- - name: Configure git author
105
- run: |
106
- git config user.name "github-actions[bot]"
107
- git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
102
+ - name: Publish to npm with trusted publishing
103
+ if: steps.release.outputs.should_publish == 'true' && steps.npm-check.outputs.should_publish == 'true'
104
+ run: npm publish
108
105
 
109
- - name: Commit release version
106
+ - name: Tag published version
107
+ if: steps.release.outputs.should_publish == 'true' && steps.npm-check.outputs.should_publish == 'true'
110
108
  env:
111
- VERSION: ${{ steps.version.outputs.value }}
109
+ VERSION: ${{ steps.release.outputs.version }}
112
110
  run: |
113
- git add package.json package-lock.json
114
- git commit -m "chore(release): v${VERSION}"
115
- git tag "v${VERSION}"
111
+ if git rev-parse "v${VERSION}" >/dev/null 2>&1; then
112
+ echo "Tag v${VERSION} already exists locally."
113
+ exit 0
114
+ fi
116
115
 
117
- - name: Publish to npm with trusted publishing
118
- run: npm publish
116
+ git config user.name "github-actions[bot]"
117
+ git config user.email "41898282+github-actions[bot]@users.noreply.github.com"
118
+ git tag "v${VERSION}"
119
+ git push origin "refs/tags/v${VERSION}"
119
120
 
120
- - name: Push release commit and tag
121
+ - name: Write job summary
121
122
  env:
122
- VERSION: ${{ steps.version.outputs.value }}
123
- run: git push --atomic origin "HEAD:${GITHUB_REF_NAME}" "refs/tags/v${VERSION}"
123
+ VERSION: ${{ steps.release.outputs.version }}
124
+ SHOULD_PUBLISH: ${{ steps.release.outputs.should_publish }}
125
+ NPM_PUBLISH: ${{ steps.npm-check.outputs.should_publish }}
126
+ run: |
127
+ {
128
+ echo "## Publish summary"
129
+ echo
130
+ echo "- Version: \`${VERSION}\`"
131
+ echo "- Trigger requested publish: \`${SHOULD_PUBLISH:-false}\`"
132
+ echo "- npm publish executed: \`${NPM_PUBLISH:-false}\`"
133
+ } >> "$GITHUB_STEP_SUMMARY"
package/README.md CHANGED
@@ -9,6 +9,8 @@ npm install
9
9
  npx periapsis --violations-out sbom-violations.json
10
10
  ```
11
11
 
12
+ When working on the `periapsis` repository itself, prefer `node ./bin/periapsis.mjs ...` or `npm run policy:check` so you are exercising the checked-out CLI rather than any previously published copy.
13
+
12
14
  Initialize governed policy files:
13
15
 
14
16
  ```sh
@@ -312,6 +314,8 @@ Example:
312
314
  - run: npx periapsis --violations-out sbom-violations.json
313
315
  ```
314
316
 
317
+ If you add an inline `node -e` follow-up check in GitHub Actions, wrap the JavaScript in single quotes. Backticks inside a double-quoted shell string are treated as command substitution by `bash`.
318
+
315
319
  When violations exist, Periapsis exits non-zero and prints deterministic markdown summary suitable for Actions logs.
316
320
 
317
321
  ## Troubleshooting Large Violation Sets
@@ -166,6 +166,10 @@ export function parseArgs(argv) {
166
166
  const args = { _: [] };
167
167
  for (let i = 0; i < argv.length; i++) {
168
168
  const arg = argv[i];
169
+ if (arg === '--') {
170
+ // Ignore npm/npx argument separators so forwarded flags still parse normally.
171
+ continue;
172
+ }
169
173
  if (arg === '--help' || arg === '-h') {
170
174
  args.help = true;
171
175
  continue;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "periapsis",
3
- "version": "1.0.4",
3
+ "version": "1.0.6",
4
4
  "private": false,
5
5
  "type": "module",
6
6
  "scripts": {
@@ -34,8 +34,5 @@
34
34
  "spdx",
35
35
  "cli"
36
36
  ],
37
- "license": "MIT",
38
- "devDependencies": {
39
- "periapsis": "^1.0.1"
40
- }
37
+ "license": "MIT"
41
38
  }
@@ -1,7 +1,7 @@
1
1
  [
2
2
  {
3
3
  "name": "ajv",
4
- "version": "8.17.1",
4
+ "version": "8.18.0",
5
5
  "license": "MIT",
6
6
  "path": "node_modules/ajv",
7
7
  "repository": "ajv-validator/ajv",
@@ -61,19 +61,6 @@
61
61
  "dependencies"
62
62
  ]
63
63
  },
64
- {
65
- "name": "periapsis",
66
- "version": "1.0.1",
67
- "license": "MIT",
68
- "path": "node_modules/periapsis",
69
- "repository": {
70
- "type": "git",
71
- "url": "https://github.com/scfast/periapsis"
72
- },
73
- "dependencyTypes": [
74
- "devDependencies"
75
- ]
76
- },
77
64
  {
78
65
  "name": "require-from-string",
79
66
  "version": "2.0.2",
@@ -96,3 +96,32 @@ test('checker honors policy dependencyTypes when no CLI override is provided', a
96
96
  const sbom = JSON.parse(fs.readFileSync(path.join(cwd, 'sbom-licenses.json'), 'utf8'));
97
97
  assert.deepEqual(sbom.map((entry) => entry.name), ['a']);
98
98
  });
99
+
100
+ test('checker accepts npm forwarded args after a standalone double-dash', async () => {
101
+ const cwd = createTempDir('periapsis-npm-forwarded-args-');
102
+ writePolicyBundle(cwd);
103
+ writeJson(path.join(cwd, 'package.json'), {
104
+ name: 'forwarded-args-app',
105
+ version: '1.0.0',
106
+ dependencies: { a: '1.0.0' }
107
+ });
108
+ writeJson(path.join(cwd, 'package-lock.json'), {
109
+ name: 'forwarded-args-app',
110
+ version: '1.0.0',
111
+ lockfileVersion: 3,
112
+ packages: {
113
+ '': {
114
+ name: 'forwarded-args-app',
115
+ version: '1.0.0',
116
+ dependencies: { a: '1.0.0' }
117
+ },
118
+ 'node_modules/a': { version: '1.0.0', license: 'MIT' }
119
+ }
120
+ });
121
+ fs.mkdirSync(path.join(cwd, 'node_modules', 'a'), { recursive: true });
122
+
123
+ await runCli(cwd, ['--', '--violations-out', 'violations.json', '--quiet']);
124
+
125
+ const violations = JSON.parse(fs.readFileSync(path.join(cwd, 'violations.json'), 'utf8'));
126
+ assert.deepEqual(violations, []);
127
+ });