From a08b26addd153dd90aa7e06d758a712e83f56edd Mon Sep 17 00:00:00 2001 From: Daniel Vianna <1708810+dmvianna@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:22:25 +1000 Subject: [PATCH 1/2] Use changelog-driven release notes --- .beads/issues.jsonl | 1 + .github/workflows/linux-release.yml | 2 +- .github/workflows/macos-release.yml | 2 +- .github/workflows/release-reusable.yml | 19 +++++++++- docs/development.md | 10 +++++ scripts/release-notes-from-changelog.sh | 50 +++++++++++++++++++++++++ 6 files changed, 80 insertions(+), 4 deletions(-) create mode 100755 scripts/release-notes-from-changelog.sh diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 207bd97..1bc3a7d 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -2,6 +2,7 @@ {"id":"devloop-8km","title":"Implement config and workflow engine","description":"Load config, watch paths, classify events, and execute ordered workflows against named processes and hooks.","status":"closed","priority":2,"issue_type":"feature","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed","dependencies":[{"issue_id":"devloop-8km","depends_on_id":"devloop-0nx","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]} {"id":"devloop-c6l","title":"Polish 0.8.0 release docs and test ergonomics","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T07:12:58Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T07:20:27Z","closed_at":"2026-04-08T07:20:27Z","close_reason":"Closed"} {"id":"devloop-d81","title":"Move real client config out of devloop examples","description":"Keep only generic examples in devloop and move the working blog config into the client repository root.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:31:27Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:39:07Z","closed_at":"2026-03-24T02:39:07Z","close_reason":"Kept only generic examples in devloop and moved the working blog config into the client repo root."} +{"id":"devloop-dxz","title":"Use changelog-driven GitHub release notes and backfill old releases","status":"in_progress","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T08:18:49Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T08:19:31Z"} {"id":"devloop-dzy","title":"Make client hook paths repo-relative","description":"Resolve devloop config command paths relative to the client repo or config, not the tool checkout.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:16:34Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:22:17Z","closed_at":"2026-03-24T02:22:17Z","close_reason":"Closed"} {"id":"devloop-mml","title":"Address roborev findings on state ownership and client-specific URL composition","status":"closed","priority":1,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:44:51Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:53:31Z","closed_at":"2026-03-24T02:53:31Z","close_reason":"Shared session state is now owned in memory, generic state templating replaced blog-specific derivation, redundant writes are skipped, and the review job was addressed."} {"id":"devloop-nmu","title":"Add blog client example and verification","description":"Create example config/hooks for the blog repo and verify the tool runs against it.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed","dependencies":[{"issue_id":"devloop-nmu","depends_on_id":"devloop-8km","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]} diff --git a/.github/workflows/linux-release.yml b/.github/workflows/linux-release.yml index f04a039..3cfa435 100644 --- a/.github/workflows/linux-release.yml +++ b/.github/workflows/linux-release.yml @@ -15,4 +15,4 @@ jobs: runner: ubuntu-latest target: x86_64-unknown-linux-gnu archive_name: devloop-x86_64-unknown-linux-gnu.tar.gz - generate_release_notes: true + publish_release_notes: true diff --git a/.github/workflows/macos-release.yml b/.github/workflows/macos-release.yml index fca93a5..699414d 100644 --- a/.github/workflows/macos-release.yml +++ b/.github/workflows/macos-release.yml @@ -15,4 +15,4 @@ jobs: runner: macos-14 target: aarch64-apple-darwin archive_name: devloop-aarch64-apple-darwin.tar.gz - generate_release_notes: false + publish_release_notes: false diff --git a/.github/workflows/release-reusable.yml b/.github/workflows/release-reusable.yml index 40d1465..b24d175 100644 --- a/.github/workflows/release-reusable.yml +++ b/.github/workflows/release-reusable.yml @@ -12,7 +12,7 @@ on: archive_name: required: true type: string - generate_release_notes: + publish_release_notes: required: true type: boolean @@ -56,9 +56,24 @@ jobs: cp "target/${{ inputs.target }}/release/devloop" dist/devloop tar -C dist -czf "${{ inputs.archive_name }}" devloop + - name: Extract release notes from changelog + if: inputs.publish_release_notes + shell: bash + run: | + set -euo pipefail + ./scripts/release-notes-from-changelog.sh "${GITHUB_REF_NAME#v}" "${GITHUB_REPOSITORY}" > release-notes.md + - name: Publish GitHub release asset + if: inputs.publish_release_notes + uses: softprops/action-gh-release@v2 + with: + files: ${{ inputs.archive_name }} + body_path: release-notes.md + fail_on_unmatched_files: true + + - name: Publish GitHub release asset without notes + if: ${{ !inputs.publish_release_notes }} uses: softprops/action-gh-release@v2 with: files: ${{ inputs.archive_name }} - generate_release_notes: ${{ inputs.generate_release_notes }} fail_on_unmatched_files: true diff --git a/docs/development.md b/docs/development.md index ac064c1..ea45cbe 100644 --- a/docs/development.md +++ b/docs/development.md @@ -17,6 +17,16 @@ cargo clippy --all-targets --all-features -- -D warnings checks that `devloop run` can start, begin watching, react to one file change, and shut down cleanly. +## Releases + +`devloop` releases are tag-driven. Push a tag such as `v0.8.0` and the +platform release workflows publish the Linux and macOS archives. + +GitHub release notes are generated from the matching section in +`CHANGELOG.md`, not from GitHub's automatic PR summary. The Linux +release job publishes the changelog text and compare link; the macOS job +only attaches its archive asset to that same release. + ## Opt-in watch flake smoke The repeated-edit watch flake smoke test is intentionally opt-in. It is diff --git a/scripts/release-notes-from-changelog.sh b/scripts/release-notes-from-changelog.sh new file mode 100755 index 0000000..bf71f77 --- /dev/null +++ b/scripts/release-notes-from-changelog.sh @@ -0,0 +1,50 @@ +#!/usr/bin/env bash +set -euo pipefail + +if [[ $# -lt 1 || $# -gt 2 ]]; then + echo "usage: $0 [repo-slug]" >&2 + exit 1 +fi + +version="$1" +repo_slug="${2:-}" +changelog_path="$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)/CHANGELOG.md" + +section="$( + awk -v version="$version" ' + $0 ~ "^## \\[" version "\\] - " { in_section=1 } + in_section { + if ($0 ~ "^## \\[" && $0 !~ "^## \\[" version "\\] - ") { + exit + } + print + } + ' "$changelog_path" +)" + +if [[ -z "$section" ]]; then + echo "could not find changelog section for version $version" >&2 + exit 1 +fi + +printf '%s\n' "$section" + +if [[ -n "$repo_slug" ]]; then + previous_version="$( + awk -v version="$version" ' + $0 ~ "^## \\[" version "\\] - " { found=1; next } + found && $0 ~ /^## \[[^]]+\] - / { + line = $0 + sub(/^## \[/, "", line) + sub(/\].*/, "", line) + print line + exit + } + ' "$changelog_path" + )" + + if [[ -n "$previous_version" && "$previous_version" != "Unreleased" ]]; then + printf '\n**Full Changelog**: https://github.com/%s/compare/v%s...v%s\n' \ + "$repo_slug" "$previous_version" "$version" + fi +fi From c990e39e341064c495c72a974e646be6e879083e Mon Sep 17 00:00:00 2001 From: Daniel Vianna <1708810+dmvianna@users.noreply.github.com> Date: Wed, 8 Apr 2026 18:27:21 +1000 Subject: [PATCH 2/2] Sync bd issue state --- .beads/issues.jsonl | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.beads/issues.jsonl b/.beads/issues.jsonl index 1bc3a7d..748d73a 100644 --- a/.beads/issues.jsonl +++ b/.beads/issues.jsonl @@ -2,7 +2,7 @@ {"id":"devloop-8km","title":"Implement config and workflow engine","description":"Load config, watch paths, classify events, and execute ordered workflows against named processes and hooks.","status":"closed","priority":2,"issue_type":"feature","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed","dependencies":[{"issue_id":"devloop-8km","depends_on_id":"devloop-0nx","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]} {"id":"devloop-c6l","title":"Polish 0.8.0 release docs and test ergonomics","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T07:12:58Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T07:20:27Z","closed_at":"2026-04-08T07:20:27Z","close_reason":"Closed"} {"id":"devloop-d81","title":"Move real client config out of devloop examples","description":"Keep only generic examples in devloop and move the working blog config into the client repository root.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:31:27Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:39:07Z","closed_at":"2026-03-24T02:39:07Z","close_reason":"Kept only generic examples in devloop and moved the working blog config into the client repo root."} -{"id":"devloop-dxz","title":"Use changelog-driven GitHub release notes and backfill old releases","status":"in_progress","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T08:18:49Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T08:19:31Z"} +{"id":"devloop-dxz","title":"Use changelog-driven GitHub release notes and backfill old releases","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-04-08T08:18:49Z","created_by":"Daniel Vianna","updated_at":"2026-04-08T08:26:59Z","closed_at":"2026-04-08T08:26:59Z","close_reason":"Closed"} {"id":"devloop-dzy","title":"Make client hook paths repo-relative","description":"Resolve devloop config command paths relative to the client repo or config, not the tool checkout.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:16:34Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:22:17Z","closed_at":"2026-03-24T02:22:17Z","close_reason":"Closed"} {"id":"devloop-mml","title":"Address roborev findings on state ownership and client-specific URL composition","status":"closed","priority":1,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T02:44:51Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:53:31Z","closed_at":"2026-03-24T02:53:31Z","close_reason":"Shared session state is now owned in memory, generic state templating replaced blog-specific derivation, redundant writes are skipped, and the review job was addressed."} {"id":"devloop-nmu","title":"Add blog client example and verification","description":"Create example config/hooks for the blog repo and verify the tool runs against it.","status":"closed","priority":2,"issue_type":"task","owner":"1708810+dmvianna@users.noreply.github.com","created_at":"2026-03-24T01:15:00Z","created_by":"Daniel Vianna","updated_at":"2026-03-24T02:09:33Z","closed_at":"2026-03-24T02:09:33Z","close_reason":"Closed","dependencies":[{"issue_id":"devloop-nmu","depends_on_id":"devloop-8km","type":"blocks","created_at":"2026-03-24T12:15:07Z","created_by":"Daniel Vianna","metadata":"{}"}]}