Skip to content

Add CHANGELOG.md management and update CI release workflow#1331

Open
rohan-pandeyy wants to merge 2 commits into
AOSSIE-Org:mainfrom
rohan-pandeyy:feat/changelogs
Open

Add CHANGELOG.md management and update CI release workflow#1331
rohan-pandeyy wants to merge 2 commits into
AOSSIE-Org:mainfrom
rohan-pandeyy:feat/changelogs

Conversation

@rohan-pandeyy

@rohan-pandeyy rohan-pandeyy commented Jun 20, 2026

Copy link
Copy Markdown
Member

Closes #1311

Description

PictoPy has no CHANGELOG.md and no mechanism to verify a changelog entry exists before a release is published. This PR adds a Keep a Changelog file backfilled from real release history, a CI gate that blocks releases without a matching entry, and a manually-triggered workflow that scaffolds version bumps + changelog drafts into a reviewable PR.

This builds on the version bump script introduced in #1306.

What's included

1. CHANGELOG.md

Added at the repository root, following Keep a Changelog. Backfilled [1.1.0] entry derived from actual merged PRs (cross-checked against Release Drafter output, grouped into Added / Changed / Fixed / Documentation, linked back to each PR).

2. CI release gate (build-and-release.yml)

  • New validate-changelog job, runs first on every release trigger
  • Checks CHANGELOG.md for a ## [X.Y.Z] entry matching the release tag
  • Uses ${{ github.event.release.tag_name || github.event.inputs.tag }} — not github.ref_name, which resolves to the branch name on manual triggers and would always fail the check
  • publish-tauri's needs array updated to include validate-changelog

3. Release prep workflow (release-prep.yml)

  • Manual trigger only (workflow_dispatch), takes a version input — no scheduled/automatic cadence
  • Bumps versions via the existing npm run version:bump script
  • Drafts a new CHANGELOG section from merged PRs since the last stable tag, grouped by Release Drafter label
  • Opens a PR (release/prep-x.y.z) with a checklist for the reviewer — does not touch main directly or publish anything

4. Documentation (CONTRIBUTING.md)

Two new subsections under ## Release Management:

  • Automated release prep — how to trigger the workflow and what to check before merging its PR
  • Release validation (CI gate) — what validate-changelog checks and why the prep PR must be merged before creating the GitHub Release

How it works

Release prep flow (manual trigger → version bump + changelog draft → PR → review/merge → create release):

image

Build & release CI gate (changelog validation runs in parallel with platform builds; publish only proceeds if both succeed):

image

Testing

  • Verified bump-version.mjs round-trips correctly (bump → diff → revert → empty diff)
  • Fixed a YAML indentation bug in release-prep.yml where multi-line bash string continuations broke the workflow's block scalar parsing
  • Ran the workflow end-to-end on a fork branch — confirmed it executes without errors, bumps versions, and opens a PR correctly
  • Note: fork testing can't fully exercise LAST_TAG detection or PR grouping, since GitHub forks don't copy tags or merged-PR history by default. The workflow falls back gracefully (empty draft) when no tag/PR data is available — confirmed this fallback path doesn't crash
  • Validated the validate-changelog grep logic locally against both a matching and non-matching version string

Note

  • This workflow's blast radius is intentionally small: it only opens a PR, never pushes to main or publishes a release
  • The CHANGELOG [1.1.0] backfill was manually curated against actual merged PRs rather than generated, to avoid inaccurate entries
  • First real validation of tag/PR-detection logic against live data will happen the next time someone runs Release Prep for an actual release — the human-review step in that PR is the safety net if anything's off

AI Usage Disclosure:

We encourage contributors to use AI tools responsibly when creating Pull Requests. While AI can be a valuable aid, it is essential to ensure that your contributions meet the task requirements, build successfully, include relevant tests, and pass all linters. Submissions that do not meet these standards may be closed without warning to maintain the quality and integrity of the project. Please take the time to understand the changes you are proposing and their impact. AI slop is strongly discouraged and may lead to banning and blocking. Do not spam our repos with AI slop.

Check one of the checkboxes below:

  • This PR does not contain AI-generated code at all.
  • This PR contains AI-generated code. I have read the AI Usage Policy and this PR complies with this policy. I have tested the code locally and I am responsible for it.

I have used the following AI models and tools: Claude, Gemini

