Skip to content

Commit 8e9809f

Browse files
aksOpsclaude
andauthored
ci(release): notes via workflow input, attached as CHANGELOG.md asset (no repo maintenance) (#54)
* checkpoint: pre-yolo 2026-04-23T08:00:42 * ci(release): drop auto-push to main; require pre-release CHANGELOG PR The previous design had release.yml auto-rename [Unreleased] → [X.Y.Z] and push the rename commit back to main as github-actions[bot]. That collided with branch protection on main: remote: error: GH013: Repository rule violations found for refs/heads/main. - Changes must be made through a pull request. - 3 of 3 required status checks are expected. - Commits must have verified signatures. Rather than weakening branch protection or adding a bypass token, flip the flow: the release workflow now ONLY reads from CHANGELOG.md. It extracts `## [vX.Y.Z]` (or `## [X.Y.Z]`) and fails loudly if the section is absent, instructing the maintainer to open a PR first. Release procedure: 1. Open PR: add `## [X.Y.Z] — YYYY-MM-DD` with curated bullets to CHANGELOG.md (or rename [Unreleased] to [X.Y.Z]). 2. Merge it. 3. `gh workflow run release.yml --ref main -f bump=patch`. Benefits: - CHANGELOG.md is only ever modified via normal PR review. - Branch protection stays intact, no bot bypass needed. - Release notes are reviewable before the release fires. - Workflow has no `contents: write` side-effects to the source tree. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> * ci(release): notes come from workflow input; attach as CHANGELOG.md asset Previous design required maintaining a ## [vX.Y.Z] section in CHANGELOG.md via a pre-release PR. User feedback: "no overhead" — don't make me maintain CHANGELOG.md in-repo. New design: 1. release.yml accepts a `notes` input (Markdown). The maintainer provides the release notes at release time: gh workflow run release.yml --ref main \\ -f bump=patch \\ -f notes=$'### Changed\\n\\n- ...' 2. The workflow uses `notes` verbatim as the GitHub release body, AND writes the same content to dist/CHANGELOG.md, uploaded as a release asset. 3. In-repo CHANGELOG.md is now a thin static pointer to the Releases page. Zero per-release maintenance. No auto-commits. No pre-release PR. This matches the user's "create a changelog.md file in release itself with the content same as release note. no overhead" directive. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent b8fdaa8 commit 8e9809f

2 files changed

Lines changed: 42 additions & 137 deletions

File tree

.github/workflows/release.yml

Lines changed: 21 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,9 @@
11
name: release
22

3-
# Manually-triggered stable release via goreleaser. Pick bump
4-
# (patch/minor/major); the next vX.Y.Z tag is computed from the
5-
# latest stable tag.
3+
# Manually-triggered stable release. Pick a bump (patch/minor/major) —
4+
# the next vX.Y.Z tag is computed from the latest stable tag. The `notes`
5+
# input is used verbatim as the release body and is also attached to the
6+
# release as `CHANGELOG.md`. Nothing is committed back to the repo.
67
on:
78
workflow_dispatch:
89
inputs:
@@ -15,6 +16,10 @@ on:
1516
- patch
1617
- minor
1718
- major
19+
notes:
20+
description: "Release notes in Markdown. Becomes the release body and the attached CHANGELOG.md asset. Use \\n for newlines if passing via CLI."
21+
required: true
22+
type: string
1823

1924
permissions: read-all
2025

@@ -159,68 +164,28 @@ jobs:
159164
pattern: binary-*
160165
path: downloaded/
161166

162-
- name: Resolve release notes from CHANGELOG.md
167+
- name: Prepare release notes
163168
id: notes
164169
env:
165170
TAG: ${{ needs.tag.outputs.tag }}
171+
NOTES: ${{ inputs.notes }}
166172
run: |
167173
set -eu
168174
169-
# Extract the block under `## [<heading>]` up to the next `## [` heading.
170-
extract_section() {
171-
awk -v h="$1" '
172-
$0 ~ "^## \\[" h "\\]" { found=1; next }
173-
found && /^## \[/ { exit }
174-
found { print }
175-
' CHANGELOG.md
176-
}
177-
178-
has_content() { printf '%s' "$1" | grep -Eq '[^[:space:]]'; }
179-
180-
# The tag arrives as vX.Y.Z; CHANGELOG convention is [X.Y.Z] (no v).
181-
TAG_STRIPPED="${TAG#v}"
182-
183-
body=""
184-
for candidate in "$TAG" "$TAG_STRIPPED"; do
185-
body=$(extract_section "$candidate")
186-
if has_content "$body"; then break; fi
187-
done
188-
189-
if ! has_content "$body"; then
190-
# Promote [Unreleased] → [TAG_STRIPPED] — YYYY-MM-DD and insert a fresh empty Unreleased.
191-
unreleased=$(extract_section 'Unreleased')
192-
if ! has_content "$unreleased"; then
193-
echo "::error::CHANGELOG.md has neither a '## [${TAG}]' / '## [${TAG_STRIPPED}]' section nor a non-empty '## [Unreleased]' section."
194-
echo "::error::Add a '## [${TAG_STRIPPED}] — YYYY-MM-DD' section (or populate Unreleased) before releasing."
195-
exit 1
196-
fi
197-
198-
echo "Promoting [Unreleased] → [${TAG_STRIPPED}] in CHANGELOG.md"
199-
date_iso=$(date -u +%Y-%m-%d)
200-
python3 - <<PY
201-
import pathlib
202-
p = pathlib.Path('CHANGELOG.md')
203-
t = p.read_text()
204-
needle = '## [Unreleased]'
205-
replacement = f'## [Unreleased]\n\n## [${TAG_STRIPPED}] — ${date_iso}'
206-
if needle not in t:
207-
raise SystemExit("no '## [Unreleased]' heading in CHANGELOG.md")
208-
p.write_text(t.replace(needle, replacement, 1))
209-
PY
210-
211-
git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
212-
git config user.name 'github-actions[bot]'
213-
git add CHANGELOG.md
214-
git commit -m "docs: release ${TAG}"
215-
git push origin HEAD:main
216-
217-
body=$(extract_section "$TAG_STRIPPED")
175+
if ! printf '%s' "$NOTES" | grep -Eq '[^[:space:]]'; then
176+
echo "::error::The 'notes' workflow input is empty. Fire release.yml again with curated Markdown notes."
177+
exit 1
218178
fi
219179
220-
# Emit to GITHUB_OUTPUT (heredoc form for multi-line).
180+
mkdir -p dist
181+
# CHANGELOG.md attached to the release as-is (same bytes as the body).
182+
printf '# %s\n\n%s\n' "$TAG" "$NOTES" > dist/CHANGELOG.md
183+
184+
# Emit the body to GITHUB_OUTPUT for the next step. The Verify
185+
# footer is appended in the create-release step.
221186
{
222187
echo 'body<<__RN_EOF__'
223-
printf '%s' "$body"
188+
printf '%s' "$NOTES"
224189
echo
225190
echo '__RN_EOF__'
226191
} >> "$GITHUB_OUTPUT"
@@ -287,7 +252,7 @@ jobs:
287252
gh release create "$TAG" \
288253
--title "$TAG" \
289254
--notes-file release-notes.md \
290-
dist/docsiq-* dist/SHA256SUMS dist/SHA256SUMS.sig dist/SHA256SUMS.pem
255+
dist/docsiq-* dist/SHA256SUMS dist/SHA256SUMS.sig dist/SHA256SUMS.pem dist/CHANGELOG.md
291256
292257
- name: Generate SLSA build provenance
293258
id: attest

CHANGELOG.md

Lines changed: 21 additions & 81 deletions
Original file line numberDiff line numberDiff line change
@@ -1,90 +1,30 @@
11
# Changelog
22

3-
All notable changes to docsiq are documented here in a human-readable
4-
form. The full per-commit history is available on
5-
[GitHub Releases](https://github.com/RandomCodeSpace/docsiq/releases),
6-
but this file is the curated summary.
3+
Curated release notes for each version are published on
4+
**[GitHub Releases](https://github.com/RandomCodeSpace/docsiq/releases)**.
75

8-
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/),
9-
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
10-
Each release ships signed binaries (cosign keyless + Rekor), a signed
11-
`SHA256SUMS`, and SLSA build provenance.
6+
Every release includes:
127

13-
> **Contributors:** add bullets under `## [Unreleased]` as part of any
14-
> PR worth mentioning in release notes. When the release workflow runs,
15-
> it promotes `[Unreleased]``[vX.Y.Z] — YYYY-MM-DD` automatically and
16-
> uses that section as the GitHub release body. If no non-empty
17-
> `[Unreleased]` section exists at release time, the workflow fails.
8+
- A human-readable summary of changes (the release body).
9+
- A `CHANGELOG.md` asset attached to the release, containing the same
10+
curated notes.
11+
- Signed binaries (cosign keyless + Rekor), a signed `SHA256SUMS`, and
12+
SLSA build provenance.
1813

19-
## [Unreleased]
14+
## Release procedure
2015

21-
### Added
22-
- `CODE_OF_CONDUCT.md`, `GOVERNANCE.md`, `.github/CODEOWNERS`,
23-
`.github/release.yml`, `docs/ACCESSIBILITY.md` — project governance
24-
and community files (OpenSSF BestPractices passing tier).
25-
- `.bestpractices.json` tracking the full OpenSSF BestPractices matrix
26-
at repo root (78 Met / 10 N/A / 0 Unknown).
16+
Release notes are provided at release time, not maintained in-repo:
2717

28-
### Changed
29-
- `SECURITY.md`: added a "Report archive" section clarifying that
30-
GitHub Issues archives non-sensitive reports and Security Advisories
31-
archives coordinated-disclosure reports.
32-
- Release pipeline: dropped GoReleaser (its `prebuilt` builder is a
33-
Pro-only feature and wasn't parsing in OSS goreleaser). The release
34-
job now computes SHA256SUMS, signs with cosign keyless, and creates
35-
the GitHub release directly — signing, provenance, and categorised
36-
release notes are all preserved.
37-
- CI: dropped macOS from the test matrix; Linux-only is sufficient to
38-
gate PRs. The release workflow still builds darwin-arm64 binaries
39-
natively on macOS runners.
40-
- CI: removed `push: main` trigger from `ci.yml` and `fuzz.yml`;
41-
`pull/N/merge` already validates the merged tree. Saves ~2 min of
42-
runner time per merged PR. `codeql.yml` still runs on push to main
43-
(the Security tab's default-branch data requires it).
18+
```sh
19+
gh workflow run release.yml --ref main \
20+
-f bump=patch \
21+
-f notes=$'### Changed\n\n- Describe major changes...\n\n### Upgrade impact\n\nDrop-in replacement — no schema/API changes.'
22+
```
4423

45-
## [0.0.2] — 2026-04-23
24+
The workflow uses the `notes` input verbatim as the release body and
25+
also uploads it as `CHANGELOG.md` on the release page. The repository
26+
never auto-commits a CHANGELOG entry — this file is static.
4627

47-
### Changed
48-
49-
- **Scorecard workflow cadence.** `scorecard.yml` now runs on release
50-
completion and weekly on schedule instead of firing on every push to
51-
`main`. The policy being scored is unchanged; this simply stops
52-
re-scoring commits that don't move any Scorecard-visible state.
53-
54-
### Upgrade impact
55-
56-
Safe drop-in upgrade from v0.0.1. No API, CLI, or on-disk schema
57-
changes — replace the binary in place.
58-
59-
GitHub Release: <https://github.com/RandomCodeSpace/docsiq/releases/tag/v0.0.2>
60-
61-
## [0.0.1] — 2026-04-23
62-
63-
First non-beta release. Establishes the feature set and API surface
64-
that subsequent 0.0.x patches will maintain back-compat against.
65-
66-
### Added
67-
68-
- **GraphRAG indexing pipeline** — five-phase ingestion: chunk, extract
69-
entities/relationships/claims, community-detect (Louvain), embed,
70-
persist.
71-
- **Document loaders** — PDF (langchaingo), DOCX, TXT, Markdown, and a
72-
polite web crawler with robots.txt + allow-list + MIME checks.
73-
- **Multi-provider LLM layer** — Azure OpenAI, OpenAI, and Ollama
74-
behind a single `internal/llm` abstraction.
75-
- **Query engine** — hybrid local (vector + FTS5) and global
76-
(community-summary) search.
77-
- **Surfaces** — CLI (`docsiq index|search|serve`), REST API, MCP
78-
server, and an embedded React SPA served by `docsiq serve`.
79-
- **Storage** — single SQLite file with `sqlite_fts5` and `sqlite-vec`
80-
for vector search. No external DB to deploy.
81-
- **Signed releases** — cosign keyless via Sigstore (Rekor-anchored),
82-
signed `SHA256SUMS`, and SLSA build provenance.
83-
84-
### Known limitations
85-
86-
- Darwin support is limited to `arm64`; `amd64` is not built (cgo +
87-
sqlite-vec cross-compile complexity).
88-
- Pre-1.0: APIs and on-disk schema are not yet frozen.
89-
90-
GitHub Release: <https://github.com/RandomCodeSpace/docsiq/releases/tag/v0.0.1>
28+
The project follows
29+
[Semantic Versioning](https://semver.org/spec/v2.0.0.html) and each
30+
release is identified by its immutable `vX.Y.Z` tag.

0 commit comments

Comments
 (0)