diff --git a/.github/workflows/release-notes.yml b/.github/workflows/release-notes.yml index 818ccbf4c..85fd9e62b 100644 --- a/.github/workflows/release-notes.yml +++ b/.github/workflows/release-notes.yml @@ -23,36 +23,14 @@ jobs: name: Release Notes runs-on: ubuntu-latest steps: - - name: Skip release notes check - if: contains(github.event.pull_request.labels.*.name, 'no-changelog') - run: echo "Release notes check skipped by the no-changelog label." - - name: git checkout - if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }} uses: actions/checkout@v4 with: fetch-depth: 0 - - name: Check for a release note - if: ${{ !contains(github.event.pull_request.labels.*.name, 'no-changelog') }} + - name: Check release notes env: BASE_SHA: ${{ github.event.pull_request.base.sha }} HEAD_SHA: ${{ github.event.pull_request.head.sha }} - RELEASE_NOTES_FILE: docs/release-notes/release-notes-next.md - run: | - if git diff --unified=0 "$BASE_SHA...$HEAD_SHA" -- \ - "$RELEASE_NOTES_FILE" | - awk ' - /^@@/ { in_hunk = 1; next } - in_hunk && /^\+/ { - line = substr($0, 2) - if (line ~ /[^[:space:]]/) found = 1 - } - END { exit found ? 0 : 1 } - ' - then - exit 0 - fi - - echo "::error file=$RELEASE_NOTES_FILE::Add a release note or apply the no-changelog label." - exit 1 + NO_CHANGELOG: ${{ contains(github.event.pull_request.labels.*.name, 'no-changelog') }} + run: scripts/release-notes.py pr "$BASE_SHA" "$HEAD_SHA" diff --git a/Makefile b/Makefile index 1caeef2da..a08fec96a 100644 --- a/Makefile +++ b/Makefile @@ -57,6 +57,7 @@ DOCKER_RELEASE_BUILDER = docker run \ -v $(shell bash -c "go env GOMODCACHE 2>/dev/null || (mkdir -p /tmp/go-modcache; echo /tmp/go-modcache)"):/tmp/build/.modcache \ -v $$(pwd):/repo \ -e LOOPBUILDSYS='$(buildsys)' \ + -e SKIP_RELEASE_NOTES_CHECK \ loop-release-builder GREEN=\033[0;32m diff --git a/docs/release-notes/README.md b/docs/release-notes/README.md index a23154fbe..a8f8d15eb 100644 --- a/docs/release-notes/README.md +++ b/docs/release-notes/README.md @@ -6,9 +6,20 @@ Contributor lists are derived from non-merge commit authors and co-authors between consecutive official release tags. Add user-facing changes to [release-notes-next.md](release-notes-next.md). -When preparing a release, use -[release-notes-template.md](release-notes-template.md) to turn those notes into -a versioned file, then reset the next-release sections. +When preparing a release, run +`./scripts/release-notes.py rotate `. +The script moves the unreleased notes into the versioned file, updates adjacent +links and this table, then resets the next-release file from the template. It +uses the current date by default; pass `--date YYYY-MM-DD` when preparing notes +for another date. The template defines the complete notes structure. The +scripts only read or update the release date, release page, previous-release, +and next-release fields, so adding or rearranging sections requires no script +changes. + +The release-notes CI check detects version-component changes in `version.go`. +Those release-preparation pull requests must satisfy the same rotated-notes +check as the release builder; other pull requests need a next-release entry or +the `no-changelog` label. | Release notes | Date | Highlights | | --- | --- | --- | @@ -110,4 +121,5 @@ a versioned file, then reset the next-release sections. | [v0.33.2-beta](release-notes-0.33.2.md) | 2026-06-08 | Exposed static swap details and hardened MuSig2 inputs | | [v0.33.3-beta](release-notes-0.33.3.md) | 2026-06-21 | Raised the LND floor and updated the LND dependency | | [v0.34.0-beta](release-notes-0.34.0.md) | 2026-07-23 | Tracked low-confirmation deposits and production Taproot channels | +| [v0.35.0-beta](release-notes-0.35.0.md) | 2026-08-24 | Added stateless Loop Out HTLC recovery and hardened Instant Out handling | | [Next release](release-notes-next.md) | Unreleased | No changes yet | diff --git a/docs/release-notes/release-notes-0.34.0.md b/docs/release-notes/release-notes-0.34.0.md index 19fa587f0..53b6eecf2 100644 --- a/docs/release-notes/release-notes-0.34.0.md +++ b/docs/release-notes/release-notes-0.34.0.md @@ -4,7 +4,7 @@ - **Release page:** [v0.34.0-beta](https://github.com/lightninglabs/loop/releases/tag/v0.34.0-beta) - **Previous release:** [v0.33.3-beta](release-notes-0.33.3.md) -- **Next release:** [Next release](release-notes-next.md) +- **Next release:** [v0.35.0-beta](release-notes-0.35.0.md) #### New Features diff --git a/docs/release-notes/release-notes-0.35.0.md b/docs/release-notes/release-notes-0.35.0.md new file mode 100644 index 000000000..5aa716384 --- /dev/null +++ b/docs/release-notes/release-notes-0.35.0.md @@ -0,0 +1,58 @@ +# Loop Client Release Notes + +- **Release date:** 2026-08-24 +- **Release page:** + [v0.35.0-beta](https://github.com/lightninglabs/loop/releases/tag/v0.35.0-beta) +- **Previous release:** [v0.34.0-beta](release-notes-0.34.0.md) +- **Next release:** [Next release](release-notes-next.md) + +#### New Features + +* The `loop out sweephtlc` recovery command can reconstruct and sweep a + protocol-11 Loop Out HTLC from public swap data when the local swap database + record is unavailable. It can also request the server's cooperative MuSig2 + signature to spend an original Taproot HTLC through its key path. + +#### Breaking Changes + +* Instant Out and reservation RPCs now require the `loop:out` permission. + Operators using custom scoped macaroons must rebake them before calling + `ListReservations`, `InstantOut`, `InstantOutQuote`, or `ListInstantOuts`. + [PR #1194](https://github.com/lightninglabs/loop/pull/1194) + +#### Bug Fixes + +* Loop Out requests now account for channel reserves when checking outbound + capacity, preventing swaps from starting when their off-chain payment cannot + be funded. + +* Improved Instant Out and reservation validation, lifecycle cleanup, recovery + timing, fee limits, and macaroon permissions. + [PR #1194](https://github.com/lightninglabs/loop/pull/1194) + +* Taproot Asset Loop Out handling now validates RFQ timeouts and asset rates, + keeps cached asset-name lookups responsive during slow `tapd` queries, and + closes `tapd` connections cleanly during shutdown and startup failures. + [PR #1189](https://github.com/lightninglabs/loop/pull/1189) + +#### Maintenance + +* Updated the gRPC dependency to v1.83.1. + +* Updated the Taproot Assets dependency to v0.8.1; asset conversions that + overflow a millisatoshi amount are now safely rejected. + +* Added a CI gate and repository agent guidance requiring every pull request to + include a non-empty entry in the next release notes unless it carries the + `no-changelog` label. + +#### Contributors (Alphabetical Order) + +- Alex Bosworth +- Andras Banki-Horvath +- Boris Nagaev +- dependabot[bot] +- Gustavo Stingelin +- saubyk +- Slyghtning +- Viktor Torstensson diff --git a/docs/release-notes/release-notes-next.md b/docs/release-notes/release-notes-next.md index 896af8058..b361f2905 100644 --- a/docs/release-notes/release-notes-next.md +++ b/docs/release-notes/release-notes-next.md @@ -2,18 +2,8 @@ #### New Features -* The `loop out sweephtlc` recovery command can reconstruct and sweep a - protocol-11 Loop Out HTLC from public swap data when the local swap database - record is unavailable. It can also request the server's cooperative MuSig2 - signature to spend an original Taproot HTLC through its key path. - #### Breaking Changes -* Instant Out and reservation RPCs now require the `loop:out` permission. - Operators using custom scoped macaroons must rebake them before calling - `ListReservations`, `InstantOut`, `InstantOutQuote`, or `ListInstantOuts`. - [PR #1194](https://github.com/lightninglabs/loop/pull/1194) - #### Bug Fixes * `loopd --version` inside the official Docker images now reports the commit @@ -24,33 +14,15 @@ `loopd` failed with `exec format error` on ARM hosts. [Issue #1211](https://github.com/lightninglabs/loop/issues/1211) -* Loop Out requests now account for channel reserves when checking outbound - capacity, preventing swaps from starting when their off-chain payment cannot - be funded. - -* Improved Instant Out and reservation validation, lifecycle cleanup, recovery - timing, fee limits, and macaroon permissions. - [PR #1194](https://github.com/lightninglabs/loop/pull/1194) - -* Taproot Asset Loop Out handling now validates RFQ timeouts and asset rates, - keeps cached asset-name lookups responsive during slow `tapd` queries, and - closes `tapd` connections cleanly during shutdown and startup failures. - [PR #1189](https://github.com/lightninglabs/loop/pull/1189) - #### Maintenance +* Release-note CI and release builds now require a versioned release-notes file + and an empty next release-notes file, unless explicitly overridden for + historical or non-release builds. + * The Docker image build now verifies that every platform of the image index holds binaries for the architecture it advertises, and gives a release its tag only once that check has passed. [Issue #1211](https://github.com/lightninglabs/loop/issues/1211) -* Updated the gRPC dependency to v1.83.1. - -* Updated the Taproot Assets dependency to v0.8.1; asset conversions that - overflow a millisatoshi amount are now safely rejected. - -* Added a CI gate and repository agent guidance requiring every pull request to - include a non-empty entry in the next release notes unless it carries the - `no-changelog` label. - #### Contributors (Alphabetical Order) diff --git a/docs/release-notes/release-notes-template.md b/docs/release-notes/release-notes-template.md index f7c158294..5eec5e9f8 100644 --- a/docs/release-notes/release-notes-template.md +++ b/docs/release-notes/release-notes-template.md @@ -4,7 +4,7 @@ - **Release page:** [vX.Y.Z-beta](https://github.com/lightninglabs/loop/releases/tag/vX.Y.Z-beta) - **Previous release:** [vX.Y.W-beta](release-notes-X.Y.W.md) -- **Next release:** None +- **Next release:** [Next release](release-notes-next.md) #### New Features diff --git a/docs/reproducible_release.md b/docs/reproducible_release.md index 06e552888..eb5c44f45 100644 --- a/docs/reproducible_release.md +++ b/docs/reproducible_release.md @@ -105,3 +105,13 @@ LOOPBUILDSYS='linux-amd64 windows-amd64' ./release.sh v0.32.1-beta This will produce the same artifacts in a `loop-` directory as the `make docker-release` command. The latter simply runs the `release.sh` script inside a Docker container. + +Before packaging a release, `release.sh` verifies that the matching versioned +release-notes file exists and that `release-notes-next.md` contains no entries. +To reproduce a historical release or build an untagged commit where that state +does not apply, explicitly set `SKIP_RELEASE_NOTES_CHECK=1`. For Docker builds, +set it on the `make` command so it is passed into the builder: + +```bash +SKIP_RELEASE_NOTES_CHECK=1 make docker-release tag= +``` diff --git a/release.Dockerfile b/release.Dockerfile index 85c9a3a48..d89197140 100644 --- a/release.Dockerfile +++ b/release.Dockerfile @@ -1,7 +1,7 @@ FROM golang:1.26.0 RUN apt-get update && apt-get install -y --no-install-recommends \ - git ca-certificates zip gpg && rm -rf /var/lib/apt/lists/* + git ca-certificates zip gpg python3 && rm -rf /var/lib/apt/lists/* # Add GPG key of Alex Bosworth to verify release tag signature. RUN gpg --keyserver keys.openpgp.org \ diff --git a/release.sh b/release.sh index 88a208500..7ab767d3d 100755 --- a/release.sh +++ b/release.sh @@ -218,6 +218,9 @@ commit=$(git --git-dir "${SCRIPT_DIR}/.git" rev-parse HEAD) green " - Checkout commit ${commit} in ${BUILD_DIR}" git checkout -b build-branch "$commit" +green " - Checking release notes" +./scripts/release-notes.py release "${1:-untagged}" + green " - Checking tag $1" check_tag $1 diff --git a/scripts/release-notes.py b/scripts/release-notes.py new file mode 100755 index 000000000..56f8c29f5 --- /dev/null +++ b/scripts/release-notes.py @@ -0,0 +1,349 @@ +#!/usr/bin/env python3 + +"""Rotate and validate Loop release notes.""" + +import argparse +import datetime +import os +import re +import stat +import subprocess +import sys +import tempfile +from pathlib import Path + + +ROOT = Path(__file__).resolve().parent.parent +NOTES = ROOT / "docs" / "release-notes" +NEXT = NOTES / "release-notes-next.md" +TEMPLATE = NOTES / "release-notes-template.md" +README = NOTES / "README.md" +NEXT_PATH = NEXT.relative_to(ROOT) +TEMPLATE_PATH = TEMPLATE.relative_to(ROOT) +VERSION_PATH = Path("version.go") + +REPOSITORY_URL = "https://github.com/lightninglabs/loop" +NEXT_LINK = "[Next release](release-notes-next.md)" +NEXT_ROW = "| [Next release](release-notes-next.md) | Unreleased | No changes yet |" +FIELDS = ( + "Release date", + "Release page", + "Previous release", + "Next release", +) +FIELD_RE = re.compile( + r"^- \*\*(?P[^*\n]+):\*\*(?P[^\n]*)" + r"(?P(?:\n(?: {2,}|\t)[^\n]*)*)", + re.MULTILINE, +) +RELEASE_RE = re.compile( + r"^v?(?P[0-9]+\.[0-9]+\.[0-9]+)" + r"-beta(?P(?:-[0-9A-Za-z][0-9A-Za-z.-]*)?)$" +) +LINK_RE = re.compile(r"^\[(?P