docs: add release process and notarization script

This commit is contained in:
Peter Steinberger 2026-01-04 01:53:56 +01:00
parent d88c59321d
commit 9258c16278
3 changed files with 196 additions and 0 deletions

85
.github/workflows/release.yml vendored Normal file
View File

@ -0,0 +1,85 @@
name: release
on:
workflow_dispatch:
inputs:
tag:
description: "Tag to (re)release (e.g. v0.1.0)"
required: true
type: string
permissions:
contents: write
jobs:
release:
runs-on: macos-latest
steps:
- name: Checkout
uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Determine tag
id: tag
shell: bash
run: |
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
else
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
fi
- name: Checkout release tag
if: ${{ github.event_name == 'workflow_dispatch' }}
run: git checkout ${{ inputs.tag }}
- name: Resolve packages
run: swift package resolve
- name: Sync version
run: scripts/generate-version.sh
- name: Build
run: swift build -c release --product remindctl
- name: Codesign
run: codesign --force --sign - --identifier com.steipete.remindctl .build/release/remindctl
- name: Package artifact
run: |
mkdir -p dist
cp .build/release/remindctl dist/remindctl
(
cd dist
zip -r remindctl-macos.zip remindctl
)
- name: Publish release assets
uses: softprops/action-gh-release@v2
with:
files: dist/remindctl-macos.zip
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
- name: Update GitHub release notes from CHANGELOG
shell: bash
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
TAG: ${{ steps.tag.outputs.tag }}
run: |
version="${TAG#v}"
notes_file="/tmp/release-notes.md"
awk -v v="$version" '
$0 ~ ("^## " v "($|[[:space:]]-)") { in_section=1; next }
in_section && $0 ~ "^## " { exit }
in_section { print }
' CHANGELOG.md > "$notes_file"
if ! grep -q '[^[:space:]]' "$notes_file"; then
echo "No CHANGELOG.md section found for version $version" >&2
exit 1
fi
gh release edit "$TAG" --notes-file "$notes_file"

38
docs/RELEASING.md Normal file
View File

@ -0,0 +1,38 @@
# Releasing
## Release notes source
- GitHub Release notes come from `CHANGELOG.md` for the matching version section (`## X.Y.Z - YYYY-MM-DD`).
## Steps
1. Update changelog and version
- Ensure `CHANGELOG.md` has `## 0.1.0 - YYYY-MM-DD` with final notes.
- Update `version.env` to `0.1.0` (already set for the first release).
- Run `scripts/generate-version.sh` (refreshes `Sources/remindctl/Version.swift` + embedded Info.plist).
2. Ensure checks are green
- `make check`
3. Build, sign, and notarize (local)
- Requires `APP_STORE_CONNECT_API_KEY_P8`, `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`.
- `scripts/sign-and-notarize.sh` (outputs `/tmp/remindctl-macos.zip` by default).
4. Tag, push, and publish
- `git tag -a v0.1.0 -m "v0.1.0"`
- `git push origin v0.1.0`
- Extract release notes:
```sh
version=0.1.0
notes_file=/tmp/release-notes.txt
awk -v v="$version" '
$0 ~ ("^## " v "($|[[:space:]]-)") { in_section=1; next }
in_section && $0 ~ "^## " { exit }
in_section { print }
' CHANGELOG.md > "$notes_file"
```
- Create GitHub release:
```sh
gh release create v0.1.0 /tmp/remindctl-macos.zip -t "v0.1.0" -F /tmp/release-notes.txt
```
5. Homebrew tap
- Update `../homebrew-tap/Formula/remindctl.rb` to point at the GitHub release asset.
## What happens in CI
- Release signing + notarization are done locally via `scripts/sign-and-notarize.sh`.
- `.github/workflows/release.yml` is only for manual rebuilds, not the primary release path.

73
scripts/sign-and-notarize.sh Executable file
View File

@ -0,0 +1,73 @@
#!/usr/bin/env bash
set -euo pipefail
ROOT=$(cd "$(dirname "$0")/.." && pwd)
source "$ROOT/version.env"
APP_NAME="remindctl"
CODESIGN_IDENTITY=${CODESIGN_IDENTITY:-"Developer ID Application: Peter Steinberger (Y5PE65HELJ)"}
ENTITLEMENTS="${ROOT}/Resources/remindctl.entitlements"
OUTPUT_DIR=${OUTPUT_DIR:-/tmp}
ZIP_PATH="${OUTPUT_DIR}/remindctl-macos.zip"
ARCHES_VALUE=${ARCHES:-"arm64 x86_64"}
ARCH_LIST=( ${ARCHES_VALUE} )
DIST_DIR="$(mktemp -d "/tmp/${APP_NAME}-dist.XXXXXX")"
API_KEY_FILE="$(mktemp "/tmp/${APP_NAME}-notary.XXXXXX.p8")"
cleanup() {
rm -f "$API_KEY_FILE"
rm -rf "$DIST_DIR"
}
trap cleanup EXIT
if [[ -z "${APP_STORE_CONNECT_API_KEY_P8:-}" || -z "${APP_STORE_CONNECT_KEY_ID:-}" || -z "${APP_STORE_CONNECT_ISSUER_ID:-}" ]]; then
echo "Missing APP_STORE_CONNECT_* env vars (API key, key id, issuer id)." >&2
exit 1
fi
echo "$APP_STORE_CONNECT_API_KEY_P8" | sed 's/\\n/\n/g' > "$API_KEY_FILE"
"$ROOT/scripts/generate-version.sh"
for ARCH in "${ARCH_LIST[@]}"; do
swift build -c release --product remindctl --arch "$ARCH"
done
BINARIES=()
for ARCH in "${ARCH_LIST[@]}"; do
BINARIES+=("$ROOT/.build/${ARCH}-apple-macosx/release/remindctl")
done
lipo -create "${BINARIES[@]}" -output "$DIST_DIR/remindctl"
if [[ -f "$ENTITLEMENTS" ]]; then
codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \
--entitlements "$ENTITLEMENTS" \
"$DIST_DIR/remindctl"
else
codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \
"$DIST_DIR/remindctl"
fi
chmod -R u+rw "$DIST_DIR"
xattr -cr "$DIST_DIR"
find "$DIST_DIR" -name '._*' -delete
DITTO_BIN=${DITTO_BIN:-/usr/bin/ditto}
(
cd "$DIST_DIR"
"$DITTO_BIN" --norsrc -c -k . "$ZIP_PATH"
)
xcrun notarytool submit "$ZIP_PATH" \
--key "$API_KEY_FILE" \
--key-id "$APP_STORE_CONNECT_KEY_ID" \
--issuer "$APP_STORE_CONNECT_ISSUER_ID" \
--wait
codesign --verify --strict --verbose=4 "$DIST_DIR/remindctl"
if ! spctl -a -t exec -vv "$DIST_DIR/remindctl"; then
echo "spctl check failed (CLI binaries often report 'not an app')." >&2
fi
echo "Done: $ZIP_PATH"