diff --git a/.github/prompts/release-announcement.prompt.yml b/.github/prompts/release-announcement.prompt.yml new file mode 100644 index 0000000000..ce879bde8f --- /dev/null +++ b/.github/prompts/release-announcement.prompt.yml @@ -0,0 +1,68 @@ +# AI prompt for maintaining ReleaseAnnouncement.md. +# Used by .github/workflows/update-release-announcement.yml via actions/ai-inference. +# Edit this file to adjust the style or rules without touching the workflow YAML. + +messages: + - role: system + content: | + You are a technical writer maintaining the working Release Announcement draft for Jamulus — + a free, open-source application that lets musicians rehearse, perform, and jam together in + real time over the internet. + + The announcement is a single flat list of bullet points covering user-relevant changes, + written in the same friendly, conversational style used in real Jamulus beta and release + announcements. Here are real examples of entries from those announcements: + + - the big one: extended SRV record support ([@rdica](https://github.com/rdica) and [@softins](https://github.com/softins)) + - Windows and macOS moving to Qt 6.10.2 + - a new Japanese translation ([@tsukurun](https://github.com/tsukurun)) + - fixed a crash in the Connect dialog ([@ann0see](https://github.com/ann0see)) + - Windows: Jamulus now prevents the screen saver from activating while you are connected ([@ann0see](https://github.com/ann0see)) + - added a JSON-RPC method to control server directory settings ([@pljones](https://github.com/pljones)) + - client RPC support for controlling fader levels ([@corrados](https://github.com/corrados)) + + Rules: + - Add each new change as a bullet point (- ) in the "Here's what's new" section of the + document. Do not add, remove, or modify any headings, the maintainer note block, or the + REMINDER section. + - Write in plain, friendly language. Use past tense for bug fixes, present tense for new + features or improvements. + - Use the CHANGELOG: line in the PR description (if present) as the primary source for + the description. Strip the category prefix (Client:, Server:, Build:, Tools:, etc.) + unless keeping it adds helpful context for a non-technical reader — e.g. keep "Windows:", + "macOS:", "iOS:", "Android:" for OS-specific changes; keep "Server:" when distinguishing + a server-only change is genuinely useful. + - Always credit contributors: ([@username](https://github.com/username)) + For multiple contributors: ([@a](https://github.com/a) and [@b](https://github.com/b)) + - Only include changes that are relevant to end users or server operators. + Omit purely internal changes: CI configuration, build system, code style, developer + tooling, and routine dependency bumps — unless they have a direct, noticeable impact on + users (e.g. a bundled library upgrade that fixes a crash or enables a new feature). + - Place more impactful changes nearer the top of the bullet list. + - Preserve all existing bullet points — never remove, merge, or rewrite them. + - If this PR introduces no user-relevant changes, return the announcement completely + unchanged. + - Output the COMPLETE updated Markdown document and nothing else. Do not add any + explanation, preamble, or commentary outside the document. + + - role: user + content: | + Current working announcement: + --- + {{current_announcement}} + --- + + Newly merged pull request: + {{pr_info}} + --- + + Update the Release Announcement to include any user-relevant changes from this PR. + Return the complete updated Markdown document only. + +model: openai/gpt-4o-mini +modelParameters: + # High token limit to ensure the full document is always returned without truncation. + # The default max-tokens in actions/ai-inference is only 200, which would cut off the document. + maxCompletionTokens: 16384 + # Low temperature for consistent, deterministic output when editing a structured document. + temperature: 0.1 diff --git a/.github/pull_request_template.md b/.github/pull_request_template.md index d267c12c91..025fe36750 100644 --- a/.github/pull_request_template.md +++ b/.github/pull_request_template.md @@ -4,7 +4,7 @@ -CHANGELOG: +CHANGELOG: **Context: Fixes an issue?** diff --git a/.github/release-announcement-template.md b/.github/release-announcement-template.md new file mode 100644 index 0000000000..0a8713d662 --- /dev/null +++ b/.github/release-announcement-template.md @@ -0,0 +1,25 @@ +# Jamulus Next Release — Working Announcement Draft + +> **Note for maintainers:** This is a working draft, automatically updated by GitHub Copilot +> as PRs are merged to `main`. Please review, polish, and publish to +> [GitHub Discussions (Announcements)](https://github.com/orgs/jamulussoftware/discussions) +> and other channels when the release is ready. +> +> Run [`tools/get_release_contributors.py`](tools/get_release_contributors.py) to compile +> the full contributor list before publishing. +> +> See the [ChangeLog](ChangeLog) for the complete technical record of all changes. + +Here's what's new in the next release of Jamulus: + + + +As always, all feedback on the new version is welcome. Please raise any problems in a new bug report or discussion topic. + +--- + +**REMINDER:** Those of you with virus checkers are likely to find the Windows installer incorrectly flagged as a virus. This is because the installer is open source and virus checkers cannot be bothered to check what it installs, so assume that it's going to be malign. If you download the installer _only from the official release_, you should be safe to ignore any warning. + +--- + +*This draft is automatically maintained by the [Update Release Announcement](.github/workflows/update-release-announcement.yml) workflow.* diff --git a/.github/workflows/autobuild.yml b/.github/workflows/autobuild.yml index 805ff819c1..261c3512f3 100644 --- a/.github/workflows/autobuild.yml +++ b/.github/workflows/autobuild.yml @@ -39,6 +39,9 @@ on: - 'COMPILING.md' - 'COPYING' - 'APPLEAPPSTORE.LICENCE.WAIVER' + - 'ReleaseAnnouncement.md' + - '.github/release-announcement-template.md' + - '.github/prompts/**' - '.github/ISSUE_TEMPLATE/*' - '.github/pull_request_template.md' pull_request: @@ -52,6 +55,9 @@ on: - 'COMPILING.md' - 'COPYING' - 'APPLEAPPSTORE.LICENCE.WAIVER' + - 'ReleaseAnnouncement.md' + - '.github/release-announcement-template.md' + - '.github/prompts/**' - '.github/ISSUE_TEMPLATE/*' - '.github/pull_request_template.md' diff --git a/.github/workflows/update-release-announcement.yml b/.github/workflows/update-release-announcement.yml new file mode 100644 index 0000000000..913ab10569 --- /dev/null +++ b/.github/workflows/update-release-announcement.yml @@ -0,0 +1,253 @@ +name: Update Release Announcement + +# This workflow maintains ReleaseAnnouncement.md — a working draft of the release +# announcement for Client users and Server operators — separate from the technical ChangeLog. +# +# On every merged PR to main: GitHub Copilot updates the draft with any user-relevant +# changes from that PR, in the same conversational bullet-point style used in real +# Jamulus beta/release announcements on GitHub Discussions. +# +# On every push to an autobuild* branch: GitHub Copilot updates the draft on that branch +# with any user-relevant changes from the HEAD commit. This lets developers preview how +# their changes would appear in the announcement before the PR is merged. +# +# On every full release tag (r__, no suffix): the draft is reset to the pristine +# template, ready for the next development cycle. Pre-release tags (beta, rc) do NOT reset +# the draft, so it can continue to build up until the final release. +# +# Security note (pull_request_target): +# - The workflow file and the AI prompt always come from main, never from the PR branch. +# - PR content is written to a temp file via an env variable before being passed to the +# AI — it never touches a YAML value directly, preventing injection issues. +# - No code from the PR is ever checked out or executed. +# See: https://securitylab.github.com/research/github-actions-preventing-pwn-requests/ + +on: + pull_request_target: + types: + - closed + branches: + - main + push: + branches: + - "autobuild**" + tags: + - "r*" + +permissions: {} + +jobs: + update-announcement: + name: Update announcement for merged PR + # Only run on actual merges (not just closed PRs) in the main jamulussoftware repo. + if: >- + github.repository_owner == 'jamulussoftware' && + github.event.pull_request.merged == true + runs-on: ubuntu-latest + permissions: + contents: write + models: read + + steps: + - uses: actions/checkout@v6 + with: + # Always check out the base branch (main), never the PR branch. + ref: main + + - name: Check if announcement update should be skipped + id: check-skip + env: + PR_BODY: ${{ github.event.pull_request.body }} + run: | + # Skip when the PR author explicitly marked the change as not user-facing. + if printf '%s\n' "$PR_BODY" | grep -qE '^CHANGELOG:[[:space:]]*SKIP[[:space:]]*$'; then + echo "Skipping: PR is marked CHANGELOG: SKIP" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Prepare PR metadata for AI prompt + id: prep-pr-info + if: steps.check-skip.outputs.skip == 'false' + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + PR_TITLE: ${{ github.event.pull_request.title }} + PR_AUTHOR: ${{ github.event.pull_request.user.login }} + PR_BODY: ${{ github.event.pull_request.body }} + run: | + # Write all PR metadata to a temp file so it can be safely passed to the AI + # via file_input, avoiding any YAML injection from PR body content. + printf 'PR #%s — %s\nby @%s\n\n%s\n' \ + "$PR_NUMBER" "$PR_TITLE" "$PR_AUTHOR" "$PR_BODY" \ + > "${RUNNER_TEMP}/pr_info.txt" + + - name: Update Release Announcement with GitHub Copilot + id: update-announcement + if: steps.check-skip.outputs.skip == 'false' + uses: actions/ai-inference@v1 + with: + prompt-file: '.github/prompts/release-announcement.prompt.yml' + file_input: | + current_announcement: ReleaseAnnouncement.md + pr_info: ${{ runner.temp }}/pr_info.txt + + - name: Write updated Release Announcement + if: steps.check-skip.outputs.skip == 'false' + env: + RESPONSE_FILE: ${{ steps.update-announcement.outputs.response-file }} + run: | + # Guard against an empty or missing response, which would wipe the file. + if [ ! -s "$RESPONSE_FILE" ]; then + echo "Warning: AI returned an empty response — skipping update." + exit 0 + fi + cp "$RESPONSE_FILE" ReleaseAnnouncement.md + + - name: Commit and push updated announcement + if: steps.check-skip.outputs.skip == 'false' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "actions@github.com" + git config --global user.name "github-actions[bot]" + git add ReleaseAnnouncement.md + # Exit cleanly if the AI returned the document unchanged (nothing to commit). + git diff --staged --quiet && exit 0 + git commit -m "docs: update Release Announcement for PR #${{ github.event.pull_request.number }}" + git push + + update-announcement-on-autobuild: + name: Update announcement for autobuild push + # Run on autobuild branch pushes in the main jamulussoftware repo. + # Skip when the pusher is the bot itself to prevent an infinite commit loop: + # autobuild.yml already has ReleaseAnnouncement.md in paths-ignore, so the + # bot commit will not re-trigger the build, but it would re-trigger this workflow. + if: >- + github.repository_owner == 'jamulussoftware' && + startsWith(github.ref, 'refs/heads/autobuild') && + github.actor != 'github-actions[bot]' + runs-on: ubuntu-latest + permissions: + contents: write + models: read + + steps: + - uses: actions/checkout@v6 + with: + # Check out the autobuild branch being pushed, so the announcement is + # updated and committed directly on that branch. + ref: ${{ github.ref }} + + - name: Check if announcement update should be skipped + id: check-skip + env: + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + run: | + # Skip when the commit message explicitly marks the change as not user-facing. + if printf '%s\n' "$COMMIT_MESSAGE" | grep -qE '^CHANGELOG:[[:space:]]*SKIP[[:space:]]*$'; then + echo "Skipping: commit is marked CHANGELOG: SKIP" + echo "skip=true" >> "$GITHUB_OUTPUT" + else + echo "skip=false" >> "$GITHUB_OUTPUT" + fi + + - name: Prepare commit metadata for AI prompt + id: prep-commit-info + if: steps.check-skip.outputs.skip == 'false' + env: + COMMIT_SHA: ${{ github.event.head_commit.id }} + COMMIT_MESSAGE: ${{ github.event.head_commit.message }} + COMMIT_AUTHOR: ${{ github.event.head_commit.author.username }} + BRANCH_NAME: ${{ github.ref_name }} + run: | + # Write commit metadata to a temp file so it can be safely passed to the AI + # via file_input, avoiding any YAML injection from the commit message content. + printf 'Commit %s on branch %s\nby @%s\n\n%s\n' \ + "$COMMIT_SHA" "$BRANCH_NAME" "$COMMIT_AUTHOR" "$COMMIT_MESSAGE" \ + > "${RUNNER_TEMP}/commit_info.txt" + + - name: Update Release Announcement with GitHub Copilot + id: update-announcement + if: steps.check-skip.outputs.skip == 'false' + uses: actions/ai-inference@v1 + with: + prompt-file: '.github/prompts/release-announcement.prompt.yml' + file_input: | + current_announcement: ReleaseAnnouncement.md + pr_info: ${{ runner.temp }}/commit_info.txt + + - name: Write updated Release Announcement + if: steps.check-skip.outputs.skip == 'false' + env: + RESPONSE_FILE: ${{ steps.update-announcement.outputs.response-file }} + run: | + # Guard against an empty or missing response, which would wipe the file. + if [ ! -s "$RESPONSE_FILE" ]; then + echo "Warning: AI returned an empty response — skipping update." + exit 0 + fi + cp "$RESPONSE_FILE" ReleaseAnnouncement.md + + - name: Commit and push updated announcement + if: steps.check-skip.outputs.skip == 'false' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "actions@github.com" + git config --global user.name "github-actions[bot]" + git add ReleaseAnnouncement.md + # Exit cleanly if the AI returned the document unchanged (nothing to commit). + git diff --staged --quiet && exit 0 + git commit -m "docs: update Release Announcement for ${{ github.sha }}" + git push + + reset-after-release: + name: Reset announcement after full release + # Only run on tag pushes in the main jamulussoftware repo. + if: >- + github.repository_owner == 'jamulussoftware' && + github.event_name == 'push' + runs-on: ubuntu-latest + permissions: + contents: write + + steps: + - uses: actions/checkout@v6 + with: + ref: main + + - name: Check if this is a full (non-prerelease) release tag + id: check-tag + run: | + # Match only clean version tags like r3_12_0. + # Tags with any suffix (e.g. r3_12_0beta1, r3_12_0rc1) are pre-releases + # and intentionally do NOT reset the draft, so it keeps building up + # towards the final release announcement. + if [[ "${GITHUB_REF_NAME}" =~ ^r([0-9]+)_([0-9]+)_([0-9]+)$ ]]; then + major="${BASH_REMATCH[1]}" + minor="${BASH_REMATCH[2]}" + patch="${BASH_REMATCH[3]}" + echo "is_full_release=true" >> "$GITHUB_OUTPUT" + echo "version=${major}.${minor}.${patch}" >> "$GITHUB_OUTPUT" + else + echo "is_full_release=false" >> "$GITHUB_OUTPUT" + fi + + - name: Reset Release Announcement to template + if: steps.check-tag.outputs.is_full_release == 'true' + run: | + cp .github/release-announcement-template.md ReleaseAnnouncement.md + + - name: Commit and push reset announcement + if: steps.check-tag.outputs.is_full_release == 'true' + env: + GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + git config --global user.email "actions@github.com" + git config --global user.name "github-actions[bot]" + git add ReleaseAnnouncement.md + git diff --staged --quiet && exit 0 + git commit -m "docs: reset Release Announcement after v${{ steps.check-tag.outputs.version }} release" + git push + diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index df801e67b0..b1601559b2 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -100,6 +100,8 @@ Admins reserve the right to do this as they see fit. The ChangeLog must be updated for each new feature or bug fix. Please include a single-sentence suggestion for that as part of your pull request description after the `CHANGELOG: ` keyword. Do not modify the ChangeLog file as part of your PR as it will lead to conflicts. +The same `CHANGELOG:` line is also used to automatically update the working [Release Announcement draft](ReleaseAnnouncement.md), which is a curated summary for end users and server operators. Use `CHANGELOG: SKIP` to skip both the ChangeLog entry and the Release Announcement update for purely internal changes (CI, build system, code style fixes, etc.). + If you are a first-time contributor/translator, please add your name to the contributors/translators list in the About dialog of Jamulus (see in `src/util.cpp` in the constructor function `CAboutDlg::CAboutDlg()`). ### Merging pull requests diff --git a/ReleaseAnnouncement.md b/ReleaseAnnouncement.md new file mode 100644 index 0000000000..0a8713d662 --- /dev/null +++ b/ReleaseAnnouncement.md @@ -0,0 +1,25 @@ +# Jamulus Next Release — Working Announcement Draft + +> **Note for maintainers:** This is a working draft, automatically updated by GitHub Copilot +> as PRs are merged to `main`. Please review, polish, and publish to +> [GitHub Discussions (Announcements)](https://github.com/orgs/jamulussoftware/discussions) +> and other channels when the release is ready. +> +> Run [`tools/get_release_contributors.py`](tools/get_release_contributors.py) to compile +> the full contributor list before publishing. +> +> See the [ChangeLog](ChangeLog) for the complete technical record of all changes. + +Here's what's new in the next release of Jamulus: + + + +As always, all feedback on the new version is welcome. Please raise any problems in a new bug report or discussion topic. + +--- + +**REMINDER:** Those of you with virus checkers are likely to find the Windows installer incorrectly flagged as a virus. This is because the installer is open source and virus checkers cannot be bothered to check what it installs, so assume that it's going to be malign. If you download the installer _only from the official release_, you should be safe to ignore any warning. + +--- + +*This draft is automatically maintained by the [Update Release Announcement](.github/workflows/update-release-announcement.yml) workflow.*