Skip to content
Open
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
3 changes: 2 additions & 1 deletion MAINTAINING.md
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ Written 2026-07-05 @ v0.1.114 (main @ fe35cf7). This is the institutional-knowle

## 5. Release process

Flow (the `np-release` skill orchestrates this): bump `CMakeLists.txt` VERSION → `release_notes/vX.md` → append `CHANGELOG.md` → `scripts/release-check.sh` → PII sweep → commit → tag → push → watch CI → verify GitHub Release assets + Pages.
Flow (the `np-release` skill orchestrates this): bump `CMakeLists.txt` VERSION **and `notepatra-mcp/Cargo.toml`** → `release_notes/vX.md` → append `CHANGELOG.md` → `scripts/release-check.sh` → PII sweep → commit → tag → push → watch CI → verify GitHub Release assets + Pages → **`cd notepatra-mcp && cargo publish`**.

Gates in `scripts/` (release-check.sh calls most): `stale-text-check.sh` (canonical counts, capability claims, installer filenames), `verify-download-sizes.sh` (published bytes vs docs claims), `test-install-selection.sh` (lite/full artifact pick), `post-release-verify.sh`, `bump_version.sh`, `smoke-multiprocess.sh`.

Expand All @@ -75,6 +75,7 @@ CI traps:
- **GitHub push protection** rejects anything key-shaped (`sk-…`, `ghp_…`) even all-zero fakes — build key-shaped test literals from concatenated fragments. Recovery: `reset --soft`, fix, re-commit, move tag.
- `Closes #N` in a direct push does NOT close issues — only PR merges do; follow up with `gh issue close N`.
- Install scripts **hard-fail** verification when the SHA/sig source is unreachable (Tier 1). Every new security check gets classified into exactly one tier first: hard-fail / soft-warn / skip-when-tool-missing.
- **crates.io is a distribution surface and nobody was watching it.** `notepatra-mcp` 0.1.118/0.1.119 were published by hand on 2026-07-18, then the channel rotted for eight releases — `cargo install notepatra-mcp` served 0.1.119, the one release whose Windows named-pipe transport deadlocked on every verb and had never completed a tool call. `release-check.sh` now hard-fails when crates.io's `max_version` is not the previous tag (soft-warn when the registry is unreachable). Publishing is still manual and happens AFTER the tag; the gate catches the omission on the next release, not the current one.

**User-pause rule:** "do not CI until I say" / "stop shipping" / "wait" = no push, no tag, no commit-to-main. Work locally, batch fixes, resume only on an explicit "ship it". Status questions do not unpause.

Expand Down
44 changes: 44 additions & 0 deletions scripts/release-check.sh
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,9 @@
# • docs/index.html stat card / hero / sticky CTA mention the tag
# • All 22+ test executables build (notepatra_all_tests target)
# • ctest passes
# • notepatra-mcp/Cargo.toml version matches the tag
# • crates.io already carries the PREVIOUS release (the sidecar publish
# is manual and silently rotted for 8 releases once already)
# • git working tree is clean
# • No vendored binary (vendor/, build/, etc.) is staged
#
Expand Down Expand Up @@ -241,6 +244,47 @@ if [ -d notepatra-mcp ]; then
fi
fi

echo
echo "── crates.io ──"
# The sidecar is published to crates.io as `notepatra-mcp`. Nothing in CI does
# this, so it is a manual step — and a manual step that is not a gate is a step
# that stops happening. It did: 0.1.118 and 0.1.119 went up on 2026-07-18 and
# then the channel rotted for EIGHT releases, leaving `cargo install
# notepatra-mcp` serving 0.1.119 — the one release whose Windows named-pipe
# transport deadlocked on every verb and had never completed a tool call.
#
# The invariant that would have caught it on the very next release: by the time
# you cut vN, the PREVIOUS tag must already be on crates.io. Checking for the
# version being cut would be wrong — that one is published after the tag.
prev_tag="$(git describe --tags --abbrev=0 2>/dev/null || true)"
prev_ver="${prev_tag#v}"
crate_ver="$(grep -m1 -oE '^version = "[0-9]+\.[0-9]+\.[0-9]+"' notepatra-mcp/Cargo.toml \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+' || true)"

check "notepatra-mcp/Cargo.toml version matches the tag being cut" \
"[ \"$crate_ver\" = \"$VERSION\" ]"

if [ -z "$prev_ver" ] || [ "$prev_ver" = "$VERSION" ]; then
echo " ⚠ no earlier tag to compare against — crates.io currency unchecked"
elif published="$(curl -fsSL --max-time 15 -A notepatra-release-check \
https://crates.io/api/v1/crates/notepatra-mcp 2>/dev/null \
| grep -oE '"max_version":"[0-9]+\.[0-9]+\.[0-9]+"' \
| grep -oE '[0-9]+\.[0-9]+\.[0-9]+')" && [ -n "$published" ]; then
# Hard-fail: a stale registry ships a known-broken sidecar to anyone
# running `cargo install`, and nothing else in the pipeline notices.
check "crates.io is current (published $published = previous tag $prev_ver)" \
"[ \"$published\" = \"$prev_ver\" ]"
if [ "$published" != "$prev_ver" ]; then
echo " crates.io serves $published but the last release was $prev_ver."
echo " cd notepatra-mcp && cargo publish # then re-run this script"
fi
else
# Soft-warn: offline or crates.io down is not a reason to block a release.
echo " ⚠ could not reach crates.io — published sidecar version unverified"
fi
echo
echo " → after tagging: cd notepatra-mcp && cargo publish (publishes $VERSION)"

echo
echo "── tag ──"
if git rev-parse "$TAG" >/dev/null 2>&1; then
Expand Down
Loading