Checklist

  • My PR addresses a single issue, fixes a single bug or makes a single improvement.
  • My code follows the project's code style and conventions
  • If applicable, I have made corresponding changes or additions to the documentation
  • If applicable, I have made corresponding changes or additions to tests
  • My changes generate no new warnings or errors
  • I have joined the Discord server and I will share a link to this PR with the project maintainers there
  • I have read the Contribution Guidelines
  • Once I submit my PR, CodeRabbit AI will automatically review it and I will address CodeRabbit's comments.
  • I have filled this PR template completely and carefully, and I understand that my PR may be closed without review otherwise.

Summary by CodeRabbit

  • New Features

    • Introduced automated release preparation workflow that handles version bumping across project manifests and generates changelog sections automatically
    • Added changelog validation as a CI gate to ensure release notes are present before publishing
  • Documentation

    • Enhanced contribution guidelines with detailed release management procedures including automated prep workflow and validation steps

@github-actions github-actions Bot added CI/CD GSoC 2026 documentation Improvements or additions to documentation enhancement New feature or request labels Jun 20, 2026
@coderabbitai

coderabbitai Bot commented Jun 20, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Walkthrough

Introduces a Release Prep GitHub Actions workflow that bumps versions, drafts CHANGELOG sections from merged PRs, and opens a release PR. Adds a validate-changelog CI job to build-and-release.yml that blocks publishing if CHANGELOG.md lacks a matching version entry. Seeds CHANGELOG.md with the 1.1.0 release and updates CONTRIBUTING.md with release process documentation.

Changes

Release Automation Pipeline

Layer / File(s) Summary
validate-changelog CI gate
.github/workflows/build-and-release.yml
Adds validate-changelog job that extracts the release version from the tag or manual input, greps CHANGELOG.md for a ## [X.Y.Z] header, and fails if missing; publish-tauri is updated to depend on this job.
Release Prep workflow scaffolding
.github/workflows/release-prep.yml
New Release Prep workflow with workflow_dispatch version input, write permissions for contents and pull-requests, full-history checkout, Node.js setup, and npm run version:bump step.
CHANGELOG draft generation and insertion
.github/workflows/release-prep.yml
Shell + embedded Python script that identifies the latest stable tag, collects merged PRs via gh pr list, categorizes them by label into Added/Fixed/Changed/Other sections, and inserts the generated block into CHANGELOG.md after the ## [Unreleased] marker.
Branch creation, commit, and PR open
.github/workflows/release-prep.yml
Creates and pushes release/prep-<version> branch committing package.json, frontend/package.json, frontend/src-tauri/Cargo.toml, and CHANGELOG.md, then opens a PR into main with a verification checklist.
Initial CHANGELOG.md and contribution docs
CHANGELOG.md, CONTRIBUTING.md
Seeds CHANGELOG.md with an Unreleased header and the full 1.1.0 release section (Added, Changed, Fixed, Documentation); adds Automated release prep and Release validation subsections to CONTRIBUTING.md.

Sequence Diagram(s)

sequenceDiagram
  actor Developer
  participant release-prep.yml as Release Prep Workflow
  participant VersionBump as npm run version:bump
  participant GitHubAPI as gh CLI / GitHub API
  participant CHANGELOG as CHANGELOG.md
  participant MainBranch as main branch

  Developer->>release-prep.yml: workflow_dispatch(version=X.Y.Z)
  release-prep.yml->>VersionBump: bump manifests to X.Y.Z
  release-prep.yml->>GitHubAPI: gh pr list (merged since last tag)
  GitHubAPI-->>release-prep.yml: PR list with labels
  release-prep.yml->>CHANGELOG: insert ## [X.Y.Z] section after Unreleased
  release-prep.yml->>GitHubAPI: git push release/prep-X.Y.Z
  release-prep.yml->>GitHubAPI: gh pr create → main
  GitHubAPI-->>Developer: PR opened for review

  Developer->>MainBranch: merge release prep PR
  Developer->>GitHubAPI: create GitHub Release (tag vX.Y.Z)
  GitHubAPI->>release-prep.yml: triggers build-and-release.yml
  Note over GitHubAPI: validate-changelog checks CHANGELOG.md
  GitHubAPI-->>MainBranch: publish-tauri proceeds if entry found
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • AOSSIE-Org/PictoPy#457: Modifies the same publish-tauri job in build-and-release.yml, directly intersecting with this PR's addition of the validate-changelog dependency gate.
  • AOSSIE-Org/PictoPy#1308: Introduced the npm run version:bump script and scripts/bump-version.mjs that the new Release Prep workflow calls to update manifest files.

