diff --git a/.github/workflows/build-and-release.yml b/.github/workflows/build-and-release.yml index 7bdae5362..6b1cbfb74 100644 --- a/.github/workflows/build-and-release.yml +++ b/.github/workflows/build-and-release.yml @@ -10,6 +10,25 @@ on: type: string jobs: + validate-changelog: + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout code + uses: actions/checkout@v4 + + - name: Check CHANGELOG entry exists for this release + run: | + RAW_TAG="${{ github.event.release.tag_name || github.event.inputs.tag }}" + VERSION="${RAW_TAG#v}" + if ! grep -q "## \[${VERSION}\]" CHANGELOG.md; then + echo "Error: No entry found for version [${VERSION}] in CHANGELOG.md" + echo "Add a '## [${VERSION}]' section to CHANGELOG.md before releasing." + exit 1 + fi + echo "CHANGELOG.md entry for [${VERSION}] verified." + build-server-windows: runs-on: windows-latest steps: @@ -268,6 +287,7 @@ jobs: contents: write needs: [ + validate-changelog, build-server-windows, build-server-ubuntu, build-server-macos, diff --git a/.github/workflows/release-prep.yml b/.github/workflows/release-prep.yml new file mode 100644 index 000000000..1c0eadd0d --- /dev/null +++ b/.github/workflows/release-prep.yml @@ -0,0 +1,144 @@ +name: Release Prep + +on: + workflow_dispatch: + inputs: + version: + description: "Version to release (X.Y.Z format — no v prefix)" + required: true + type: string + +permissions: + contents: write + pull-requests: write + +jobs: + release-prep: + runs-on: ubuntu-latest + steps: + - name: Checkout code + uses: actions/checkout@v4 + with: + fetch-depth: 0 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: lts/* + + - name: Bump version across manifests + run: npm run version:bump -- ${{ inputs.version }} + + - name: Generate CHANGELOG section + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + VERSION: ${{ inputs.version }} + run: | + DATE=$(date -u +%Y-%m-%d) + + # Find last stable release tag (ignore test/pre-release tags) + LAST_TAG=$(git tag -l --sort=-version:refname | grep -E '^v?[0-9]+\.[0-9]+\.[0-9]+$' | head -1) + echo "Last stable tag: ${LAST_TAG:-none}" + + # Fetch PRs merged since last stable tag + if [ -n "$LAST_TAG" ]; then + SINCE_DATE=$(git log -1 --format=%aI "${LAST_TAG}") + PRS_JSON=$(gh pr list --state merged --search "merged:>${SINCE_DATE}" \ + --json number,title,labels,author --limit 200) + else + PRS_JSON=$(gh pr list --state merged \ + --json number,title,labels,author --limit 200) + fi + + # Group PRs by Release Drafter label categories + ADDED=$(echo "$PRS_JSON" | jq -r ' + .[] | select(.labels | map(.name) | any(. == "enhancement" or . == "UI")) + | "- \(.title) (#\(.number)) by @\(.author.login)"') + + FIXED=$(echo "$PRS_JSON" | jq -r ' + .[] | select(.labels | map(.name) | any(. == "bug")) + | "- \(.title) (#\(.number)) by @\(.author.login)"') + + CHANGED=$(echo "$PRS_JSON" | jq -r ' + .[] | select(.labels | map(.name) | any(. == "documentation")) + | "- \(.title) (#\(.number)) by @\(.author.login)"') + + OTHER=$(echo "$PRS_JSON" | jq -r ' + .[] | select( + (.labels | map(.name) | any(. == "enhancement" or . == "UI" or . == "bug" or . == "documentation")) + | not) + | "- \(.title) (#\(.number)) by @\(.author.login)"') + + # Build section + SECTION="## [$VERSION] - $DATE + + " + + [ -n "$ADDED" ] && SECTION="$SECTION + + ### Added + $ADDED" + [ -n "$FIXED" ] && SECTION="$SECTION + + ### Fixed + $FIXED" + [ -n "$CHANGED" ] && SECTION="$SECTION + + ### Changed + $CHANGED" + [ -n "$OTHER" ] && SECTION="$SECTION + + ### Other + $OTHER" + + echo "$SECTION" > /tmp/section.md + + # Insert section after ## [Unreleased] in CHANGELOG.md + 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) + print('CHANGELOG.md updated.') + PYEOF + + - name: Commit and push prep branch + run: | + git config user.name "github-actions[bot]" + git config user.email "github-actions[bot]@users.noreply.github.com" + git checkout -b release/prep-${{ inputs.version }} + git add package.json frontend/package.json frontend/src-tauri/Cargo.toml CHANGELOG.md + git commit -m "chore: prepare release ${{ inputs.version }}" + git push origin release/prep-${{ inputs.version }} + + - name: Open pull request + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + run: | + gh pr create \ + --title "chore: release ${{ inputs.version }}" \ + --body "## Release Prep: \`${{ inputs.version }}\` + + Auto-generated by the release prep workflow. + + ### What's included + - Version bumped to \`${{ inputs.version }}\` across all manifest files + - CHANGELOG.md updated with a draft \`[${{ inputs.version }}]\` entry + + ### Before merging + - [ ] Review and clean up the CHANGELOG entry + - [ ] Verify version strings in \`package.json\`, \`frontend/package.json\`, and \`frontend/src-tauri/Cargo.toml\` + - [ ] Run \`cargo check\` in \`frontend/src-tauri/\` to regenerate \`Cargo.lock\` + + After merging, create the GitHub Release with tag \`v${{ inputs.version }}\` to trigger the build pipeline." \ + --base main \ + --head release/prep-${{ inputs.version }} \ + --assignee ${{ github.actor }} diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 000000000..e1be51da0 --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,66 @@ +# Changelog + +All notable changes to PictoPy will be documented in this file. + +The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.1.0/), +and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). + +## [Unreleased] + +## [1.1.0] - 2026-05-22 + +### Added + +- Settings page with folder and user preference management ([#606](https://github.com/AOSSIE-Org/PictoPy/pull/606), [#516](https://github.com/AOSSIE-Org/PictoPy/pull/516)) +- Memories backend implementation ([#777](https://github.com/AOSSIE-Org/PictoPy/pull/777)) +- Global face reclustering option with backend API and UI support ([#560](https://github.com/AOSSIE-Org/PictoPy/pull/560)) +- Centralized logging system ([#548](https://github.com/AOSSIE-Org/PictoPy/pull/548)) +- Live progress tracking with polling and animated progress bar ([#574](https://github.com/AOSSIE-Org/PictoPy/pull/574)) +- Scrollbar timeline with month-year markers for Home and AI Tagging pages ([#552](https://github.com/AOSSIE-Org/PictoPy/pull/552)) +- Empty state placeholders for AI tagging and home pages ([#549](https://github.com/AOSSIE-Org/PictoPy/pull/549)) +- Camera selection for the webcam component ([#624](https://github.com/AOSSIE-Org/PictoPy/pull/624)) +- Advanced zoom-on-scroll and panning logic in image preview ([#530](https://github.com/AOSSIE-Org/PictoPy/pull/530), [#835](https://github.com/AOSSIE-Org/PictoPy/pull/835)) +- Slideshow looping for seamless playback ([#1100](https://github.com/AOSSIE-Org/PictoPy/pull/1100)) +- Smooth exit animation for the image details panel ([#978](https://github.com/AOSSIE-Org/PictoPy/pull/978)) +- Face renaming via Enter key ([#581](https://github.com/AOSSIE-Org/PictoPy/pull/581)) +- Spawning and closing of backend and sync microservice from the Tauri app itself ([#1009](https://github.com/AOSSIE-Org/PictoPy/pull/1009)) +- Arch Linux AUR package with automated publishing workflow ([#1268](https://github.com/AOSSIE-Org/PictoPy/pull/1268)) +- AI models-based app size optimization ([#1263](https://github.com/AOSSIE-Org/PictoPy/pull/1263)) + +### Changed + +- Streamlined Rust backend to a minimal API and updated docs structure ([#515](https://github.com/AOSSIE-Org/PictoPy/pull/515)) +- Extended hover activation hotspot for navigation arrows ([#702](https://github.com/AOSSIE-Org/PictoPy/pull/702)) +- Reversed marquee animation direction ([#894](https://github.com/AOSSIE-Org/PictoPy/pull/894)) +- Improved gallery hover interaction with hover delay and cursor cleanup ([#715](https://github.com/AOSSIE-Org/PictoPy/pull/715)) +- Switched backend setup to use Conda ([#975](https://github.com/AOSSIE-Org/PictoPy/pull/975)) +- Separated backend services and changed server port numbers ([#933](https://github.com/AOSSIE-Org/PictoPy/pull/933), [#934](https://github.com/AOSSIE-Org/PictoPy/pull/934)) +- Simplified Redux state ([#599](https://github.com/AOSSIE-Org/PictoPy/pull/599)) + +### Fixed + +- Validated and sanitized image_ids in album APIs to prevent empty/invalid input and unsafe IN clauses ([#1253](https://github.com/AOSSIE-Org/PictoPy/pull/1253), [#629](https://github.com/AOSSIE-Org/PictoPy/pull/629)) +- Prevented InfoDialog from flickering to blue variant on close ([#630](https://github.com/AOSSIE-Org/PictoPy/pull/630)) +- Prevented duplicate scrollbars on Windows/Tauri ([#941](https://github.com/AOSSIE-Org/PictoPy/pull/941)) +- Enabled "Open Folder" button functionality ([#976](https://github.com/AOSSIE-Org/PictoPy/pull/976)) +- Fixed infinite recursion in InterceptHandler logging ([#940](https://github.com/AOSSIE-Org/PictoPy/pull/940)) +- Fixed Windows build not opening on first launch ([#922](https://github.com/AOSSIE-Org/PictoPy/pull/922)) +- Fixed macOS build not opening ([#659](https://github.com/AOSSIE-Org/PictoPy/pull/659)) +- Fixed progress bar overflow in onboarding steps ([#726](https://github.com/AOSSIE-Org/PictoPy/pull/726)) +- Improved face clustering accuracy with a similarity threshold ([#771](https://github.com/AOSSIE-Org/PictoPy/pull/771)) +- Fixed atomic DB operations and consistency in global reclustering ([#570](https://github.com/AOSSIE-Org/PictoPy/pull/570)) +- Fixed 500 error in the `/cluster_id/images` API ([#598](https://github.com/AOSSIE-Org/PictoPy/pull/598)) +- Fixed critical database connection leaks ([#547](https://github.com/AOSSIE-Org/PictoPy/pull/547)) +- Fixed database consistency on external image deletion events ([#520](https://github.com/AOSSIE-Org/PictoPy/pull/520)) +- Fixed "Open Original File" button in the image details panel ([#542](https://github.com/AOSSIE-Org/PictoPy/pull/542)) +- Fixed text overflow in the image details panel ([#537](https://github.com/AOSSIE-Org/PictoPy/pull/537)) +- Fixed oversized navbar height on first load ([#692](https://github.com/AOSSIE-Org/PictoPy/pull/692)) +- Fixed hover state and search bar UI issues ([#532](https://github.com/AOSSIE-Org/PictoPy/pull/532), [#529](https://github.com/AOSSIE-Org/PictoPy/pull/529)) +- Fixed image lock error ([#565](https://github.com/AOSSIE-Org/PictoPy/pull/565)) + +### Documentation + +- Added CI check for markdown linting ([#1266](https://github.com/AOSSIE-Org/PictoPy/pull/1266)) +- Restructured intro page and sidebar navigation ([#1264](https://github.com/AOSSIE-Org/PictoPy/pull/1264)) +- Improved documentation site responsiveness on mobile devices ([#601](https://github.com/AOSSIE-Org/PictoPy/pull/601), [#763](https://github.com/AOSSIE-Org/PictoPy/pull/763)) +- Added Miniconda installation instructions to the Manual Setup Guide ([#971](https://github.com/AOSSIE-Org/PictoPy/pull/971)) diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md index 43f70f665..36a8a775e 100644 --- a/CONTRIBUTING.md +++ b/CONTRIBUTING.md @@ -137,6 +137,22 @@ cargo check > **Note:** The `--` separator between `version:bump` and the version number is required by npm to forward the argument to the underlying script. +### Automated release prep + +When a release is approaching, trigger the release prep workflow from the GitHub Actions tab: + +1. Go to **Actions → Release Prep → Run workflow** +2. Enter the target version in `X.Y.Z` format (no `v` prefix) +3. The workflow will open a PR that includes: + - Version bumped across all manifest files + - A draft `CHANGELOG.md` entry scaffolded from merged PRs since the last release, grouped by label +4. Review the PR, clean up the CHANGELOG entry prose, and merge +5. Create the GitHub Release with tag `v` to trigger the build pipeline + +### Release validation (CI gate) + +`build-and-release.yml` includes a `validate-changelog` job that runs on every release trigger. It checks that `CHANGELOG.md` contains a `## [X.Y.Z]` entry matching the release tag before the publish step is allowed to proceed. If the entry is missing, the release is blocked. Always merge the release prep PR before creating the GitHub Release. + ## Additional Resources - [Tauri Documentation](https://tauri.app/start/)