# Distributing an App with GerdsToolSet

This is the end-to-end checklist for shipping a notarized macOS app with the
GerdsToolSet build/publish pipeline. It assumes `GerdsToolSet/` is already
vendored into the project.

For the reference documentation of each individual script, see
[`README.md`](README.md).

## One-time setup (per project)

1. **Vendor the toolset.** Have `GerdsToolSet/` in the project root. A symlink
   `tagForDistribution -> GerdsToolSet/Xcode/perl/tagForDistribution` at the
   root is convenient but optional.
   
	git share GerdsToolSet ssh://git@gogs.local:10022/osx/GerdsToolSet.git
	ln -s GerdsToolSet/Xcode/perl/tagForDistribution t
	ln -s GerdsToolSet/Xcode/buildScripts/UpdateVersionPhaseScript v
	
	That allows to use `./v` to see the current version and `./t` to tag a new
	Version.

2. **Disable script sandboxing** so build-phase scripts can run. In the target's
   build settings or an `.xcconfig`:

	   ENABLE_USER_SCRIPT_SANDBOXING = NO

3. **Set the `GTS_*` config variables** (typically in `Release.xcconfig`):

   - `GTS_SITE_URL` — public base URL of the site, e.g.
     `https://www.example.com`. In `.xcconfig` files `//` starts a comment, so
     write the URL as `https:$()/$()/www.example.com` — each `$()` expands to
     empty and keeps the slashes intact.
   - `GTS_SITE_DIR` — local path to the site's `site/` directory, e.g.
     `$(HOME)/WebSites/Example/site`. Its parent directory must contain an
     executable `publishSite` script, which is run at the end to push the site
     live.
   - `GTS_TAG_LINE` — short tagline shown on the landing page.
   - `GTS_ACCENT_COLOR` — accent color for the generated pages, e.g. `#d97958`.
   - `GTS_COPYRIGHT` — copyright holder, e.g. `Example Consulting, LLC`.

   Optional overrides read by `updateSite`: `GTS_MIN_MACOS` (default `14.0`),
   `GTS_HOME_NAME` (default `BITart`), `GTS_HOME_URL`
   (default `https://www.bitart.com/`).

4. **Add the version build phase.** Add a *Run Script Phase* at the **end** of
   the target's Build Phases (leave the shell as `/bin/sh`):

	   $PROJECT_DIR/GerdsToolSet/Xcode/buildScripts/UpdateVersionPhaseScript

   This patches `CFBundleVersion` and `CFBundleShortVersionString` in the built
   Info.plist from the current git tag/branch. It edits the copy in the build
   products directory, so it does not dirty the project.

5. **Wire the post-archive hook.** Edit Scheme → Archive → Post-actions →
   click **+** → **New Run Script Action** → set "Provide build settings from"
   to your target → script:

	   ${PROJECT_DIR}/GerdsToolSet/Xcode/buildScripts/postArchiveHook

   On a successful archive this captures the build environment and spawns a
   Terminal window that continues with notarization and packaging.

6. **Store notarization credentials** (once per machine):

	   xcrun notarytool store-credentials notarytool

   The profile must be named `notarytool`. Without it the pipeline aborts
   (unless `SKIP_NOTARIZATION = 1`). Optionally set `SPARKLE_KEY_FILE` to point
   at the Sparkle private key file; otherwise the default key from the keychain
   is used to sign appcast entries.

## Per-release workflow

1. **Commit everything.** `tagForDistribution` and the version phase refuse to
   run cleanly with uncommitted changes.

2. **Tag the release** on `main` (or `release`):

	   ./tagForDistribution        # -n to preview, -r to move the last tag onto a fix commit

   This produces a `yy.n` tag — the last two digits of the year plus the release
   number within that year (auto-incremented from existing tags).

3. **Archive** in Xcode: Product → Archive. This runs
   `UpdateVersionPhaseScript` to stamp the real version into the plist, then
   `postArchiveHook` fires and opens a Terminal running `notarizeAndPackage`.

4. **`notarizeAndPackage` runs automatically** and:

   - exports a Developer ID–signed `.app` from the archive,
   - zips it, submits to Apple (`notarytool submit --wait`), and staples the
     ticket,
   - builds the distribution zip `<Product>-<version>.zip`,
   - calls `updateSite`, which renders the landing/history/appcast pages and
     runs `publishSite` to push the site live.

## The version decides the "train"

There is no separate switch — which train an archive publishes to is decided
entirely by the version string:

- **Official release** — the build sits exactly *on* a distribution tag, so
  `commitsSince` is `0` and the version is `<yy>.<releaseNumber>` (e.g. `26.2`).
  It publishes to the official train under `site/<app>/`.
- **Dev release** — the build has commits *past* the most recent tag, so
  `commitsSince` is non-zero (e.g. `26.2.3`). It publishes to the dev train
  under `site/<app>/dev/`.

Promote dev → official by running `tagForDistribution` (which bumps
`releaseNumber` and tags), then archiving from that tag. On an official publish
the scripts also purge any dev releases at or below the new official version and
re-render the dev train so its subscribers immediately see the new official as
the latest.

## Escape hatches (dev iteration)

- `SKIP_NOTARIZATION = 1` — package and update the site, but skip the Apple
  round-trip (no submit, no staple).
- `SKIP_PACKAGING = 1` — archive normally but skip the entire post-archive
  pipeline.
- **Re-render the site without re-archiving** — after a successful archive, edit
  `<app>/Site/index.md` or the templates under `GerdsToolSet/Site/templates/`
  and re-publish without rebuilding or re-notarizing:

	   cd <project root>
	   ./GerdsToolSet/Xcode/buildScripts/updateSite

  It reuses the last archive's saved env file (`$BUILD_DIR/Release/lastArchiveEnv.sh`)
  and proceeds straight to template substitution and `publishSite`. Xcode's
  "Clean Build Folder" wipes that file, forcing a real archive again.