Suggested labels

Documentation

🐇 A changelog was missing, oh what a mess,
Now every release must pass the CI test!
A workflow runs, PRs get sorted with care,
"Added", "Fixed", "Changed" — all sections are there.
The bunny hops forward, no version will slip,
With automated prep on each release trip! 🎉

🚥 Pre-merge checks | ✅ 4
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main changes: adding CHANGELOG.md management and updating the CI release workflow to include validation.
Linked Issues check ✅ Passed The PR fully addresses issue #1311 by implementing CHANGELOG.md in Keep a Changelog format, adding CI validation gates, and establishing release management workflows.
Out of Scope Changes check ✅ Passed All changes are directly scoped to changelog management and CI release workflow updates. Documentation updates in CONTRIBUTING.md directly support the new workflows.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (1)
.github/workflows/build-and-release.yml (1)

18-19: ⚡ Quick win

Add persist-credentials: false to the checkout action for better security posture.

Although this job only reads CHANGELOG.md and doesn't push changes, it's a security best practice to avoid persisting credentials when not needed. This reduces the surface area for potential token leakage.

🛡️ Proposed addition
      - name: Checkout code
        uses: actions/checkout@v4
+       with:
+         persist-credentials: false
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-and-release.yml around lines 18 - 19, The checkout
action in the workflow is currently persisting credentials unnecessarily. Add
the `persist-credentials: false` parameter to the `actions/checkout@v4` action
to follow security best practices and reduce the risk of credential leakage,
since this job only needs to read files and does not push any changes.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/build-and-release.yml:
- Line 25: The grep command in the build-and-release workflow uses the VERSION
variable as a regex pattern, which can cause unintended matches if the version
string contains regex metacharacters. Add the -F flag to the grep command to
force literal string matching instead of regex interpretation. This ensures that
the pattern "## [${VERSION}]" is matched exactly as a literal string rather than
being interpreted as a regex pattern.

In @.github/workflows/release-prep.yml:
- Around line 30-31: The workflow expands the raw user-provided `inputs.version`
directly into shell commands at lines 30, 117-120, and 126-141, which creates a
shell injection vulnerability. Validate the version input early in the workflow
before it is used in any run scripts, then either pass the validated version as
an environment variable to the npm run version:bump command or have the Node
script validate and read the input directly rather than interpolating it raw
into the shell command. This ensures validation happens before any shell
interpretation occurs and prevents malicious shell syntax from being executed.
- Around line 97-109: The Python script that inserts changelog content after the
`## [Unreleased]` marker does not check whether a release section header (such
as `## [X.Y.Z]`) already exists in the changelog, causing duplicate sections on
workflow reruns. Extract the version header from the section variable being
inserted, search the existing content for that same header, and only proceed
with the insertion if the header is not already present. This ensures
idempotency when the release-prep workflow runs multiple times for the same
version.
- Line 20: The GitHub Actions workflow is using mutable version tags (v4) for
both actions/checkout and actions/setup-node, which compromises supply-chain
security since tags can be moved to point to different commits without
visibility. Replace the mutable tags `@v4` with full immutable commit SHAs for
both the actions/checkout action on line 20 and the actions/setup-node action on
line 25. This ensures the workflow always uses the exact intended version
regardless of any tag changes.

---

Nitpick comments:
In @.github/workflows/build-and-release.yml:
- Around line 18-19: The checkout action in the workflow is currently persisting
credentials unnecessarily. Add the `persist-credentials: false` parameter to the
`actions/checkout@v4` action to follow security best practices and reduce the
risk of credential leakage, since this job only needs to read files and does not
push any changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 45bb691a-6399-458c-b141-bedb7490c4af

📥 Commits

Reviewing files that changed from the base of the PR and between 9caa450 and 9a3f202.

📒 Files selected for processing (4)
  • .github/workflows/build-and-release.yml
  • .github/workflows/release-prep.yml
  • CHANGELOG.md
  • CONTRIBUTING.md

run: |
RAW_TAG="${{ github.event.release.tag_name || github.event.inputs.tag }}"
VERSION="${RAW_TAG#v}"
if ! grep -q "## \[${VERSION}\]" CHANGELOG.md; then

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major | ⚡ Quick win

Use grep -F for literal string matching to prevent regex injection.

