Skip to content

Implement onboarding, update management, and version 2.1.0 enhancements#39

Open
vordenken wants to merge 46 commits into
mainfrom
feature/v2.1.0
Open

Implement onboarding, update management, and version 2.1.0 enhancements#39
vordenken wants to merge 46 commits into
mainfrom
feature/v2.1.0

Conversation

@vordenken

@vordenken vordenken commented Jun 16, 2026

Copy link
Copy Markdown
Owner

🚀 Release v2.1.0 — Onboarding & Automated Releases

This PR promotes v2.1.0 from beta to stable after 13 beta releases and extensive testing.


✨ What's New

🎨 Onboarding Flow

First-time users are now guided through a 3-step setup:

  1. Welcome — Introduction to AutoPiP with quick links
  2. Features — Overview of automatic PiP, scroll-PiP, keyboard shortcut, and site control
  3. Updates — Configure update preferences (auto-check, auto-download, beta channel) before opening Safari

State is persisted via UserDefaults (OnboardingCompleted flag) — the flow only runs once per user.

⚙️ Configurable Update Settings

Update preferences are now first-class settings, exposed via the onboarding flow and the main app view:

  • ✅ Automatically check for updates
  • ✅ Automatically download updates (gated by auto-check)
  • ✅ Include beta updates — opt in to test upcoming features

All settings are wired through UpdateController to the underlying Sparkle SPUUpdater.


🔧 Under the Hood

🤖 Automated Builds & Releases

