Skip to content

Commit 82cb863

Browse files
TzuH-Hsuclaude
andauthored
fix: switch off syft's Actions catalogers instead of excluding .github (#32)
## Summary 拿上游 github-project-os v0.5.6(`5d22ae6`)對我們 09-25 回報的回應: - **`Makefile`**:`lint-licenses` 和 `sbom` 不再用 `--exclude './.github/**'`,改用 `--select-catalogers '-github-actions-usage-cataloger,-github-action-workflow-usage-cataloger'`,註解同步改寫。整個排除 `.github/` 會把 `.github/actions/` 底下 local action 的 npm 依賴也丟掉,而那些是會交付的。本 repo 目前沒有 `.github/actions/`,這次是預防將來漏掉。 - **`skills/release-management`、`skills/validation-ladder`**:整檔換成 v0.5.6(原本跟 v0.5.5 byte 相同)。內容包括 `Release-As` 要放在 squash commit 最後一行(多 commit 的 PR 要用 `gh pr merge --squash --body-file`),以及 L4 授權檢查範本的 cataloger 寫法和「用完整 purl 跳過自家套件」。 ## Related issue Closes #31 ## Validation - [x] L0 static — `make lint` - [x] L1 unit — `make test` - [x] L2 integration — 用 syft 1.52.0 實跑:`make lint-licenses` rc=0,`make sbom` rc=0,SBOM 裡 github-action 元件 0 個。另外用 fixture 比對兩種寫法:`--exclude` 掃出 `[]`;`--select-catalogers` 掃出 local action 的 `dep(npm)`,workflow 用到的 action 沒列入 - [ ] L3 e2e / preview — n/a ## Risk / rollback ```text RISK: none at runtime — both targets are still outside lint/ci-pr Rollback: revert this squash commit ``` ## Checklist - [x] Conventional Commit PR title (`<type>: <description>`) - [x] Linked issue using "Closes #N" - [x] No secrets, no `*.local.md` files committed - [x] Documentation updated where affected Co-authored-by: Claude Opus 5.5 <noreply@anthropic.com>
1 parent 0ca3830 commit 82cb863

3 files changed

Lines changed: 27 additions & 10 deletions

File tree

‎Makefile‎

Lines changed: 6 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -76,15 +76,17 @@ maintenance: ## Everything the weekly maintenance workflow runs (network; not in
7676
# Not wired into `lint`/`ci-pr` yet: there are no dependencies to scan, and syft
7777
# is not in the pinned CI tool list. Wire both in (scripts/install-ci-tools.sh +
7878
# the `lint` aggregate) with the first real dependency.
79-
# Both targets exclude .github/: syft catalogues the workflows' GitHub Actions,
80-
# which are CI tooling, not delivered with the product, and carry no licence data.
79+
# Both targets switch off syft's two GitHub Actions catalogers: the actions a
80+
# workflow or action.yml references are CI tooling, not delivered with the
81+
# product, and carry no licence data. Do not `--exclude './.github/**'` instead:
82+
# that also drops the npm dependencies of a local action under .github/actions/.
8183

8284
lint-licenses: ## Reject copyleft dependencies (GPL/AGPL/LGPL/SSPL/...)
8385
@command -v syft >/dev/null 2>&1 || { echo "install: brew install syft"; exit 1; }
84-
set -o pipefail; syft dir:. -o json -q --exclude './.github/**' | python3 scripts/check-licenses.py
86+
set -o pipefail; syft dir:. -o json -q --select-catalogers '-github-actions-usage-cataloger,-github-action-workflow-usage-cataloger' | python3 scripts/check-licenses.py
8587

8688
sbom: ## Write SPDX SBOM + readable third-party licence list to dist/
8789
@command -v syft >/dev/null 2>&1 || { echo "install: brew install syft"; exit 1; }
8890
@mkdir -p dist
89-
syft dir:. -q --exclude './.github/**' -o spdx-json=dist/sbom.spdx.json -o table=dist/third-party-licences.txt
91+
syft dir:. -q --select-catalogers '-github-actions-usage-cataloger,-github-action-workflow-usage-cataloger' -o spdx-json=dist/sbom.spdx.json -o table=dist/third-party-licences.txt
9092
@echo "wrote dist/sbom.spdx.json and dist/third-party-licences.txt"