The grep pattern on line 25 treats VERSION as a regex pattern. If a release tag contains regex metacharacters (e.g., v1.2.0.*, which Git allows), the pattern could match unintended CHANGELOG entries, bypassing the validation. Use the -F flag to match the version string literally.

🔒 Proposed fix for literal string matching
- if ! grep -q "## \[${VERSION}\]" CHANGELOG.md; then
+ if ! grep -qF "## [${VERSION}]" CHANGELOG.md; then
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
if ! grep -q "## \[${VERSION}\]" CHANGELOG.md; then
if ! grep -qF "## [${VERSION}]" CHANGELOG.md; then
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/build-and-release.yml at line 25, The grep command in the
build-and-release workflow uses the VERSION variable as a regex pattern, which
can cause unintended matches if the version string contains regex
metacharacters. Add the -F flag to the grep command to force literal string
matching instead of regex interpretation. This ensures that the pattern "##
[${VERSION}]" is matched exactly as a literal string rather than being
interpreted as a regex pattern.

runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Verify all mutable action refs in workflows
rg -nP 'uses:\s*[^@]+@v\d+(\.\d+)?(\.\d+)?\b' .github/workflows

Repository: AOSSIE-Org/PictoPy

Length of output: 4338


🏁 Script executed:

cat -n .github/workflows/release-prep.yml | sed -n '15,30p'

Repository: AOSSIE-Org/PictoPy

Length of output: 548


Pin GitHub Actions to immutable commit SHAs

Lines 20 and 25 use mutable tags (@v4). This weakens workflow supply-chain integrity because tag movement can change behavior without PR visibility.

Affected lines
20:        uses: actions/checkout@v4
25:        uses: actions/setup-node@v4

Replace with pinned commit SHAs, for example: actions/checkout@<full-commit-hash>

🧰 Tools
🪛 zizmor (1.25.2)

[error] 20-20: unpinned action reference (unpinned-uses): action is not pinned to a hash (required by blanket policy)

(unpinned-uses)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-prep.yml at line 20, The GitHub Actions workflow
is using mutable version tags (v4) for both actions/checkout and
actions/setup-node, which compromises supply-chain security since tags can be
moved to point to different commits without visibility. Replace the mutable tags
`@v4` with full immutable commit SHAs for both the actions/checkout action on line
20 and the actions/setup-node action on line 25. This ensures the workflow
always uses the exact intended version regardless of any tag changes.

Source: Linters/SAST tools

Comment on lines +30 to +31
run: npm run version:bump -- ${{ inputs.version }}

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical | ⚡ Quick win

Validate version once and stop interpolating raw ${{ inputs.version }} into shell commands.

Line 30 (and downstream lines in git/gh commands) expands user-provided workflow input directly inside run scripts. That enables shell injection before your Node script validation executes.

Suggested fix
+      - name: Validate release version
+        env:
+          RAW_VERSION: ${{ inputs.version }}
+        run: |
+          if [[ ! "$RAW_VERSION" =~ ^[0-9]+\.[0-9]+\.[0-9]+$ ]]; then
+            echo "Error: version must be X.Y.Z (no v prefix)"
+            exit 1
+          fi
+          echo "VERSION=$RAW_VERSION" >> "$GITHUB_ENV"
+
       - name: Bump version across manifests
-        run: npm run version:bump -- ${{ inputs.version }}
+        run: npm run version:bump -- "$VERSION"
...
       - name: Commit and push prep branch
         run: |
...
-          git checkout -b release/prep-${{ inputs.version }}
+          git checkout -b "release/prep-$VERSION"
...
-          git commit -m "chore: prepare release ${{ inputs.version }}"
-          git push origin release/prep-${{ inputs.version }}
+          git commit -m "chore: prepare release $VERSION"
+          git push origin "release/prep-$VERSION"
...
       - name: Open pull request
...
         run: |
           gh pr create \
