Organization-wide CI/CD building blocks for thaw-app.
Product repos (including apps that are not yet under thaw-app) should reference these actions by their org-qualified GitHub path, pinned to a commit SHA:
uses: thaw-app/org-ci/actions/configure-signing@<sha>
uses: thaw-app/org-ci/actions/build@<sha>
uses: thaw-app/org-ci/actions/export-and-package@<sha>
uses: thaw-app/org-ci/actions/notarize-and-validate@<sha>
uses: thaw-app/org-ci/actions/sparkle-release@<sha>
uses: thaw-app/org-ci/actions/publish-file-to-branch@<sha>export-and-package defaults to fetching DMG art from the public org repo thaw-app/brand-assets. Override brand-assets-repository / dmg-background if needed.
| Action | Purpose |
|---|---|
actions/configure-signing |
Import Developer ID cert + notarytool profile |
actions/build |
xcodebuild archive (Developer ID, hardened runtime) |
actions/export-and-package |
Export + signed DMG |
actions/notarize-and-validate |
notarytool + staple + Gatekeeper |
actions/sparkle-release |
Sparkle ZIP + signed appcast.xml (does not publish) |
actions/publish-file-to-branch |
Commit one file onto a branch, retrying on push conflict |
By default, sparkle-release keeps ZIP download URLs and appcast Pages on the
caller repository (legacy). To split installers from update payloads:
| Artifact | Typical home |
|---|---|
| DMG / human GitHub Release | Application repo (for example thaw-app/Thaw) |
Sparkle ZIP, deltas, appcast.xml |
Dedicated updates repo (for example thaw-app/updates) |
Pass:
- uses: thaw-app/org-ci/actions/sparkle-release@<sha>
with:
# …
updates-repository: thaw-app/updates
updates-token: ${{ secrets.UPDATES_GITHUB_TOKEN }}
release-html-url: https://github.com/${{ github.repository }}/releases/tag/${{ inputs.tag }}sparkle-release generates the appcast but does not publish it. Push the
appcast-path output yourself, which lets one run update several feeds:
- uses: thaw-app/org-ci/actions/publish-file-to-branch@<sha>
with:
repository: thaw-app/updates
branch: gh-pages
source-path: ${{ steps.sparkle.outputs.appcast-path }}
destination-path: appcast.xml
token: ${{ secrets.UPDATES_GITHUB_TOKEN }}
commit-message: "chore(sparkle): publish appcast for ${{ inputs.tag }}"
create-branch-if-missing: "true"
gitignore-allowlist: |
*
!appcast.xml
!.gitignoreupdates-token reads the existing feed and prior ZIPs. The token passed to
publish-file-to-branch needs contents: write on the target repo. When
updates-repository is empty, behavior is unchanged and github.token is
enough.
The build actions in a ship pipeline must run in the same job (shared $RUNNER_TEMP keychain, exported app, Sparkle env).
Typical order:
configure-signingbuildexport-and-package(notarizes + staples the.app, then builds the DMG)notarize-and-validate(notarizes + staples the DMG)sparkle-release(optional)publish-file-to-branch(optional, once per feed)
Two notary submissions per build is deliberate: stapling only the DMG leaves
the enclosed .app without a ticket, so it fails Gatekeeper on first launch
without network access.
configure-signing and notarize-and-validate share a keychain path (default $RUNNER_TEMP/buildagent.keychain). Override both with the same keychain-path if needed.
- Release / Sparkle publish on the caller repo:
permissions: contents: write(DMG / GitHub Release) - Dedicated updates feed: a PAT or GitHub App token with
contents: writeon the updates repo (updates-token), plus callercontents: writefor the DMG release - Read-only DMG builds can omit write if they only upload artifacts
If two Sparkle publishes can race on gh-pages, set a workflow concurrency group, for example:
concurrency:
group: sparkle-appcast
cancel-in-progress: falsepublish-file-to-branch retries refetch+push on conflict (max-attempts,
default 5).
- Pass exactly one of
project-nameorworkspace-name enable-hardened-runtimedefaults totrue(needed for notarization unless the Xcode project already sets it)
- Actions are product-agnostic: pass
app-name, scheme,asset-prefix, etc. from the caller. - No org membership is required to use these actions from a public consumer repo; only public read of
thaw-app/org-ci(andthaw-app/brand-assetswhen used).