‎skills/release-management/SKILL.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ A release must never ship from an unverified state, and version/changelog bookke
1717
2. **A human merges the release PR — never auto-merge.** The branch ruleset requires the `ci` status check to be green before merge is even possible; this is the mitigation for the fact that both CI and release-please trigger on `push:main`, so an unverified commit could otherwise reach the release PR.
1818
3. Merging the release PR cuts the git tag and GitHub Release automatically.
1919
4. The maintainer then edits the published release notes to add a short, hand-written TLDR **above** the generated changelog. Release notes have non-technical readers — the generated bullet list alone is not the message.
20-
2. **Version bump is derived, not chosen.** It comes from Conventional Commit types accumulated since the last release: `fix` → patch, `feat` → minor, any commit with `!` or a `BREAKING CHANGE:` footer → major. This is exactly why commit type discipline matters (see `CONTRIBUTING.md`). One exception: a change that adopters must pick up but that genuinely is a hidden type (a new CI gate under `ci`) produces no release on its own; then a `Release-As: X.Y.Z` footer in the PR body cuts one, and X.Y.Z is always the current version plus one patch — if you want more than a patch, the type was wrong. Before the first release there is no current version: that release is numbered by `initial-version` in `release-please-config.json` (see `docs/setup/bootstrap.md`), so a footer there names that version. Use the footer only when nothing is already waiting: the footer overrides release-please's computed version for the whole range since the last tag, so with a release PR already open (an unreleased `feat` or `fix`) a patch footer would under-version that release — merge it first, or leave the footer out and let the hidden-type change ride along. The footer goes on the PR that is the change, not on a follow-up: hidden-type commits are omitted from the generated changelog except the one that carries `Release-As`, which release-please always renders under its type's section, so a footer on a follow-up makes the changelog name the follow-up and omit the change.
20+
2. **Version bump is derived, not chosen.** It comes from Conventional Commit types accumulated since the last release: `fix` → patch, `feat` → minor, any commit with `!` or a `BREAKING CHANGE:` footer → major. This is exactly why commit type discipline matters (see `CONTRIBUTING.md`). One exception: a change that adopters must pick up but that genuinely is a hidden type (a new CI gate under `ci`) produces no release on its own; then a `Release-As: X.Y.Z` footer cuts one — it must be the last line of the squash commit, so on a PR with more than one commit merge with `gh pr merge <n> --squash --body-file <file>`: the file is the PR body, then the branch's `Co-authored-by:` trailers (an explicit body replaces the ones GitHub would add), then `Release-As: X.Y.Z` as the last line. Left to itself, GitHub appends a `---------` line and those trailers after the body, and release-please no longer reads the footer — that is why v0.5.6 first failed to cut — and X.Y.Z is always the current version plus one patch — if you want more than a patch, the type was wrong. Before the first release there is no current version: that release is numbered by `initial-version` in `release-please-config.json` (see `docs/setup/bootstrap.md`), so a footer there names that version. Use the footer only when nothing is already waiting: the footer overrides release-please's computed version for the whole range since the last tag, so with a release PR already open (an unreleased `feat` or `fix`) a patch footer would under-version that release — merge it first, or leave the footer out and let the hidden-type change ride along. The footer goes on the PR that is the change, not on a follow-up: hidden-type commits are omitted from the generated changelog except the one that carries `Release-As`, which release-please always renders under its type's section, so a footer on a follow-up makes the changelog name the follow-up and omit the change.
2121
3. **Pre-1.0 semantics**: a minor bump may contain breaking changes. Don't assume `0.x` minor bumps are safe to blindly consume — read the changelog.
2222
4. **Documented alternative — manual tag-first.** Use this instead of release-please when release cadence is near-zero or the team wants zero release automation:
2323
1. Decide the version by hand.

‎skills/validation-ladder/SKILL.md‎

Lines changed: 20 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,9 @@ lint-licenses: ## L4 - reject copyleft dependencies (adopter-defined)
7272
@command -v syft >/dev/null 2>&1 || { echo "FAIL: syft not installed, cannot verify L4"; exit 1; }
7373
@found=""; for m in $(MANIFESTS); do [ -s "$$m" ] && found=1; done; \
7474
[ -n "$$found" ] || { echo "FAIL: none of ($(MANIFESTS)) present - refusing to report a clean scan of nothing"; exit 1; }
75-
syft dir:. -o json -q > $(SBOM_TMP)
75+
syft dir:. -o json -q \
76+
--select-catalogers '-github-actions-usage-cataloger,-github-action-workflow-usage-cataloger' \
77+
> $(SBOM_TMP)
7678
scripts/check-licenses.py < $(SBOM_TMP)
7779
```
7880

@@ -85,10 +87,23 @@ fails (`set -o pipefail` is the alternative, but it is not portable to every
8587
adopter's `SHELL`). And an SBOM artifact is subject to the same rule as the
8688
check — an empty one is worse than none, because it looks like evidence.
8789

88-
If you write the checker yourself, two things fail open by default: an SPDX `OR`
89-
is a *choice*, so `MIT OR GPL-2.0` must pass rather than fail, and `NOASSERTION`
90-
or an empty licence field must fail loudly, since that is what scanners emit for
91-
every package they could not resolve.
90+
The `--select-catalogers` line is load-bearing too: syft catalogs every action
91+
a workflow or a local `action.yml` references as a `github-action` package with
92+
no licence, so without it a checker written as below fails on every repository
93+
made from this template. It switches off exactly those two catalogers; do not
94+
`--exclude './.github/**'` instead, which also drops the npm dependencies of a
95+
local JavaScript action under `.github/actions/` — the ones you do ship.
96+
97+
If you write the checker yourself, three things decide whether it is honest. An
98+
SPDX `OR` is a *choice*, so `MIT OR GPL-2.0` must pass rather than fail.
99+
`NOASSERTION` or an empty licence field must fail loudly, since that is what
100+
scanners emit for every package they could not resolve. And syft lists the
101+
packages you author — the root `package.json`, a local action's own manifest —
102+
as packages too, carrying whatever the manifest declares (`UNLICENSED` for a
103+
private application, or nothing). Skip exactly those, by their full purl
104+
(ecosystem, name and version: `pkg:npm/my-app@1.0.0`), never by bare name — a
105+
third-party package from another ecosystem can share the name. Do not loosen
106+
the empty-licence rule to let them through.
92107

93108
## Pitfalls
94109

0 commit comments

Comments
 (0)