Releases are now fully automated via GitHub Actions (.github/workflows/build-release.yml):

  • semver.txt is the single source of truth for version + changelog
  • Every push to feature/* → auto-incremented v2.1.0-beta{N} (never overwritten)
  • Merge to main with changed semver.txt → stable v2.1.0 release
  • Auto-builds DMG, signs with code certificate + Sparkle EdDSA, publishes GitHub Release, updates appcast.xml
  • Bot-authored commits are skipped to prevent infinite loops
  • Concurrency control cancels in-progress builds when a new push arrives

🧪 Beta Release Channel

Users can now opt in to beta updates through Sparkle's channel mechanism (allowedChannels returns ["beta"] when BetaUpdatesEnabled is true). The appcast marks beta releases with a visible warning banner.

🐛 Bugfix: Keyboard PiP small in corner

Fixed a bug where the keyboard shortcut (⌥P) opened PiP at the video element's CSS-rendered size instead of its intrinsic dimensions. The toggle now prefers the W3C requestPictureInPicture() API and falls back to the WebKit API on failure. This affected YouTube's mini-player (which renders the <video> element very small) and other CSS-scaled video players.


🧪 Testing

This release has been validated through 13 beta releases with no regressions reported. Manual test plan:

  • Fresh install → onboarding flow appears, completes successfully
  • Onboarding toggles persist after quit/relaunch
  • Main view reflects Safari extension enabled/disabled state
  • Toggling beta channel makes v2.1.0 stable visible after next check
  • Keyboard shortcut opens PiP at correct (intrinsic) size
  • Update check finds v2.1.0 stable when upgrading from v2.0.0

📝 Notes

  • Beta-Channel users will need to disable beta updates in settings (or wait for v2.1.0-beta{N+1} to be released, which won't happen) to receive the stable v2.1.0.
  • appcast.xml on main will be auto-updated by the workflow after merge — the beta banner will be removed and the sparkle:channel will be set to stable.
  • No database migrations or data format changes — drop-in upgrade from v2.0.0.

✅ Checklist

  • Version bumped in semver.txt, manifest.json, and Xcode project
  • Changelog entries added
  • Beta tested (beta1–beta13)
  • No new TODOs / FIXMEs introduced
  • No breaking changes for existing users

Summary by Sourcery

Introduce a guided onboarding flow, configurable update channel and settings, and automated build-and-release pipeline for the AutoPiP v2.1.0 release.

New Features:

  • Add a three-step onboarding flow that runs on first launch to introduce AutoPiP, explain key features, and configure update preferences before opening Safari.
  • Expose update preferences (auto-check, auto-download, beta channel) in both the onboarding flow and the main app view, wired through the update controller to Sparkle.
  • Introduce a beta update channel selection backed by Sparkle’s channel mechanism so users can opt in to pre-release builds.

Bug Fixes:

  • Fix keyboard shortcut PiP behavior so it uses intrinsic video dimensions (via the standard Picture-in-Picture API) instead of CSS-rendered size, preventing tiny PiP windows on sites like YouTube.

Enhancements:

  • Redesign the main app HTML/CSS/JS to support multiple pages, shared styling, and in-app update controls, including version display and Safari state messaging.
  • Extend the update controller to manage automatic check/download options and beta channel toggling via Sparkle, persisting state in user defaults.
  • Update the appcast feed to include 2.1.0 beta entries and normalized Sparkle version metadata for historical releases.
  • Add default enabling of automatic update checks in the app’s Info.plist.

Build:

  • Add a GitHub Actions workflow that automates versioning from semver.txt, builds and signs the app, creates DMG artifacts, tags and publishes GitHub releases, and updates the Sparkle appcast across branches.
  • Introduce semver.txt as the single source of truth for app versioning and per-release changelogs used by the CI pipeline.

CI:

  • Configure GitHub Actions to gate main-branch builds on semver.txt changes, auto-increment beta tags for feature branches, skip bot-authored commits, and manage build concurrency.

Documentation:

  • Expand the README with Gatekeeper installation instructions, update guidance, and a link to build/release docs.
  • Add BUILD.md documenting the build-from-source process, release workflow, semver.txt format, and required GitHub secrets and app setup.
  • Add a SECURITY.md describing supported versions and responsible vulnerability reporting.
  • Add GitHub issue templates for bug reports and feature requests, and a pull request template to standardize contributions.

Chores:

  • Expose the update controller from the app delegate so it can be controlled from the WebView onboarding and main UI.

Summary by CodeRabbit

Release Notes

  • New Features

    • Added a redesigned onboarding flow for initial setup and configuration.
    • Added update preferences (auto-check, auto-download) and a beta-channel opt-in.
    • Refreshed the main UI to show version info and provide update controls.
  • Bug Fixes

    • Improved Picture-in-Picture compatibility by preferring the modern PiP request API with a fallback for older environments.
  • Documentation

    • Updated README with clearer installation steps, and added BUILD and SECURITY documentation.

vordenken and others added 29 commits April 14, 2026 22:14
Update version to 2.1.0 in manifest and project files
Expose update controller in AppDelegate for onboarding
Enable automatic update checks in Info.plist
Implement onboarding pages for user guidance
Enhance main view with update settings and controls
Refactor update controller to manage beta updates
Handle onboarding completion and settings in ViewController
Create build and release documentation
Establish security policy for vulnerability reporting
Update appcast for new versioning scheme
Add Renovate configuration for dependency management
Set version to 2.1.0 in semver file
@sourcery-ai

sourcery-ai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Reviewer's Guide

Implements a three-step onboarding flow with persisted state, exposes and wires update/beta-channel settings through a new UpdateController API into the WebView UI, introduces a GitHub Actions-based automated build-and-release pipeline around semver.txt and Sparkle, and fixes the keyboard PiP toggle to use the W3C PiP API to avoid CSS-sized PiP windows.

Sequence diagram for onboarding flow and update settings persistence

sequenceDiagram
    actor User
    participant WebViewHTMLJS as WebView_HTML_JS
    participant WKWebView
    participant ViewController
    participant UpdateController
    participant SPUUpdater
    participant SFSafariApplication

    User->>WKWebView: Launch AutoPiP app
    WKWebView->>ViewController: webView(_:didFinish:)
    ViewController->>WKWebView: evaluateJavaScript("setVersion")
    ViewController->>WKWebView: evaluateJavaScript("startOnboarding()")

    User->>WebViewHTMLJS: Click "Done – Open Safari"
    WebViewHTMLJS->>WebViewHTMLJS: Collect onb-auto-check/onb-auto-download/onb-beta
    WebViewHTMLJS->>WKWebView: postMessage("onboarding-done:" + JSON)
    WKWebView->>ViewController: userContentController(_:didReceive:)
    ViewController->>ViewController: handleOnboardingDone(jsonString)
    ViewController->>UpdateController: set automaticallyChecksForUpdates
    ViewController->>UpdateController: set automaticallyDownloadsUpdates
    ViewController->>UpdateController: set isBetaUpdatesEnabled
    UpdateController->>SPUUpdater: automaticallyChecksForUpdates / automaticallyDownloadsUpdates
    UpdateController->>SPUUpdater: resetUpdateCycleAfterShortDelay()

    ViewController->>ViewController: UserDefaults["OnboardingCompleted"] = true
    ViewController->>SFSafariApplication: showPreferencesForExtension
    SFSafariApplication-->>ViewController: completion(error?)
    ViewController->>SFSafariApplication: openApplication (fallback if error)
    ViewController->>NSApplication: terminate(nil)
Loading

Sequence diagram for update and beta channel setting changes in main view

sequenceDiagram
    actor User
    participant WebViewHTMLJS as WebView_HTML_JS
    participant WKWebView
    participant ViewController
    participant UpdateController
    participant UpdaterDelegate
    participant SPUUpdater

    User->>WKWebView: Reopen AutoPiP app
    WKWebView->>ViewController: webView(_:didFinish:)
    ViewController->>WKWebView: evaluateJavaScript("setVersion")
    ViewController->>ViewController: showMainView()
    ViewController->>WKWebView: evaluateJavaScript("show(enabled,useSettings)")
    ViewController->>UpdateController: read automaticallyChecksForUpdates / automaticallyDownloadsUpdates / isBetaUpdatesEnabled
    ViewController->>WKWebView: evaluateJavaScript("setUpdateSettings(settings)")

    User->>WebViewHTMLJS: Toggle "Include beta updates"
    WebViewHTMLJS->>WKWebView: postMessage("set-beta:true")
    WKWebView->>ViewController: userContentController(_:didReceive:)
    ViewController->>UpdateController: isBetaUpdatesEnabled = true
    UpdateController->>UserDefaults: set(BetaUpdatesEnabled,true)
    UpdateController->>SPUUpdater: resetUpdateCycleAfterShortDelay()

    SPUUpdater->>UpdaterDelegate: allowedChannels(for:)
    UpdaterDelegate-->>SPUUpdater: Set(["beta"]) (when BetaUpdatesEnabled)
Loading

Sequence diagram for keyboard PiP toggle using W3C API with WebKit fallback

sequenceDiagram
    actor User
    participant ContentScript as content_js
    participant VideoElement as HTMLVideoElement
    participant BrowserPiP as Browser_PiP_System

    User->>ContentScript: Press keyboard shortcut (⌥P)
    ContentScript->>ContentScript: togglePiP()
    alt PiP currently active
        ContentScript->>ContentScript: disablePiP()
    else No PiP active
        ContentScript->>VideoElement: check typeof video.requestPictureInPicture
        alt requestPictureInPicture available
            ContentScript->>VideoElement: requestPictureInPicture()
            VideoElement-->>BrowserPiP: Open PiP at intrinsic size
            VideoElement-->>ContentScript: Promise reject on error
            ContentScript->>ContentScript: catch(error)
            ContentScript->>ContentScript: setWebkitPresentationMode(video,"picture-in-picture")
        else Only WebKit API available
            ContentScript->>ContentScript: setWebkitPresentationMode(video,"picture-in-picture")
        end
    end
Loading

File-Level Changes

Change Details Files
Add a page-based onboarding and main view UI with shared styling, support links, and integrated update settings, backed by WebView–Swift message handling and persisted onboarding state.
  • Replace the simple single-page HTML with multiple page containers for onboarding steps and the main settings view, including support links and version labels.
  • Refactor CSS into shared page, typography, toggle, settings list, and state visibility styles to support the multi-page layout.
  • Extend the popup script to manage page navigation, surface version text, synchronize update toggles, and send structured commands back to Swift via WKScriptMessageHandler.
  • Update the view controller to inject the app version, decide between onboarding and main view based on a UserDefaults flag, and handle all new message commands for update settings, beta toggle, support links, and onboarding completion before opening Safari and quitting.
AutoPiP/Resources/Style.css
AutoPiP/Resources/Script.js
AutoPiP/Resources/Base.lproj/Main.html
AutoPiP/ViewController.swift
AutoPiP/AppDelegate.swift
Expose Sparkle update and beta-channel configuration through UpdateController, enable automatic checks, and connect those settings to the UI and appcast feed.
  • Introduce an SPUUpdaterDelegate implementation to return the beta channel when a BetaUpdatesEnabled UserDefaults flag is set.
  • Add computed properties on UpdateController for automatically checking and downloading updates and for enabling beta updates, ensuring dependent flags stay consistent and resetting the Sparkle update cycle when the beta state changes.
  • Enable Sparkle’s automatic update checks at the app level and surface update settings into the WebView via JSON from the view controller.
  • Expand appcast.xml with historical beta 2.1.0 entries using build-number-based sparkle:version and sparkle:shortVersionString for all releases.
AutoPiP/UpdateController.swift
AutoPiP/Info.plist
appcast.xml
Fix the keyboard Picture-in-Picture toggle to use intrinsic video dimensions by preferring the modern W3C API and falling back to WebKit when needed.
  • Change the PiP toggle handler in the content script to call video.requestPictureInPicture when available, with error handling and a fallback to webkitSetPresentationMode.
  • Improve logging and exception handling around PiP activation failures to avoid silent errors.
AutoPiP Extension/Resources/content.js
Introduce a fully automated build-and-release pipeline that derives versioning and changelog from semver.txt, signs artifacts, updates the Sparkle appcast, and publishes GitHub releases.
  • Add a GitHub Actions workflow that gates builds on semver.txt changes on main, auto-increments beta tags for feature branches, sets MARKETING_VERSION/CURRENT_PROJECT_VERSION and extension manifest version, builds and signs a DMG, generates Sparkle EdDSA signatures, updates appcast.xml, and creates GitHub releases with changelog-based notes.
  • Implement concurrency controls, bot-actor skipping, and optional GitHub App token support for pushing appcast changes to protected branches.
  • Document build, release, signing, and GitHub App setup in a new BUILD.md and define semver.txt as the single source of truth for versions and changelog entries.
  • Introduce semver.txt, Renovate configuration, and update Xcode project / SwiftPM resolution files as part of the new release process.
.github/workflows/build-release.yml
BUILD.md
semver.txt
AutoPiP.xcodeproj/project.pbxproj
AutoPiP.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
renovate.json
Refresh documentation and repo metadata for CI, security, and contribution flow, and bump the extension manifest version for v2.1.0.
  • Update the README with clearer installation and update instructions, add a build-status badge, and link to BUILD.md for build/release details.
  • Add SECURITY policy and GitHub issue/PR templates for bugs, feature requests, and standardized pull requests, including disabling blank issues.
  • Bump the Safari extension manifest version to 2.1.0 to align with the new release.
README.md
SECURITY.md
.github/ISSUE_TEMPLATE/bug_report.yml
.github/ISSUE_TEMPLATE/feature_request.yml
.github/ISSUE_TEMPLATE/config.yml
.github/pull_request_template.md
AutoPiP Extension/Resources/manifest.json

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 17ae40a4-7dcd-4612-b6ed-3212d406d6ca

📥 Commits

Reviewing files that changed from the base of the PR and between 7a4c531 and 29c58d8.

📒 Files selected for processing (1)
  • .github/workflows/build-release.yml
💤 Files with no reviewable changes (1)
  • .github/workflows/build-release.yml

📝 Walkthrough

Walkthrough

AutoPiP 2.1.0 adds onboarding with configurable Sparkle updates, a W3C PiP fallback, updated version metadata, an automated macOS release workflow, appcast entries, and repository issue, security, Renovate, and documentation configuration.

Changes

Onboarding and Update Settings

Layer / File(s) Summary
Sparkle update channel and controller settings
AutoPiP/UpdateController.swift, AutoPiP/AppDelegate.swift, AutoPiP/Info.plist, AutoPiP.xcodeproj/.../Package.resolved
Adds beta-channel selection and automatic update settings, enables automatic checks, exposes updateController within the module, and updates Sparkle.
Onboarding gate and native message routing
AutoPiP/ViewController.swift
Adds onboarding completion gating, version injection, Safari extension state handling, update-setting synchronization, and WebView command routing.
Onboarding UI and WebView controller
AutoPiP/Resources/Base.lproj/Main.html, AutoPiP/Resources/Script.js, AutoPiP/Resources/Style.css
Adds onboarding pages, main-view settings controls, navigation and native message handlers, themed layouts, toggles, and state visibility styles.
PiP fallback and 2.1.0 version metadata
AutoPiP Extension/Resources/content.js, AutoPiP Extension/Resources/manifest.json, AutoPiP.xcodeproj/project.pbxproj
Prefers requestPictureInPicture() with a WebKit fallback and updates application and extension versions to 2.1.0.

Automated Release Pipeline

Layer / File(s) Summary
Workflow triggers and release versioning
.github/workflows/build-release.yml, semver.txt
Adds gated push/manual triggers, semver changelog extraction, stable and beta tag computation, and release outputs.
Signing, archive, and DMG creation
.github/workflows/build-release.yml
Creates a temporary signing keychain, updates build inputs, resolves packages, archives the app, builds and uploads the DMG, and cleans up the keychain.
Sparkle signing and release publication
.github/workflows/build-release.yml, appcast.xml, BUILD.md
Signs the DMG, generates appcast entries, creates GitHub Releases, pushes appcast changes, and documents the workflow.

Repository Configuration and Community Files

Layer / File(s) Summary
Issue, pull request, security, and Renovate configuration
.github/ISSUE_TEMPLATE/*, .github/pull_request_template.md, SECURITY.md, renovate.json
Adds structured issue forms, disables blank issues, adds a pull request checklist, documents security reporting, and configures Renovate.
README and release documentation
README.md
Updates installation, compatibility, Sparkle update, build, and release documentation.

Estimated code review effort: 4 (Complex) | ~60 minutes

Sequence Diagram(s)

sequenceDiagram
  participant WKWebView
  participant ViewController
  participant UpdateController
  participant SafariExtensionState
  WKWebView->>ViewController: webView(_:didFinish:)
  ViewController->>WKWebView: setVersion(v)
  ViewController->>WKWebView: startOnboarding() or show()
  WKWebView->>ViewController: onboarding-done or update command
  ViewController->>UpdateController: apply settings or check updates
  ViewController->>SafariExtensionState: query extension state
  SafariExtensionState-->>ViewController: enabled state
  ViewController->>WKWebView: setUpdateSettings(settings)
Loading
sequenceDiagram
  participant Workflow
  participant Keychain
  participant Xcode
  participant SparkleTools
  participant Appcast
  participant GitHubRelease
  Workflow->>Keychain: import signing certificate
  Workflow->>Xcode: archive app and create DMG
  Workflow->>SparkleTools: sign DMG
  SparkleTools-->>Workflow: EdDSA signature
  Workflow->>Appcast: insert and deduplicate release item
  Workflow->>GitHubRelease: create release and upload DMG
  Workflow->>Appcast: push updated feed
  Workflow->>Keychain: delete temporary keychain
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 26.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ 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 reflects the main changes: onboarding, update management, and the 2.1.0 release enhancements.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/v2.1.0

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.

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 3 issues, and left some high level feedback:

  • There are several hard-coded UserDefaults keys ("OnboardingCompleted", "BetaUpdatesEnabled", etc.) scattered across ViewController, the updater delegate, and JavaScript; consider centralizing these keys in a single Swift enum/struct (and mirroring them in JS constants) to avoid typos and make future changes safer.
  • The string-based message protocol between the web view and ViewController (e.g. "set-auto-check:true", "open-url:" + href, "onboarding-done:" + JSON) is starting to get complex; you might want to move to a small structured JSON message format with type/payload` fields to make parsing more robust and easier to extend.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- There are several hard-coded UserDefaults keys (`"OnboardingCompleted"`, `"BetaUpdatesEnabled"`, etc.) scattered across ViewController, the updater delegate, and JavaScript; consider centralizing these keys in a single Swift enum/struct (and mirroring them in JS constants) to avoid typos and make future changes safer.
- The string-based message protocol between the web view and `ViewController` (e.g. `"set-auto-check:true"`, `"open-url:" + href`, `"onboarding-done:" + JSON) is starting to get complex; you might want to move to a small structured JSON message format with `type`/`payload` fields to make parsing more robust and easier to extend.

## Individual Comments

### Comment 1
<location path="AutoPiP/Resources/Script.js" line_range="118-120" />
<code_context>
+        webkit.messageHandlers.controller.postMessage("check-for-updates");
+    });
+
+    document.getElementById('auto-check-toggle').addEventListener('change', function() {
+        webkit.messageHandlers.controller.postMessage("set-auto-check:" + this.checked);
+        var dl = document.getElementById('auto-download-toggle');
+        dl.disabled = !this.checked;
+        if (!this.checked) dl.checked = false;
</code_context>
<issue_to_address>
**issue (bug_risk):** Keep native `autoDownload` flag in sync when disabling it via the `auto-check` toggle.

In this handler you disable and uncheck `auto-download-toggle` only in the DOM, but you never send `set-auto-download:false` to the native side as is done in onboarding. That leaves Sparkle’s `automaticallyDownloadsUpdates` still `true` after a user turns off auto-checking.

You can align behavior by also posting the native message when auto-check is turned off:
```js
document.getElementById('auto-check-toggle').addEventListener('change', function() {
    webkit.messageHandlers.controller.postMessage("set-auto-check:" + this.checked);
    var dl = document.getElementById('auto-download-toggle');
    dl.disabled = !this.checked;
    if (!this.checked) {
        dl.checked = false;
        webkit.messageHandlers.controller.postMessage("set-auto-download:false");
    }
});
```
</issue_to_address>

### Comment 2
<location path="BUILD.md" line_range="55" />
<code_context>
+### GitHub App for Branch Protection
+
+If `main` has branch protection, the workflow needs a GitHub App to push the appcast update. Create a GitHub App with **Contents: Read & Write** permission, install it on the repo, then:
+- Add variable `CLienT_ID` with the App's client ID
+- Add secret `APP_PRIVATE_KEY` with the App's private key
+- Add the App to the branch protection bypass list
</code_context>
<issue_to_address>
**issue (typo):** Typo in variable name: `CLienT_ID` should be `CLIENT_ID` for consistency

Below you use `CLIENT_ID` in the secrets section. Please update this bullet to `CLIENT_ID` as well so the variable name is consistent and avoids configuration confusion.

```suggestion
- Add variable `CLIENT_ID` with the App's client ID
```
</issue_to_address>

### Comment 3
<location path="BUILD.md" line_range="49" />
<code_context>
+- Sets the version in the Xcode project and `manifest.json`
+- Builds, archives, and creates a DMG
+- Signs the DMG with the code signing certificate and Sparkle EdDSA
+- Creates a git tag and GitHub Release with changelog + installation instructions
+- Updates `appcast.xml` (with changelog as `<description>`) on the working branch and `main`
+
</code_context>
<issue_to_address>
**nitpick (typo):** Consider capitalizing "Git" as a proper noun

Here, "git" should be capitalized as "Git" to match its proper name and stay consistent with "GitHub" in the same sentence.

```suggestion
- Creates a Git tag and GitHub Release with changelog + installation instructions
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread AutoPiP/Resources/Script.js
Comment thread BUILD.md Outdated
Comment thread BUILD.md Outdated
vordenken and others added 3 commits June 16, 2026 21:40
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>
Co-authored-by: sourcery-ai[bot] <58596630+sourcery-ai[bot]@users.noreply.github.com>

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 7

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

18-18: Pin GitHub Actions to commit SHAs for supply-chain security.

These actions use tag-pinning (@v*) instead of immutable commit SHAs, which increases supply-chain attack risk. Consider enabling Renovate's helpers:pinGitHubActionDigests preset (or upgrading to config:best-practices) to automatically pin actions to full-length commit SHAs with version comments for maintainability.

Lines: 18, 68, 74, 341, 400

🤖 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-release.yml at line 18, The GitHub Actions in this
workflow are pinned to semantic version tags (such as `@v6` in
actions/checkout@v6) instead of immutable commit SHAs, which introduces
supply-chain security risks. Replace each action version tag reference with the
full commit SHA of that action version, and include the version tag as a comment
for maintainability (for example, replace `@v6` with @<full-commit-sha> # v6).
Alternatively, enable Renovate's helpers:pinGitHubActionDigests preset or
upgrade to config:best-practices to automatically manage this pinning going
forward.

Source: Linters/SAST tools

🤖 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/ISSUE_TEMPLATE/config.yml:
- Line 1: In the `.github/ISSUE_TEMPLATE/config.yml` file, rename the
configuration key from `blank_issues` to `blank_issues_enabled`. GitHub's issue
template configuration schema uses `blank_issues_enabled` as the correct key
name to control whether blank issues are available in the issue template
chooser. Update this key name to match the expected schema.

In @.github/workflows/build-release.yml:
- Around line 8-10: The workflow-level permissions are granting write access to
all jobs via `contents: write`, which violates the principle of least privilege.
Remove the workflow-level `contents: write` permission to revert to the default
read-only state, then add a job-level permissions section specifically to the
`build-and-release` job that grants `contents: write` only to that job. This
ensures the `check` job and any other jobs retain read-only permissions while
only the `build-and-release` job has the necessary write access.
- Around line 124-130: The tag existence check in the workflow hard-fails if a
tag already exists, which prevents idempotent reruns when a release partially
succeeds but a later step fails. Instead of exiting with an error when the tag
is detected, modify the logic to handle the existing tag case gracefully by
updating or reconciling the existing release and appcast rather than aborting.
This allows subsequent workflow reruns to recover from failures and complete the
release process without being blocked by the previously-created tag.
- Around line 24-33: Refactor the GitHub Actions workflow to prevent template
injection and spoofing vulnerabilities by moving GitHub context expressions from
direct `run:` script expansion to the `env:` section. Replace the bot detection
logic that uses `github.event.head_commit.author.name` (which is user-controlled
and spoofable) with `github.actor` instead. Define environment variables in the
`env:` block above the `run:` step, then reference them as shell variables
within the script. Apply this same hardening pattern at lines 93 and 417 where
similar direct template expansion and bot detection logic exists.

In `@AutoPiP/UpdateController.swift`:
- Around line 30-38: The setters for automaticallyChecksForUpdates and
automaticallyDownloadsUpdates in UpdateController do not enforce the constraint
that auto-download must be gated by auto-check (auto-download cannot be true if
auto-check is false). Update the setter for automaticallyChecksForUpdates to set
automaticallyDownloadsUpdates to false whenever auto-check is disabled, and
update the setter for automaticallyDownloadsUpdates to prevent enabling
auto-download when auto-check is false (either by rejecting the change or
forcing auto-check to true). This ensures the dependency is enforced at the
controller layer and prevents callers from creating invalid states.

In `@BUILD.md`:
- Line 55: Fix the variable name typo in the GitHub App setup documentation at
line 55 of BUILD.md. The variable name is currently written as `CLienT_ID` with
inconsistent capitalization, which will cause workflow configuration to fail.
Correct this to the proper constant naming convention `CLIENT_ID` to match the
actual variable name used in the application configuration.

In `@README.md`:
- Line 40: The update note in the README at the "Updating" section states that
Sparkle automatically checks for updates once per day as unconditional behavior.
Since auto-update checks are now user-configurable via a toggle, revise this
sentence to clarify that automatic daily checks only occur when the user has
enabled this feature. Reference the new toggle in the statement to make it clear
that users can control whether this automatic checking behavior is active.

---

Nitpick comments:
In @.github/workflows/build-release.yml:
- Line 18: The GitHub Actions in this workflow are pinned to semantic version
tags (such as `@v6` in actions/checkout@v6) instead of immutable commit SHAs,
which introduces supply-chain security risks. Replace each action version tag
reference with the full commit SHA of that action version, and include the
version tag as a comment for maintainability (for example, replace `@v6` with
@<full-commit-sha> # v6). Alternatively, enable Renovate's
helpers:pinGitHubActionDigests preset or upgrade to config:best-practices to
automatically manage this pinning going forward.
🪄 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: defaults

Review profile: CHILL

Plan: Pro

Run ID: a48e89bb-c91e-48e8-8bc8-32b3bd951291

📥 Commits

Reviewing files that changed from the base of the PR and between b8fd22e and 1fb2e6f.

📒 Files selected for processing (22)
  • .github/ISSUE_TEMPLATE/bug_report.yml
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/ISSUE_TEMPLATE/feature_request.yml
  • .github/pull_request_template.md
  • .github/workflows/build-release.yml
  • AutoPiP Extension/Resources/content.js
  • AutoPiP Extension/Resources/manifest.json
  • AutoPiP.xcodeproj/project.pbxproj
  • AutoPiP.xcodeproj/project.xcworkspace/xcshareddata/swiftpm/Package.resolved
  • AutoPiP/AppDelegate.swift
  • AutoPiP/Info.plist
  • AutoPiP/Resources/Base.lproj/Main.html
  • AutoPiP/Resources/Script.js
  • AutoPiP/Resources/Style.css
  • AutoPiP/UpdateController.swift
  • AutoPiP/ViewController.swift
  • BUILD.md
  • README.md
  • SECURITY.md
  • appcast.xml
  • renovate.json
  • semver.txt

Comment thread .github/ISSUE_TEMPLATE/config.yml Outdated
Comment thread .github/workflows/build-release.yml Outdated
Comment thread .github/workflows/build-release.yml Outdated
Comment thread .github/workflows/build-release.yml
Comment thread AutoPiP/UpdateController.swift
Comment thread BUILD.md Outdated
Comment thread README.md
@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

Note

Unit test generation is a public access feature. Expect some limitations and changes as we gather feedback and continue to improve it.


Generating unit tests... This may take up to 20 minutes.

@coderabbitai

coderabbitai Bot commented Jun 16, 2026

Copy link
Copy Markdown

❌ Failed to create PR with unit tests: AGENT_CHAT: Failed to open pull request

@vordenken

Copy link
Copy Markdown
Owner Author

@sourcery-ai review

@sourcery-ai sourcery-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Hey - I've found 1 issue, and left some high level feedback:

  • The GitHub Actions trigger uses paths-ignore: ['.github/**'] while the comment above says “this workflow itself still trigger a build”; if you want changes to workflow files to trigger builds, remove .github/** from paths-ignore or narrow it (e.g. only ignore .github/ISSUE_TEMPLATE/**).
  • The WKWebView evaluateJavaScript("setUpdateSettings(...)" ) call in showMainView() runs outside of a DispatchQueue.main.async block, unlike the extension state call; consider dispatching it explicitly to the main thread to avoid any potential threading issues with WKWebView.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- The GitHub Actions trigger uses `paths-ignore: ['.github/**']` while the comment above says “this workflow itself still trigger a build”; if you want changes to workflow files to trigger builds, remove `.github/**` from `paths-ignore` or narrow it (e.g. only ignore `.github/ISSUE_TEMPLATE/**`).
- The WKWebView `evaluateJavaScript("setUpdateSettings(...)" )` call in `showMainView()` runs outside of a `DispatchQueue.main.async` block, unlike the extension state call; consider dispatching it explicitly to the main thread to avoid any potential threading issues with WKWebView.

## Individual Comments

### Comment 1
<location path=".github/workflows/build-release.yml" line_range="143-150" />
<code_context>
+            echo '```' >> "$GITHUB_STEP_SUMMARY"
+          fi
+
+      - name: Check for existing tag
+        run: |
+          TAG="${{ steps.version.outputs.tag }}"
+          if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
+            echo "::warning::Tag ${TAG} already exists — assuming partial release, will reconcile (re-upload asset, refresh appcast)."
+            echo "tag_exists=true" >> "$GITHUB_OUTPUT"
+          else
+            echo "tag_exists=false" >> "$GITHUB_OUTPUT"
+          fi
+
</code_context>
<issue_to_address>
**nitpick:** The `Check for existing tag` step computes an output that is never consumed, and the step has no `id`.

This step writes `tag_exists` to `$GITHUB_OUTPUT`, but without an `id` the output can’t be used and isn’t referenced anywhere else. Either assign an `id` and consume `steps.<id>.outputs.tag_exists` where needed, or remove the unused output to keep the workflow simpler and clearer.
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment on lines +143 to +150
- name: Check for existing tag
run: |
TAG="${{ steps.version.outputs.tag }}"
if git rev-parse "refs/tags/${TAG}" >/dev/null 2>&1; then
echo "::warning::Tag ${TAG} already exists — assuming partial release, will reconcile (re-upload asset, refresh appcast)."
echo "tag_exists=true" >> "$GITHUB_OUTPUT"
else
echo "tag_exists=false" >> "$GITHUB_OUTPUT"

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

nitpick: The Check for existing tag step computes an output that is never consumed, and the step has no id.

This step writes tag_exists to $GITHUB_OUTPUT, but without an id the output can’t be used and isn’t referenced anywhere else. Either assign an id and consume steps.<id>.outputs.tag_exists where needed, or remove the unused output to keep the workflow simpler and clearer.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
.github/workflows/build-release.yml (1)

26-28: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Shallow fetch-depth: 2 can make the semver.txt diff silently fail (and silently skip real releases).

For multi-commit pushes to main, github.event.before may not be present in a depth-2 clone, so git diff "$BEFORE" "$HEAD_SHA" fails. Since the script has no set -e, the failure is swallowed and grep -q sees empty output, causing should-build=false — a real semver.txt change on main could be silently skipped rather than triggering a release.

🔧 Proposed fix
       - uses: actions/checkout@v6
         with:
-          fetch-depth: 2
+          fetch-depth: 0

Also applies to: 61-64

🤖 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-release.yml around lines 26 - 28, Update the
checkout steps used by the release-detection logic to fetch sufficient history
for github.event.before, replacing the shallow fetch-depth: 2 configuration in
both referenced locations. Ensure git diff "$BEFORE" "$HEAD_SHA" can resolve the
comparison commits for multi-commit pushes to main.
♻️ Duplicate comments (1)
.github/workflows/build-release.yml (1)

143-151: 🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

tag_exists output is unreachable and the "will reconcile" claim isn't implemented.

This step lacks an id:, so tag_exists can never be read via steps.<id>.outputs.tag_exists — no downstream step consumes it. The warning promises reconciliation ("re-upload asset, refresh appcast"), but nothing branches on this value.

Additionally, for beta releases the tag is always the next auto-incremented number (Lines 118-129), so a retry after a partial failure will practically never hit this "already exists" path for the failed tag — it just mints a brand-new beta tag/release each retry instead of reconciling the broken one.

🔧 Suggested direction
       - name: Check for existing tag
+        id: check-tag
         run: |

Then either skip re-tagging when tag_exists == 'true' and reuse the same tag, or reorder beta-number computation to check for an existing unfinished release for the current commit before incrementing.

🤖 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-release.yml around lines 143 - 151, Fix the release
retry flow around the “Check for existing tag” step: make its tag_exists output
consumable by assigning the step an id, then implement downstream branching so
an existing tag reuses the current tag and skips re-tagging while reconciling
the release assets/appcast. For beta releases, check for an existing unfinished
release for the current commit before generating the next auto-incremented tag,
rather than always creating a new tag on retry.
🧹 Nitpick comments (2)
.github/workflows/build-release.yml (2)

182-187: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Duplicated BUILD number calculation across two steps.

Both steps independently derive BUILD from github.run_number + 10. Currently consistent, but this duplication risks silent drift between the app's embedded CURRENT_PROJECT_VERSION and the appcast's sparkle:version if either formula is edited without the other.

♻️ Suggested refactor

Compute BUILD once in the "Read version and changelog" step and emit it as steps.version.outputs.build, then reference ${{ steps.version.outputs.build }} in both later steps instead of recomputing.

Also applies to: 241-244

🤖 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-release.yml around lines 182 - 187, Compute the
build number once in the “Read version and changelog” step and expose it as the
`build` output of `steps.version`. Update both later steps to reference `${{
steps.version.outputs.build }}` instead of independently deriving
`github.run_number + 10`, keeping the embedded version and appcast version
synchronized.

49-53: 🚀 Performance & Scalability | 🔵 Trivial | 🏗️ Heavy lift

Beta appcast entries accumulate indefinitely; every feature-branch push triggers a full macOS build+release.

The dedup logic only strips entries whose sparkle:shortVersionString exactly matches the bare VERSION, which never matches beta strings like "2.1.0-beta19". Combined with "always build" on any non-doc push to a feature branch (Line 49-53), appcast.xml accumulates a permanent, unbounded set of beta <item> entries per version (already 7 for 2.1.0), and every such push consumes a full macOS runner for build/sign/DMG/release.

Consider pruning older beta entries for the same base version (e.g., keep only the last N), and/or gating beta builds on something narrower than "any push" to reduce cost and appcast growth.

Also applies to: 344-357

🤖 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-release.yml around lines 49 - 53, Update the
feature-branch condition using REF_NAME so beta releases are not built for every
non-documentation push; gate them on the narrower release-trigger criteria used
by the workflow. Also update the appcast deduplication logic to recognize beta
versions by their base VERSION and retain only the most recent bounded number of
beta entries per base version, preventing unbounded accumulation while
preserving the latest beta releases.
🤖 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-release.yml:
- Around line 76-78: Update the workflow concurrency configuration around the
existing concurrency block to serialize release-critical runs across all
branches, rather than grouping by github.ref. Use a single stable workflow-wide
group while preserving cancel-in-progress behavior, so beta tag generation and
the main-branch appcast push cannot run concurrently.
- Around line 7-16: Update the paths-ignore list in the workflow trigger
configuration to stop excluding all of .github/**, while continuing to ignore
the intended documentation and repository metadata files. Ensure changes to
.github/workflows/build-release.yml itself remain eligible to trigger the
workflow as stated by the comment.

---

Outside diff comments:
In @.github/workflows/build-release.yml:
- Around line 26-28: Update the checkout steps used by the release-detection
logic to fetch sufficient history for github.event.before, replacing the shallow
fetch-depth: 2 configuration in both referenced locations. Ensure git diff
"$BEFORE" "$HEAD_SHA" can resolve the comparison commits for multi-commit pushes
to main.

---

Duplicate comments:
In @.github/workflows/build-release.yml:
- Around line 143-151: Fix the release retry flow around the “Check for existing
tag” step: make its tag_exists output consumable by assigning the step an id,
then implement downstream branching so an existing tag reuses the current tag
and skips re-tagging while reconciling the release assets/appcast. For beta
releases, check for an existing unfinished release for the current commit before
generating the next auto-incremented tag, rather than always creating a new tag
on retry.

---

Nitpick comments:
In @.github/workflows/build-release.yml:
- Around line 182-187: Compute the build number once in the “Read version and
changelog” step and expose it as the `build` output of `steps.version`. Update
both later steps to reference `${{ steps.version.outputs.build }}` instead of
independently deriving `github.run_number + 10`, keeping the embedded version
and appcast version synchronized.
- Around line 49-53: Update the feature-branch condition using REF_NAME so beta
releases are not built for every non-documentation push; gate them on the
narrower release-trigger criteria used by the workflow. Also update the appcast
deduplication logic to recognize beta versions by their base VERSION and retain
only the most recent bounded number of beta entries per base version, preventing
unbounded accumulation while preserving the latest beta releases.
🪄 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: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 502aa711-b6df-45bb-8b0f-60db1e614cd9

📥 Commits

Reviewing files that changed from the base of the PR and between 2c2b273 and 7a4c531.

📒 Files selected for processing (6)
  • .github/ISSUE_TEMPLATE/config.yml
  • .github/workflows/build-release.yml
  • AutoPiP/Resources/Script.js
  • AutoPiP/UpdateController.swift
  • README.md
  • appcast.xml
🚧 Files skipped from review as they are similar to previous changes (3)
  • .github/ISSUE_TEMPLATE/config.yml
  • AutoPiP/UpdateController.swift
  • AutoPiP/Resources/Script.js

Comment on lines +7 to +16
# Skip the workflow entirely when only docs/repo-meta/appcast files changed.
# Source, semver.txt and this workflow itself still trigger a build.
- '**.md'
- 'LICENSE'
- 'PRIVACY.md'
- 'SECURITY.md'
- 'renovate.json'
- '.gitignore'
- '.github/**'
- 'appcast.xml'

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

paths-ignore: .github/** contradicts the stated intent.

The comment states "this workflow itself still trigger a build," but .github/** matches .github/workflows/build-release.yml itself. A push that only edits this workflow file would be skipped entirely by GitHub's path filtering (not just gated by the check job), making it impossible to trigger a normal push-based test of workflow changes (aside from workflow_dispatch).

🔧 Proposed fix
-      - '.github/**'
+      - '.github/ISSUE_TEMPLATE/**'
+      - '.github/pull_request_template.md'
📝 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
# Skip the workflow entirely when only docs/repo-meta/appcast files changed.
# Source, semver.txt and this workflow itself still trigger a build.
- '**.md'
- 'LICENSE'
- 'PRIVACY.md'
- 'SECURITY.md'
- 'renovate.json'
- '.gitignore'
- '.github/**'
- 'appcast.xml'
# Skip the workflow entirely when only docs/repo-meta/appcast files changed.
# Source, semver.txt and this workflow itself still trigger a build.
- '**.md'
- 'LICENSE'
- 'PRIVACY.md'
- 'SECURITY.md'
- 'renovate.json'
- '.gitignore'
- '.github/ISSUE_TEMPLATE/**'
- '.github/pull_request_template.md'
- 'appcast.xml'
🤖 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-release.yml around lines 7 - 16, Update the
paths-ignore list in the workflow trigger configuration to stop excluding all of
.github/**, while continuing to ignore the intended documentation and repository
metadata files. Ensure changes to .github/workflows/build-release.yml itself
remain eligible to trigger the workflow as stated by the comment.

Comment on lines +76 to +78
concurrency:
group: build-${{ github.ref }}
cancel-in-progress: true

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Per-ref concurrency doesn't protect shared resources across branches.

build-${{ github.ref }} only serializes runs on the same ref. Two simultaneously-running feature branches (or a feature branch + main) can still race on:

  • Beta tag numbering (Lines 118-129): both read the same git tag -l "v${VERSION}-beta*" snapshot and can compute the same BETA_NUM, racing to create/publish the same tag.
  • The main-branch appcast push (Lines 433-467): two branches pushing appcast.xml to main concurrently can hit a non-fast-forward rejection with no retry/rebase, failing the job after the release/DMG already succeeded.

Consider a workflow-wide (not per-ref) concurrency group for the release-critical portion, or add fetch+rebase retry around the main push and re-check tag existence immediately before tag creation.

🤖 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-release.yml around lines 76 - 78, Update the
workflow concurrency configuration around the existing concurrency block to
serialize release-critical runs across all branches, rather than grouping by
github.ref. Use a single stable workflow-wide group while preserving
cancel-in-progress behavior, so beta tag generation and the main-branch appcast
push cannot run concurrently.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant