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
77 changes: 21 additions & 56 deletions .github/workflows/release.yml
Original file line number Diff line number Diff line change
@@ -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:
Expand All @@ -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

Expand Down Expand Up @@ -159,68 +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 `## [<heading>]` up to the next `## [` heading.
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).
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
# 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 - <<PY
import pathlib
p = pathlib.Path('CHANGELOG.md')
t = p.read_text()
needle = '## [Unreleased]'
replacement = f'## [Unreleased]\n\n## [${TAG_STRIPPED}] — ${date_iso}'
if needle not in t:
raise SystemExit("no '## [Unreleased]' heading in CHANGELOG.md")
p.write_text(t.replace(needle, replacement, 1))
PY

git config user.email '41898282+github-actions[bot]@users.noreply.github.com'
git config user.name 'github-actions[bot]'
git add CHANGELOG.md
git commit -m "docs: release ${TAG}"
git push origin HEAD:main

body=$(extract_section "$TAG_STRIPPED")
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"
Expand Down Expand Up @@ -287,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
Expand Down
102 changes: 21 additions & 81 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,90 +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:

> **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.
- 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: <https://github.com/RandomCodeSpace/docsiq/releases/tag/v0.0.2>

## [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: <https://github.com/RandomCodeSpace/docsiq/releases/tag/v0.0.1>
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.
Loading