From 6de4ba240ac6933473656c0ea6efb89781faad22 Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 23 Apr 2026 08:00:42 +0000 Subject: [PATCH 1/3] checkpoint: pre-yolo 2026-04-23T08:00:42 From 2ef66e3b6e282215e15729de080340f9efd73c8b Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 23 Apr 2026 08:01:39 +0000 Subject: [PATCH 2/3] ci(release): drop auto-push to main; require pre-release CHANGELOG PR MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/release.yml | 37 ++++++++--------------------------- CHANGELOG.md | 12 +++++++----- 2 files changed, 15 insertions(+), 34 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index c33215e..f9556d4 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -167,6 +167,8 @@ jobs: set -eu # Extract the block under `## []` up to the next `## [` heading. + # The heading may be `## [vX.Y.Z] — YYYY-MM-DD` or `## [X.Y.Z] — YYYY-MM-DD`; + # we match the prefix `## []` regardless of trailing date. extract_section() { awk -v h="$1" ' $0 ~ "^## \\[" h "\\]" { found=1; next } @@ -177,7 +179,7 @@ jobs: has_content() { printf '%s' "$1" | grep -Eq '[^[:space:]]'; } - # The tag arrives as vX.Y.Z; CHANGELOG convention is [X.Y.Z] (no v). + # The tag arrives as vX.Y.Z; CHANGELOG convention is [X.Y.Z] (no v prefix). TAG_STRIPPED="${TAG#v}" body="" @@ -187,34 +189,11 @@ jobs: done if ! has_content "$body"; then - # Promote [Unreleased] → [TAG_STRIPPED] — YYYY-MM-DD and insert a fresh empty Unreleased. - unreleased=$(extract_section 'Unreleased') - if ! has_content "$unreleased"; then - echo "::error::CHANGELOG.md has neither a '## [${TAG}]' / '## [${TAG_STRIPPED}]' section nor a non-empty '## [Unreleased]' section." - echo "::error::Add a '## [${TAG_STRIPPED}] — YYYY-MM-DD' section (or populate Unreleased) before releasing." - exit 1 - fi - - echo "Promoting [Unreleased] → [${TAG_STRIPPED}] in CHANGELOG.md" - date_iso=$(date -u +%Y-%m-%d) - python3 - < **Contributors:** add bullets under `## [Unreleased]` as part of any -> PR worth mentioning in release notes. When the release workflow runs, -> it promotes `[Unreleased]` → `[vX.Y.Z] — YYYY-MM-DD` automatically and -> uses that section as the GitHub release body. If no non-empty -> `[Unreleased]` section exists at release time, the workflow fails. +> **Release flow:** before firing the release workflow, open a PR that +> adds a new `## [X.Y.Z] — YYYY-MM-DD` section with curated bullets, or +> renames `## [Unreleased]` to the new version. Merge that PR, then fire +> `release.yml`. The workflow reads the matching section as the GitHub +> release body. If the section is missing, the release **fails** with a +> clear error — no auto-commits to main, no silent fallback to raw git +> logs. CHANGELOG.md is only modified through normal PRs. ## [Unreleased] From 3f3855f3891e3fcc1fd68b04e5dfc381b378845e Mon Sep 17 00:00:00 2001 From: Amit Kumar Date: Thu, 23 Apr 2026 08:06:15 +0000 Subject: [PATCH 3/3] ci(release): notes come from workflow input; attach as CHANGELOG.md asset MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 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) --- .github/workflows/release.yml | 54 +++++++----------- CHANGELOG.md | 104 +++++++--------------------------- 2 files changed, 41 insertions(+), 117 deletions(-) diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index f9556d4..fa9fc91 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -1,8 +1,9 @@ name: release -# Manually-triggered stable release via goreleaser. Pick bump -# (patch/minor/major); the next vX.Y.Z tag is computed from the -# latest stable tag. +# Manually-triggered stable release. Pick a bump (patch/minor/major) — +# the next vX.Y.Z tag is computed from the latest stable tag. The `notes` +# input is used verbatim as the release body and is also attached to the +# release as `CHANGELOG.md`. Nothing is committed back to the repo. on: workflow_dispatch: inputs: @@ -15,6 +16,10 @@ on: - patch - minor - major + notes: + description: "Release notes in Markdown. Becomes the release body and the attached CHANGELOG.md asset. Use \\n for newlines if passing via CLI." + required: true + type: string permissions: read-all @@ -159,47 +164,28 @@ jobs: pattern: binary-* path: downloaded/ - - name: Resolve release notes from CHANGELOG.md + - name: Prepare release notes id: notes env: TAG: ${{ needs.tag.outputs.tag }} + NOTES: ${{ inputs.notes }} run: | set -eu - # Extract the block under `## []` up to the next `## [` heading. - # The heading may be `## [vX.Y.Z] — YYYY-MM-DD` or `## [X.Y.Z] — YYYY-MM-DD`; - # we match the prefix `## []` regardless of trailing date. - extract_section() { - awk -v h="$1" ' - $0 ~ "^## \\[" h "\\]" { found=1; next } - found && /^## \[/ { exit } - found { print } - ' CHANGELOG.md - } - - has_content() { printf '%s' "$1" | grep -Eq '[^[:space:]]'; } - - # The tag arrives as vX.Y.Z; CHANGELOG convention is [X.Y.Z] (no v prefix). - TAG_STRIPPED="${TAG#v}" - - body="" - for candidate in "$TAG" "$TAG_STRIPPED"; do - body=$(extract_section "$candidate") - if has_content "$body"; then break; fi - done - - if ! has_content "$body"; then - echo "::error::CHANGELOG.md has no '## [${TAG}]' or '## [${TAG_STRIPPED}]' section." - echo "::error::Release flow: open a PR that adds" - echo "::error:: ## [${TAG_STRIPPED}] — $(date -u +%Y-%m-%d)" - echo "::error::with curated bullets to CHANGELOG.md, merge it, then fire this workflow again." + if ! printf '%s' "$NOTES" | grep -Eq '[^[:space:]]'; then + echo "::error::The 'notes' workflow input is empty. Fire release.yml again with curated Markdown notes." exit 1 fi - # Emit to GITHUB_OUTPUT (heredoc form for multi-line). + mkdir -p dist + # CHANGELOG.md attached to the release as-is (same bytes as the body). + printf '# %s\n\n%s\n' "$TAG" "$NOTES" > dist/CHANGELOG.md + + # Emit the body to GITHUB_OUTPUT for the next step. The Verify + # footer is appended in the create-release step. { echo 'body<<__RN_EOF__' - printf '%s' "$body" + printf '%s' "$NOTES" echo echo '__RN_EOF__' } >> "$GITHUB_OUTPUT" @@ -266,7 +252,7 @@ jobs: gh release create "$TAG" \ --title "$TAG" \ --notes-file release-notes.md \ - dist/docsiq-* dist/SHA256SUMS dist/SHA256SUMS.sig dist/SHA256SUMS.pem + dist/docsiq-* dist/SHA256SUMS dist/SHA256SUMS.sig dist/SHA256SUMS.pem dist/CHANGELOG.md - name: Generate SLSA build provenance id: attest diff --git a/CHANGELOG.md b/CHANGELOG.md index 06f5e73..ec1d9c2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,92 +1,30 @@ # Changelog -All notable changes to docsiq are documented here in a human-readable -form. The full per-commit history is available on -[GitHub Releases](https://github.com/RandomCodeSpace/docsiq/releases), -but this file is the curated summary. +Curated release notes for each version are published on +**[GitHub Releases](https://github.com/RandomCodeSpace/docsiq/releases)**. -The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), -and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). -Each release ships signed binaries (cosign keyless + Rekor), a signed -`SHA256SUMS`, and SLSA build provenance. +Every release includes: -> **Release flow:** before firing the release workflow, open a PR that -> adds a new `## [X.Y.Z] — YYYY-MM-DD` section with curated bullets, or -> renames `## [Unreleased]` to the new version. Merge that PR, then fire -> `release.yml`. The workflow reads the matching section as the GitHub -> release body. If the section is missing, the release **fails** with a -> clear error — no auto-commits to main, no silent fallback to raw git -> logs. CHANGELOG.md is only modified through normal PRs. +- A human-readable summary of changes (the release body). +- A `CHANGELOG.md` asset attached to the release, containing the same + curated notes. +- Signed binaries (cosign keyless + Rekor), a signed `SHA256SUMS`, and + SLSA build provenance. -## [Unreleased] +## Release procedure -### Added -- `CODE_OF_CONDUCT.md`, `GOVERNANCE.md`, `.github/CODEOWNERS`, - `.github/release.yml`, `docs/ACCESSIBILITY.md` — project governance - and community files (OpenSSF BestPractices passing tier). -- `.bestpractices.json` tracking the full OpenSSF BestPractices matrix - at repo root (78 Met / 10 N/A / 0 Unknown). +Release notes are provided at release time, not maintained in-repo: -### Changed -- `SECURITY.md`: added a "Report archive" section clarifying that - GitHub Issues archives non-sensitive reports and Security Advisories - archives coordinated-disclosure reports. -- Release pipeline: dropped GoReleaser (its `prebuilt` builder is a - Pro-only feature and wasn't parsing in OSS goreleaser). The release - job now computes SHA256SUMS, signs with cosign keyless, and creates - the GitHub release directly — signing, provenance, and categorised - release notes are all preserved. -- CI: dropped macOS from the test matrix; Linux-only is sufficient to - gate PRs. The release workflow still builds darwin-arm64 binaries - natively on macOS runners. -- CI: removed `push: main` trigger from `ci.yml` and `fuzz.yml`; - `pull/N/merge` already validates the merged tree. Saves ~2 min of - runner time per merged PR. `codeql.yml` still runs on push to main - (the Security tab's default-branch data requires it). +```sh +gh workflow run release.yml --ref main \ + -f bump=patch \ + -f notes=$'### Changed\n\n- Describe major changes...\n\n### Upgrade impact\n\nDrop-in replacement — no schema/API changes.' +``` -## [0.0.2] — 2026-04-23 +The workflow uses the `notes` input verbatim as the release body and +also uploads it as `CHANGELOG.md` on the release page. The repository +never auto-commits a CHANGELOG entry — this file is static. -### Changed - -- **Scorecard workflow cadence.** `scorecard.yml` now runs on release - completion and weekly on schedule instead of firing on every push to - `main`. The policy being scored is unchanged; this simply stops - re-scoring commits that don't move any Scorecard-visible state. - -### Upgrade impact - -Safe drop-in upgrade from v0.0.1. No API, CLI, or on-disk schema -changes — replace the binary in place. - -GitHub Release: - -## [0.0.1] — 2026-04-23 - -First non-beta release. Establishes the feature set and API surface -that subsequent 0.0.x patches will maintain back-compat against. - -### Added - -- **GraphRAG indexing pipeline** — five-phase ingestion: chunk, extract - entities/relationships/claims, community-detect (Louvain), embed, - persist. -- **Document loaders** — PDF (langchaingo), DOCX, TXT, Markdown, and a - polite web crawler with robots.txt + allow-list + MIME checks. -- **Multi-provider LLM layer** — Azure OpenAI, OpenAI, and Ollama - behind a single `internal/llm` abstraction. -- **Query engine** — hybrid local (vector + FTS5) and global - (community-summary) search. -- **Surfaces** — CLI (`docsiq index|search|serve`), REST API, MCP - server, and an embedded React SPA served by `docsiq serve`. -- **Storage** — single SQLite file with `sqlite_fts5` and `sqlite-vec` - for vector search. No external DB to deploy. -- **Signed releases** — cosign keyless via Sigstore (Rekor-anchored), - signed `SHA256SUMS`, and SLSA build provenance. - -### Known limitations - -- Darwin support is limited to `arm64`; `amd64` is not built (cgo + - sqlite-vec cross-compile complexity). -- Pre-1.0: APIs and on-disk schema are not yet frozen. - -GitHub Release: +The project follows +[Semantic Versioning](https://semver.org/spec/v2.0.0.html) and each +release is identified by its immutable `vX.Y.Z` tag.