This document describes how to produce an end-to-end release of Fluffy Flash via the GitHub Actions pipeline in .github/workflows/release.yml.
The pipeline supports two paths:
- Manual dry-run (
workflow_dispatch) — produces an unsigned.appand the GPL source bundle. Used for verifying the pipeline without publishing a Release. - Tag push (
v*) — produces a Release attached to the tag. If the codesign / notarization secrets are present, the.appis signed and notarized automatically.
| Need | Where it lives |
|---|---|
| Apple Developer Program enrolment | apple.com |
Developer ID Application certificate (.p12 + password) |
local Keychain export |
| App-specific password (Apple ID) | appleid.apple.com → "App-Specific Passwords" |
| Apple Team ID | appleid.apple.com / Apple Developer portal |
Set the following on the repository (Settings → Secrets and variables → Actions):
| Secret | Description |
|---|---|
MACOS_CERTIFICATE_P12_BASE64 |
base64 -i DeveloperID.p12 | pbcopy of the exported certificate. |
MACOS_CERTIFICATE_PASSWORD |
The password used to export the .p12. |
MACOS_KEYCHAIN_PASSWORD |
Any throwaway string. Used to lock the temporary CI keychain. |
MACOS_DEVELOPER_ID_APPLICATION_NAME |
Identity name, e.g. Developer ID Application: Foo Bar (TEAMID). |
APPLE_ID |
Apple ID email used for notarization. |
APPLE_TEAM_ID |
Apple Team ID (10-char string). |
APPLE_APP_SPECIFIC_PASSWORD |
App-specific password generated for notarization. |
If any of these is missing, the pipeline still succeeds but produces an unsigned build (signing/notarization steps log a warning and skip).
Complete before tagging v*. All items should be green on the commit you tag.
| Check | Workflow | Where to look |
|---|---|---|
| Secret scan + forbidden paths | guardrails.yml |
Actions → Guardrails |
| SAST (Swift) | security-codeql.yml |
Actions → Security (CodeQL) |
| Semgrep + Trivy (repo) | security-semgrep-trivy.yml |
Actions → Security (Semgrep + Trivy) |
# After pushing your release branch / PR, confirm workflows are green:
gh run list --workflow=guardrails.yml --limit 3
gh run list --workflow=security-codeql.yml --limit 3
gh run list --workflow=security-semgrep-trivy.yml --limit 3CodeQL runs on macos-15 and builds Debug (unsigned) — allow ~15–30 minutes on first run.
cd FluffyFlash
WIST_SKIP_TOOL_BUNDLE=1 xcodebuild -project "Fluffy Flash.xcodeproj" -scheme FluffyFlash \
-configuration Debug -destination 'platform=macOS' build
WIST_SKIP_TOOL_BUNDLE=1 xcodebuild -project "Fluffy Flash.xcodeproj" -scheme FluffyFlash \
-configuration Debug -destination 'platform=macOS' test -only-testing:"Fluffy FlashTests"In Xcode (target FluffyFlash → Build Settings):
MARKETING_VERSION→ user-facing version (e.g.0.3.0)CURRENT_PROJECT_VERSION→ integer build (must increase for Sparkle), e.g.6
FluffyFlash/docs/RELEASE-NOTES-X.Y.Z.mdREADME.md,README.ru.md,FluffyFlash/README.mdFluffyFlash/THIRD_PARTY.mdif dependencies changedObsidianVault/10-Project-Wist/Sessions/YYYY-MM-DD.md(session note)
- Upload signed/notarized
FluffyFlash-X.Y.Z.zipto the GitHub Release first. - Run Sparkle
sign_updateon that ZIP (seeFluffyFlash/docs/Sparkle.md). - Add a new
<item>at the top ofFluffyFlash/appcast.xmlwithsparkle:version= build number,url= release asset URL. - Commit and push
appcast.xmlonly after the ZIP URL returns HTTP 200. - On a test Mac with the previous build: Settings → Check for updates…
Use this on every meaningful workflow change before tagging.
gh workflow run "Release artifacts" --ref main \
-f sign_and_notarize=false
gh run watchArtifacts (downloadable from the run page):
FluffyFlash-<branch-or-dev>.zipFluffyFlash-<branch-or-dev>.zip.sha256THIRD_PARTY_NOTICES.txtthird-party-sources.tar.gztool-versions.txtthird-party-manifest.json
If sign_and_notarize=true is passed, the same workflow also runs codesign + notarytool submit --wait + stapler staple — provided secrets are configured.
Merge your feature branch to main first, then:
git switch main
git pull --rebase
# Confirm security workflows are green on this commit (see §0).
git tag v0.3.0
git push origin v0.3.0
gh run watch --workflow="Release artifacts"The workflow auto-creates a GitHub Release named after the tag and attaches:
FluffyFlash-v0.3.0.zip(+.sha256)THIRD_PARTY_NOTICES.txt,third-party-sources.tar.gz,tool-versions.txt,third-party-manifest.json
If GitHub Secrets are configured, the .app inside the ZIP is codesigned + notarized. Then complete Sparkle steps in §0 (appcast + sign_update).
Typical ChromeOS / feature release flow:
git switch feature/chromeos-mode
git add -A && git commit -m "Release 0.3.0: ChromeOS mode, security CI, download fixes"
git push -u origin feature/chromeos-mode
# Open PR → wait for Guardrails + CodeQL + Semgrep/Trivy
gh pr merge --merge # or squash per your convention
git switch main && git pull
git tag v0.3.0 && git push origin v0.3.0
gh run watch --workflow="Release artifacts"
# After release assets exist → sign_update → update appcast.xml → pushTo delete a test release and tag:
gh release delete v0.0.0-rc1 --yes --cleanup-tagunzip FluffyFlash-v0.0.0-rc1.zip
codesign --verify --deep --strict --verbose=2 FluffyFlash.app
spctl -a -vv -t install FluffyFlash.app
xcrun stapler validate FluffyFlash.appExpected: accepted, source=Notarized Developer ID.
| Symptom | Likely cause | Fix |
|---|---|---|
xcodebuild step fails during Bundle Embedded CLI Tools |
Homebrew not yet primed on runner | The workflow now runs bundle-mac-cli-tools.sh explicitly before xcodebuild. Make sure brew is reachable. |
notarytool submit rejects with Invalid |
Missing hardened runtime, expired cert, embedded binary unsigned | Inspect notarization log: xcrun notarytool log <submission-id> --apple-id ... --team-id .... Sign every Mach-O inside Resources/Tools/bin and EmbeddedCLI/lib. |
spctl reports unsigned |
Pipeline ran without secrets | Configure all secrets above and rerun. |
| Tag-driven build does not pick up new files | The new files are not committed | The workflow checks out the tagged commit; ensure all changes are committed before tagging. |
When you publish a GitHub Release / Sparkle update, update user-facing README in the same work session (see .cursor/rules/github-release-readme.mdc):
| File | Purpose |
|---|---|
README.md |
GitHub landing page (English) |
README.ru.md |
Russian mirror |
FluffyFlash/README.md |
Xcode folder: version pointer + build commands |
Minimum content per release:
- What's new (or «Что нового») with the new tag link and 2–5 bullets.
- Features / requirements if the release changed user-visible scope (e.g. new Linux mode).
- Link to
FluffyFlash/docs/RELEASE-NOTES-X.Y.Z.mdfor full notes. - Remind existing users: Settings → Check for updates… (Sparkle).
The release badge in README uses releases/latest and updates automatically on GitHub.
Whenever a bundled CLI tool is added/replaced, follow .cursor/rules/legal-third-party-intake.mdc and update: