Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
13 changes: 13 additions & 0 deletions .yamllint.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
extends: default

rules:
comments:
min-spaces-from-content: 1
document-start: disable
indentation: disable
# Checkpoint patterns embed jq expressions that can be long single-line strings;
# raise the limit to accommodate them without splitting and risking regex changes.
line-length:
max: 300
truthy:
allowed-values: ['true', 'false', 'on']
12 changes: 6 additions & 6 deletions skills/github-release/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,12 +26,12 @@ These commands are blocked by hooks. GitHub immutable releases (GA Oct 2025) mak
2. **Determine next version** — based on conventional commits or user input (major/minor/patch)
3. **Bump version files** — update all ecosystem-specific version files consistently
4. **Update CHANGELOG.md** — add release section with date and changes
5. **Create release branch and PR** — `release/vX.Y.Z` branch, open PR for review
6. **After PR merge** — create signed annotated tag: `git tag -s vX.Y.Z -m "vX.Y.Z"`
5. **Create release branch and PR** — `release/vX.Y.Z` branch, open PR for review (always use a PR; branch protection typically blocks direct pushes anyway, and CI gets one last chance to validate)
6. **After PR merge** — `git checkout main && git pull`, then tag `main`'s HEAD: `git tag -s vX.Y.Z -m "vX.Y.Z"`. Tag from `main`, not from the `release/vX.Y.Z` branch tip — see `references/release-process.md` Phase 3.
7. **Push tag** — `git push origin vX.Y.Z` triggers CI workflow
8. **CI publishes release** — with artifacts, checksums, and auto-generated release notes
9. **Overhaul release description** — rewrite the auto-generated notes into a narrative summary in a local file, then apply with `gh release edit vX.Y.Z --notes-file notes.md`. Use `--notes-file` (not `--notes "..."`) so multi-line Markdown doesn't trip over shell quoting.
10. **Do NOT re-run the release workflow after step 9** — many release workflows (e.g. `softprops/action-gh-release`) regenerate the body from the commit log on every run and will overwrite the manual overhaul. If a downstream job (TER publish, artifact upload) needs a retry, use a dedicated dispatcher workflow (see `references/ter-republish.md` for the TYPO3 pattern).
9. **Overhaul release description** — rewrite auto-generated notes into a narrative summary, apply with `gh release edit vX.Y.Z --notes-file notes.md` (use `--notes-file`, not `--notes "..."`, to avoid shell quoting issues with multi-line Markdown)
10. **Do NOT re-run the release workflow after step 9** — many workflows (e.g. `softprops/action-gh-release`) regenerate the body each run and will overwrite the overhaul. For downstream retries (TER publish, artifact upload), use a dedicated dispatcher workflow see `references/ter-republish.md`.

## Commands

Expand All @@ -50,6 +50,6 @@ These commands are blocked by hooks. GitHub immutable releases (GA Oct 2025) mak
- `references/ecosystem-detection.md` — version file patterns per ecosystem
- `references/immutable-releases.md` — GitHub immutable releases and tag burning
- `references/supply-chain-security.md` — SLSA, Sigstore, SBOMs, attestations
- `references/recovery-procedures.md` — burned tags, stuck drafts, version drift, release-body clobbering after manual overhaul, mis-tagged SemVer releases, branch-protection gotchas
- `references/ter-republish.md` — TYPO3 Extension Repository re-publish patterns (workflow_dispatch-only caller, codepoint-safe comment truncation, v-prefix + bare-version tag compatibility)
- `references/recovery-procedures.md` — burned tags, stuck drafts, version drift, release-body clobbering, mis-tagged SemVer releases, branch-protection gotchas
- `references/ter-republish.md` — TYPO3 Extension Repository re-publish patterns
- `references/ci-workflow-templates.md` — CI workflow structure and templates
16 changes: 10 additions & 6 deletions skills/github-release/checkpoints.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -88,14 +88,18 @@ mechanical:

