Skip to content

feat(macos): Ulk.app v0.3 — Sparkle 2.x + tag-triggered release pipeline#148

Merged
izo merged 4 commits intomainfrom
claude/macos-ulk-v0.3-sparkle-release
May 6, 2026
Merged

feat(macos): Ulk.app v0.3 — Sparkle 2.x + tag-triggered release pipeline#148
izo merged 4 commits intomainfrom
claude/macos-ulk-v0.3-sparkle-release

Conversation

@izo
Copy link
Copy Markdown
Owner

@izo izo commented May 6, 2026

Summary

v0.3 — l'app a maintenant un vrai auto-update Sparkle + un pipeline release CI signé/notarié de bout en bout. Quatre commits atomiques :

  1. feat(macos) — Sparkle 2.6+ ajouté en SPM (project.yml + Package.swift) ; UpdateChecker réécrit comme façade SwiftUI au-dessus de SPUStandardUpdaterController/SPUUpdaterDelegate ; DashboardShell sidebar gère les nouveaux états (download progress + restart-to-install)
  2. build(macos)scripts/sign-and-notarize.sh (xcodebuild → DMG → notarytool wait → stapler → sign Sparkle) + scripts/generate-appcast.sh (append d'un <item> à site/public/appcast.xml via Python)
  3. ci(macos).github/workflows/macos-release.yml triggered par tag macos-v* : import certif, store notary, run pipeline, publish GitHub release, ouvre une PR pour le site avec le nouvel appcast
  4. docs(macos) — README : runbook complet + 7 GitHub Secrets requis + génération keypair EdDSA ; CARD.md changelog v0.3

Pour activer le pipeline (côté toi)

Configurer les 7 secrets GitHub (Settings → Secrets and variables → Actions) — détaillé dans apps/macos/Ulk/README.md § Release pipeline :

Secret Source
APPLE_TEAM_ID Apple Developer Portal → Membership
APPLE_SIGNING_IDENTITY security find-identity -v -p codesigning
APPLE_CERTIFICATE_BASE64 Export .p12 depuis Keychain Access → base64 -i cert.p12
APPLE_CERTIFICATE_PASSWORD Mot de passe choisi à l'export
APPLE_NOTARY_APPLE_ID Email Apple ID dev
APPLE_NOTARY_APP_SPECIFIC_PASSWORD https://appleid.apple.com → App-Specific Passwords
SPARKLE_PRIVATE_KEY generate_keys Sparkle → export keychain → base64 -i ed25519_priv.pem

Une fois en place, clé publique Sparkle à coller dans apps/macos/Ulk/Ulk/Resources/Info.plist (SUPublicEDKey, actuellement placeholder).

Couper la première release

git checkout main && git pull
git tag macos-v0.3.0 && git push origin macos-v0.3.0
# → workflow démarre, ~10 min (notarisation incluse)
# → GitHub release publié avec DMG + appcast
# → PR auto chore/appcast-0.3.0 ouverte → merge pour publier le feed

Architecture

                  ┌──────────────────────────┐
                  │   git tag macos-v0.3.0   │
                  └──────────┬───────────────┘
                             ▼
              ┌──────────────────────────────┐
              │ macos-release.yml (macos-14) │
              ├──────────────────────────────┤
              │ • xcodegen + xcodebuild      │
              │ • developer-id sign          │
              │ • create-dmg                 │
              │ • notarytool wait + staple   │
              │ • Sparkle sign_update        │
              │ • generate-appcast.sh        │
              └──────────┬─────────┬─────────┘
                         ▼         ▼
            ┌─────────────────┐  ┌────────────────────────┐
            │ GitHub Release  │  │ PR chore/appcast-x.y.z │
            │ + DMG + .eddsa  │  │ → site/public/appcast  │
            │ + appcast.xml   │  │ → ulk.regrets.app      │
            └─────────────────┘  └────────────────────────┘
                         │
                         ▼
              ┌──────────────────────────────┐
              │ Ulk.app (chez l'utilisateur) │
              │ Sparkle poll appcast.xml     │
              │ → notification update        │
              └──────────────────────────────┘

Test plan

  • CI macOS-app SwiftPM build + test passe (Sparkle SPM resolve → UlkCore link → tests pass)
  • CI macOS-app XcodeGen + xcodebuild passe (Sparkle SPM resolve dans Xcode → app build sans signing)
  • Test manuel après merge + tag : configurer les 7 secrets, push macos-v0.3.0-rc1, vérifier GitHub release + PR appcast

Limites assumées

  • Le release workflow n'est pas testable en CI sans les secrets Apple — premier vrai run = première release publique
  • Sparkle public key dans Info.plist est un placeholder — à remplacer avant la première vraie release
  • Pas de notarisation côté Ulk.app direct (on notarise le DMG, ça notarise tout dedans)

Note sur les rouges attendus

Spec : docs/backlog/2026-05-06-FEATURE-MACOS-COMPANION-APP/CARD.md

https://claude.ai/code/session_01TyhPjizanX1g8xSQ6chwTu


Generated by Claude Code

claude added 4 commits May 6, 2026 22:09
Replaces the GitHub-API stub UpdateChecker with a real Sparkle 2.x
integration. UpdateChecker is now a SwiftUI-friendly facade over
SPUStandardUpdaterController that surfaces six states (idle, checking,
upToDate, updateAvailable, downloading, readyToInstall, failed) via
@published — DashboardShell sidebar binds directly to those.

- project.yml : adds sparkle-project/Sparkle ≥ 2.6 SPM package and
  declares it as a dependency of the Ulk app target. Bumps
  MARKETING_VERSION to 0.3.0 / CURRENT_PROJECT_VERSION to 3.
- Package.swift : same Sparkle dep on UlkCore so the SPM CI job that
  syntax-checks the library still builds (UpdateChecker imports Sparkle).
- UpdateChecker.swift : rewritten as ObservableObject + NSObject
  conforming to SPUUpdaterDelegate. Exposes checkNow() (user-triggered)
  and checkInBackground() (silent at launch). Delegate callbacks are
  nonisolated and hop to @mainactor before mutating state. bundleVersion
  helper is nonisolated static for thread safety.
- DashboardShell.swift : sidebar update badge handles the two new states
  (downloading with progress + readyToInstall with restart hint).

Real release pipeline lives in scripts/sign-and-notarize.sh and
.github/workflows/macos-release.yml — see README for the 7 GitHub
secrets to configure.

https://claude.ai/code/session_01TyhPjizanX1g8xSQ6chwTu
Two scripts that produce a publishable Ulk.app DMG:

- scripts/sign-and-notarize.sh  — xcodegen → xcodebuild archive →
  exportArchive (developer-id) → create-dmg → codesign → notarytool
  submit --wait → stapler staple → sign with Sparkle EdDSA key.
  Reads APPLE_TEAM_ID, APPLE_SIGNING_IDENTITY, APPLE_NOTARY_PROFILE,
  SPARKLE_PRIVATE_KEY_FILE from env.

- scripts/generate-appcast.sh — appends a fully-formed Sparkle 2 <item>
  to site/public/appcast.xml using a Python helper for safe XML edits
  (regex + str.replace are too brittle for nested CDATA notes). Pulls
  the Sparkle signature from the .eddsa sidecar produced above.

Both scripts are idempotent and can be invoked locally or by the new
.github/workflows/macos-release.yml workflow (next commit).

https://claude.ai/code/session_01TyhPjizanX1g8xSQ6chwTu
Pushing a tag matching macos-v* (e.g. macos-v0.3.0) now triggers a full
end-to-end release on macos-14 :

  1. Resolve version from tag (or workflow_dispatch input)
  2. Install xcodegen + create-dmg
  3. Import .p12 cert into a temporary keychain (APPLE_CERTIFICATE_BASE64
     + APPLE_CERTIFICATE_PASSWORD)
  4. Store notary credentials via xcrun notarytool store-credentials
     (APPLE_NOTARY_APPLE_ID + APPLE_NOTARY_APP_SPECIFIC_PASSWORD +
     APPLE_TEAM_ID)
  5. Decode Sparkle private key from SPARKLE_PRIVATE_KEY (base64)
  6. Run scripts/sign-and-notarize.sh → produces signed DMG + .eddsa
  7. Run scripts/generate-appcast.sh → updates site/public/appcast.xml
  8. Publish GitHub release with DMG, .eddsa and appcast as assets
  9. Open a chore/appcast-<version> PR with the updated appcast so the
     site (Astro static at https://ulk.regrets.app) deploys the new feed

site/public/appcast.xml seeded as an empty Sparkle 2 RSS skeleton ;
each release inserts a new <item> right after the <description>.

Required GitHub Secrets are documented in workflow comments and in
apps/macos/Ulk/README.md (Release pipeline section).

https://claude.ai/code/session_01TyhPjizanX1g8xSQ6chwTu
- README : new "Auto-update (Sparkle 2.x)" + "Release pipeline" sections
  detailing the 7 GitHub Secrets, how to generate the EdDSA keypair
  with Sparkle's generate_keys tool, how to cut a release (`git tag
  macos-v0.3.0 && git push`), and how to do a manual local release as
  a fallback.
- Spec card : v0.3 roadmap entry promoted to "in this branch" with full
  checklist done ; v0.4+ section absorbs the post-MVP extras formerly
  labelled v1.0 (settings.json editor, accountability viewer, Cloud
  Routines / Managed Agents tabs, plus deferred MCP/CLI toggles).

https://claude.ai/code/session_01TyhPjizanX1g8xSQ6chwTu
@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 6, 2026

Warning

Rate limit exceeded

@izo has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 21 minutes and 53 seconds before requesting another review.

To continue reviewing without waiting, purchase usage credits in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 869e86fb-d13c-4fc9-9a75-d12bb5e5666a

📥 Commits

Reviewing files that changed from the base of the PR and between 5305c12 and ea80071.

📒 Files selected for processing (10)
  • .github/workflows/macos-release.yml
  • apps/macos/Ulk/Package.swift
  • apps/macos/Ulk/README.md
  • apps/macos/Ulk/Ulk/Scenes/Dashboard/DashboardShell.swift
  • apps/macos/Ulk/Ulk/Services/UpdateChecker.swift
  • apps/macos/Ulk/project.yml
  • apps/macos/Ulk/scripts/generate-appcast.sh
  • apps/macos/Ulk/scripts/sign-and-notarize.sh
  • docs/backlog/2026-05-06-FEATURE-MACOS-COMPANION-APP/CARD.md
  • site/public/appcast.xml
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch claude/macos-ulk-v0.3-sparkle-release

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.

@izo izo marked this pull request as ready for review May 6, 2026 22:13
@izo izo merged commit b60f0a8 into main May 6, 2026
5 of 7 checks passed
@izo izo deleted the claude/macos-ulk-v0.3-sparkle-release branch May 6, 2026 22:13
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.

2 participants