-            --title "chore: release ${{ inputs.version }}" \
-            --body "## Release Prep: \`${{ inputs.version }}\`
+            --title "chore: release $VERSION" \
+            --body "## Release Prep: \`$VERSION\`
...
-          - Version bumped to \`${{ inputs.version }}\` across all manifest files
-          - CHANGELOG.md updated with a draft \`[${{ inputs.version }}]\` entry
+          - Version bumped to \`$VERSION\` across all manifest files
+          - CHANGELOG.md updated with a draft \`[$VERSION]\` entry
...
-          After merging, create the GitHub Release with tag \`v${{ inputs.version }}\` to trigger the build pipeline." \
+          After merging, create the GitHub Release with tag \`v$VERSION\` to trigger the build pipeline." \
             --base main \
-            --head release/prep-${{ inputs.version }} \
+            --head "release/prep-$VERSION" \
             --assignee ${{ github.actor }}

Also applies to: 117-120, 126-141

🧰 Tools
🪛 zizmor (1.25.2)

[error] 30-30: code injection via template expansion (template-injection): may expand into attacker-controllable code

(template-injection)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-prep.yml around lines 30 - 31, The workflow
expands the raw user-provided `inputs.version` directly into shell commands at
lines 30, 117-120, and 126-141, which creates a shell injection vulnerability.
Validate the version input early in the workflow before it is used in any run
scripts, then either pass the validated version as an environment variable to
the npm run version:bump command or have the Node script validate and read the
input directly rather than interpolating it raw into the shell command. This
ensures validation happens before any shell interpretation occurs and prevents
malicious shell syntax from being executed.

Source: Linters/SAST tools

Comment on lines +97 to +109
python3 << 'PYEOF'
with open('CHANGELOG.md', 'r') as f:
content = f.read()
with open('/tmp/section.md', 'r') as f:
section = f.read().strip()
marker = '## [Unreleased]'
idx = content.find(marker)
if idx == -1:
raise SystemExit('Error: [Unreleased] header not found in CHANGELOG.md')
insert_pos = idx + len(marker)
new_content = content[:insert_pos] + '\n\n' + section + '\n' + content[insert_pos:]
with open('CHANGELOG.md', 'w') as f:
f.write(new_content)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Guard against duplicate ## [X.Y.Z] sections on reruns.

The insertion logic always appends after ## [Unreleased]. Re-running the same version creates duplicate headers, which degrades changelog consistency and review clarity.

Suggested fix
       - name: Generate CHANGELOG section
         env:
           GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
           VERSION: ${{ inputs.version }}
         run: |
...
           # Insert section after ## [Unreleased] in CHANGELOG.md
           python3 << 'PYEOF'
+          import os
           with open('CHANGELOG.md', 'r') as f:
               content = f.read()
           with open('/tmp/section.md', 'r') as f:
               section = f.read().strip()
+          version = os.environ['VERSION']
+          header = f'## [{version}]'
+          if header in content:
+              raise SystemExit(f'Error: {header} already exists in CHANGELOG.md')
           marker = '## [Unreleased]'
           idx = content.find(marker)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
python3 << 'PYEOF'
with open('CHANGELOG.md', 'r') as f:
content = f.read()
with open('/tmp/section.md', 'r') as f:
section = f.read().strip()
marker = '## [Unreleased]'
idx = content.find(marker)
if idx == -1:
raise SystemExit('Error: [Unreleased] header not found in CHANGELOG.md')
insert_pos = idx + len(marker)
new_content = content[:insert_pos] + '\n\n' + section + '\n' + content[insert_pos:]
with open('CHANGELOG.md', 'w') as f:
f.write(new_content)
python3 << 'PYEOF'
import os
with open('CHANGELOG.md', 'r') as f:
content = f.read()
with open('/tmp/section.md', 'r') as f:
section = f.read().strip()
version = os.environ['VERSION']
header = f'## [{version}]'
if header in content:
raise SystemExit(f'Error: {header} already exists in CHANGELOG.md')
marker = '## [Unreleased]'
idx = content.find(marker)
if idx == -1:
raise SystemExit('Error: [Unreleased] header not found in CHANGELOG.md')
insert_pos = idx + len(marker)
new_content = content[:insert_pos] + '\n\n' + section + '\n' + content[insert_pos:]
with open('CHANGELOG.md', 'w') as f:
f.write(new_content)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/release-prep.yml around lines 97 - 109, The Python script
that inserts changelog content after the `## [Unreleased]` marker does not check
whether a release section header (such as `## [X.Y.Z]`) already exists in the
changelog, causing duplicate sections on workflow reruns. Extract the version
header from the section variable being inserted, search the existing content for
that same header, and only proceed with the insertion if the header is not
already present. This ensures idempotency when the release-prep workflow runs
multiple times for the same version.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

CI/CD documentation Improvements or additions to documentation enhancement New feature or request GSoC 2026

Projects

None yet

Development

Successfully merging this pull request may close these issues.

Add CHANGELOG.md management and update CI release workflow

1 participant