- id: GR-10
type: command
pattern: "gh release view --json assets --jq -e '[.assets[].name] as $n | ($n|any(test(\"\\\\.bundle$\"))) or ($n|any(test(\"\\\\.sig$\")) and ($n|any(test(\"\\\\.pem$\"))))' >/dev/null 2>&1"
pattern: "gh release view --json assets --jq -e '[.assets[].name] as $n | ($n|any(test(\"\\\\.sigstore\\\\.json$\"))) or ($n|any(test(\"\\\\.sigstore$\"))) or ($n|any(test(\"\\\\.bundle$\"))) or ($n|any(test(\"\\\\.sig$\")) and ($n|any(test(\"\\\\.pem$\"))))' >/dev/null 2>&1"
severity: warning
desc: >-
Latest published release should have cosign signature artefacts:
either a modern `.bundle` (Sigstore bundle), or BOTH a detached `.sig`
and matching `.pem` certificate (used by netresearch/skill-repo-skill
release workflow). A lone `.sig` or lone `.pem` is incomplete and
fails. `jq -e` ensures the boolean is reflected in the exit code.
Latest published release should have cosign signature artefacts.
Accepted (in priority order): `.sigstore.json` (preferred — on the
OSSF Scorecard Signed-Releases allowlist), `.sigstore` (also on the
Scorecard allowlist), `.bundle` (legacy cosign default — bytes are
identical to `.sigstore.json`, only the filename differs), or BOTH
a detached `.sig` and matching `.pem` certificate (used by
netresearch/skill-repo-skill release workflow). A lone `.sig` or
lone `.pem` is incomplete and fails. `jq -e` ensures the boolean
is reflected in the exit code.

- id: GR-11
type: command
Expand Down
7 changes: 4 additions & 3 deletions skills/github-release/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -752,14 +752,15 @@
{
"id": 28,
"prompt": "Is the latest release signed with cosign?",
"expected_output": "Query the latest GitHub release assets. Check for cosign signature bundles (.bundle files) and attestation artifacts. Report signing status. If missing, recommend adding cosign signing to the release workflow.",
"expected_output": "Query the latest GitHub release assets. Check for cosign signature bundles (preferred extension `.sigstore.json`, with `.bundle` accepted as legacy) and attestation artifacts. Report signing status. If missing or using only `.bundle`, recommend `.sigstore.json` for OSSF Scorecard Signed-Releases compliance.",
"expectations": [
"Queries release assets",
"Checks for .bundle files",
"Checks for .sigstore.json or .bundle signature files",
"Reports signing status",
"Recommends fix if missing",
"Notes OSSF Scorecard requires .sigstore.json (or another allowlisted extension) — .bundle alone scores 0/10",
"Does NOT: Assume signing exists without checking",
"Does NOT: Skip bundle file inspection"
"Does NOT: Skip signature file inspection"
],
"assertions": [
{
Expand Down
18 changes: 14 additions & 4 deletions skills/github-release/references/release-process.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,15 +45,25 @@ The hooks in this repository block `gh release create` and `gh release delete` t
After the PR is merged into main:

```
1. git checkout main && git pull
2. git tag -s vX.Y.Z -m "vX.Y.Z"
3. git push origin vX.Y.Z
1. git checkout main && git pull # advance to main's post-merge HEAD
2. git tag -s vX.Y.Z -m "vX.Y.Z" # tags main's HEAD
3. git push origin vX.Y.Z # release orchestrator picks it up
```

The tag MUST be:
- **Annotated** (`-a` or `-s`), never lightweight
- **Signed** (`-s` for GPG/SSH signing) — required for SLSA L1+
- **On the merge commit** — not on the branch, not on an older commit
- **On `main`'s HEAD after the PR merges** — not on the `release/vX.Y.Z` branch tip, not on an older commit

**Why `main`'s post-merge HEAD and not the release branch tip:** depending on the project's merge strategy, `main`'s HEAD after merge is one of:

- The original branch-tip commit, if the PR was fast-forwarded;
- A new squash commit, if the PR was squash-merged;
- A new merge commit, if the PR was merge-committed.

The tag must point to whatever is now the tip of `main` — that is what consumers will check out, what CI will build artifacts from, and what shows up as "the release commit" on the GitHub release page. With squash- or merge-commit strategies, the `release/vX.Y.Z` branch tip is *not* on `main`'s first-parent history, so tagging it produces a tag that doesn't correspond to any commit on `main`.

In practice: do NOT `git tag` from inside the worktree on the `release/vX.Y.Z` branch. Switch to `main`, pull, then tag — the steps above already enforce this order.

### Phase 4: CI Release Workflow

Expand Down
38 changes: 37 additions & 1 deletion skills/github-release/references/supply-chain-security.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,15 +35,51 @@ GitHub Actions natively supports SLSA L1 via `actions/attest-build-provenance`.

```yaml
- uses: sigstore/cosign-installer@v3
- run: cosign sign-blob --yes --oidc-issuer https://token.actions.githubusercontent.com artifact.tar.gz
- run: |
cosign sign-blob --yes \
--oidc-issuer https://token.actions.githubusercontent.com \
--bundle artifact.tar.gz.sigstore.json \
artifact.tar.gz
```

### Output extension matters for OSSF Scorecard

**Use `.sigstore.json` for the cosign bundle output, NOT cosign's default `.bundle`.**

OSSF Scorecard's [Signed-Releases](https://github.com/ossf/scorecard/blob/main/docs/checks.md#signed-releases) check pattern-matches release-asset filenames against a fixed allowlist of signature extensions:

- `.sig`
- `.asc`
- `.minisig`
- `.sigstore`
- `.sigstore.json`
- `.intoto.jsonl`

The `.bundle` extension that `cosign sign-blob --bundle` writes by default is **not** on that list, so cosign-signed releases that use `.bundle` are reported as unsigned (`Signed-Releases` score `0/10`).

**The bytes inside the file are identical** — cosign's bundle format IS the Sigstore bundle JSON. Only the filename matters for tooling detection. `cosign verify-blob --bundle file.sigstore.json` works exactly the same as `--bundle file.bundle`.

**Concrete fix in a workflow:**

```yaml
# Wrong — Scorecard sees this as unsigned
cosign sign-blob --yes "$file" --bundle "${file}.bundle"

# Right — Scorecard recognizes this as signed
cosign sign-blob --yes "$file" --bundle "${file}.sigstore.json"
```

**Past releases cannot be retroactively fixed.** GitHub releases are immutable once assets are attached, so renaming or replacing assets on already-published releases is not possible. Only future releases benefit from the fix. Scorecard averages the Signed-Releases score over the **last 4 releases**, so the score climbs gradually as new releases ship.

Reference upstream change for the netresearch shared workflows: [netresearch/typo3-ci-workflows#84](https://github.com/netresearch/typo3-ci-workflows/pull/84) — applied to both `release.yml` and `release-typo3-extension.yml`.

### Verification

```bash
cosign verify-blob \
--certificate-oidc-issuer https://token.actions.githubusercontent.com \
--certificate-identity-regexp "github.com/org/repo" \
--bundle artifact.tar.gz.sigstore.json \
artifact.tar.gz
```

Expand Down
11 changes: 10 additions & 1 deletion skills/github-release/templates/release-generic.yml
Original file line number Diff line number Diff line change
Expand Up @@ -40,8 +40,17 @@ jobs:
uses: sigstore/cosign-installer@v3
- name: Sign artifacts
run: |
# Use .sigstore.json extension (NOT cosign's default .bundle).
# OSSF Scorecard's Signed-Releases check pattern-matches against a
# fixed allowlist of signature extensions (.sig, .asc, .minisig,
# .sigstore, .sigstore.json, .intoto.jsonl). The .bundle extension
# cosign writes by default is NOT on that list, so cosign-signed
# releases score 0/10. The bytes inside the bundle file ARE the
# Sigstore bundle JSON format — the rename is purely cosmetic for
# tooling detection. `cosign verify-blob --bundle file.sigstore.json`
# works identically.
for f in dist/*; do
cosign sign-blob "$f" --bundle "${f}.bundle" --yes
cosign sign-blob "$f" --bundle "${f}.sigstore.json" --yes
done
- name: Generate attestation
uses: actions/attest-build-provenance@v2
Expand Down
Loading