Skip to content

Commit 9258c16

Browse files
committed
docs: add release process and notarization script
1 parent d88c593 commit 9258c16

3 files changed

Lines changed: 196 additions & 0 deletions

File tree

.github/workflows/release.yml

Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
name: release
2+
3+
on:
4+
workflow_dispatch:
5+
inputs:
6+
tag:
7+
description: "Tag to (re)release (e.g. v0.1.0)"
8+
required: true
9+
type: string
10+
11+
permissions:
12+
contents: write
13+
14+
jobs:
15+
release:
16+
runs-on: macos-latest
17+
steps:
18+
- name: Checkout
19+
uses: actions/checkout@v4
20+
with:
21+
fetch-depth: 0
22+
23+
- name: Determine tag
24+
id: tag
25+
shell: bash
26+
run: |
27+
if [[ "${GITHUB_EVENT_NAME}" == "workflow_dispatch" ]]; then
28+
echo "tag=${{ inputs.tag }}" >> "$GITHUB_OUTPUT"
29+
else
30+
echo "tag=${GITHUB_REF_NAME}" >> "$GITHUB_OUTPUT"
31+
fi
32+
33+
- name: Checkout release tag
34+
if: ${{ github.event_name == 'workflow_dispatch' }}
35+
run: git checkout ${{ inputs.tag }}
36+
37+
- name: Resolve packages
38+
run: swift package resolve
39+
40+
- name: Sync version
41+
run: scripts/generate-version.sh
42+
43+
- name: Build
44+
run: swift build -c release --product remindctl
45+
46+
- name: Codesign
47+
run: codesign --force --sign - --identifier com.steipete.remindctl .build/release/remindctl
48+
49+
- name: Package artifact
50+
run: |
51+
mkdir -p dist
52+
cp .build/release/remindctl dist/remindctl
53+
(
54+
cd dist
55+
zip -r remindctl-macos.zip remindctl
56+
)
57+
58+
- name: Publish release assets
59+
uses: softprops/action-gh-release@v2
60+
with:
61+
files: dist/remindctl-macos.zip
62+
env:
63+
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
64+
65+
- name: Update GitHub release notes from CHANGELOG
66+
shell: bash
67+
env:
68+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
69+
TAG: ${{ steps.tag.outputs.tag }}
70+
run: |
71+
version="${TAG#v}"
72+
notes_file="/tmp/release-notes.md"
73+
74+
awk -v v="$version" '
75+
$0 ~ ("^## " v "($|[[:space:]]-)") { in_section=1; next }
76+
in_section && $0 ~ "^## " { exit }
77+
in_section { print }
78+
' CHANGELOG.md > "$notes_file"
79+
80+
if ! grep -q '[^[:space:]]' "$notes_file"; then
81+
echo "No CHANGELOG.md section found for version $version" >&2
82+
exit 1
83+
fi
84+
85+
gh release edit "$TAG" --notes-file "$notes_file"

docs/RELEASING.md

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
# Releasing
2+
3+
## Release notes source
4+
- GitHub Release notes come from `CHANGELOG.md` for the matching version section (`## X.Y.Z - YYYY-MM-DD`).
5+
6+
## Steps
7+
1. Update changelog and version
8+
- Ensure `CHANGELOG.md` has `## 0.1.0 - YYYY-MM-DD` with final notes.
9+
- Update `version.env` to `0.1.0` (already set for the first release).
10+
- Run `scripts/generate-version.sh` (refreshes `Sources/remindctl/Version.swift` + embedded Info.plist).
11+
2. Ensure checks are green
12+
- `make check`
13+
3. Build, sign, and notarize (local)
14+
- Requires `APP_STORE_CONNECT_API_KEY_P8`, `APP_STORE_CONNECT_KEY_ID`, `APP_STORE_CONNECT_ISSUER_ID`.
15+
- `scripts/sign-and-notarize.sh` (outputs `/tmp/remindctl-macos.zip` by default).
16+
4. Tag, push, and publish
17+
- `git tag -a v0.1.0 -m "v0.1.0"`
18+
- `git push origin v0.1.0`
19+
- Extract release notes:
20+
```sh
21+
version=0.1.0
22+
notes_file=/tmp/release-notes.txt
23+
awk -v v="$version" '
24+
$0 ~ ("^## " v "($|[[:space:]]-)") { in_section=1; next }
25+
in_section && $0 ~ "^## " { exit }
26+
in_section { print }
27+
' CHANGELOG.md > "$notes_file"
28+
```
29+
- Create GitHub release:
30+
```sh
31+
gh release create v0.1.0 /tmp/remindctl-macos.zip -t "v0.1.0" -F /tmp/release-notes.txt
32+
```
33+
5. Homebrew tap
34+
- Update `../homebrew-tap/Formula/remindctl.rb` to point at the GitHub release asset.
35+
36+
## What happens in CI
37+
- Release signing + notarization are done locally via `scripts/sign-and-notarize.sh`.
38+
- `.github/workflows/release.yml` is only for manual rebuilds, not the primary release path.

scripts/sign-and-notarize.sh

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
ROOT=$(cd "$(dirname "$0")/.." && pwd)
5+
source "$ROOT/version.env"
6+
7+
APP_NAME="remindctl"
8+
CODESIGN_IDENTITY=${CODESIGN_IDENTITY:-"Developer ID Application: Peter Steinberger (Y5PE65HELJ)"}
9+
ENTITLEMENTS="${ROOT}/Resources/remindctl.entitlements"
10+
OUTPUT_DIR=${OUTPUT_DIR:-/tmp}
11+
ZIP_PATH="${OUTPUT_DIR}/remindctl-macos.zip"
12+
ARCHES_VALUE=${ARCHES:-"arm64 x86_64"}
13+
ARCH_LIST=( ${ARCHES_VALUE} )
14+
DIST_DIR="$(mktemp -d "/tmp/${APP_NAME}-dist.XXXXXX")"
15+
API_KEY_FILE="$(mktemp "/tmp/${APP_NAME}-notary.XXXXXX.p8")"
16+
17+
cleanup() {
18+
rm -f "$API_KEY_FILE"
19+
rm -rf "$DIST_DIR"
20+
}
21+
trap cleanup EXIT
22+
23+
if [[ -z "${APP_STORE_CONNECT_API_KEY_P8:-}" || -z "${APP_STORE_CONNECT_KEY_ID:-}" || -z "${APP_STORE_CONNECT_ISSUER_ID:-}" ]]; then
24+
echo "Missing APP_STORE_CONNECT_* env vars (API key, key id, issuer id)." >&2
25+
exit 1
26+
fi
27+
28+
echo "$APP_STORE_CONNECT_API_KEY_P8" | sed 's/\\n/\n/g' > "$API_KEY_FILE"
29+
30+
"$ROOT/scripts/generate-version.sh"
31+
32+
for ARCH in "${ARCH_LIST[@]}"; do
33+
swift build -c release --product remindctl --arch "$ARCH"
34+
done
35+
36+
BINARIES=()
37+
for ARCH in "${ARCH_LIST[@]}"; do
38+
BINARIES+=("$ROOT/.build/${ARCH}-apple-macosx/release/remindctl")
39+
done
40+
41+
lipo -create "${BINARIES[@]}" -output "$DIST_DIR/remindctl"
42+
43+
if [[ -f "$ENTITLEMENTS" ]]; then
44+
codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \
45+
--entitlements "$ENTITLEMENTS" \
46+
"$DIST_DIR/remindctl"
47+
else
48+
codesign --force --timestamp --options runtime --sign "$CODESIGN_IDENTITY" \
49+
"$DIST_DIR/remindctl"
50+
fi
51+
52+
chmod -R u+rw "$DIST_DIR"
53+
xattr -cr "$DIST_DIR"
54+
find "$DIST_DIR" -name '._*' -delete
55+
56+
DITTO_BIN=${DITTO_BIN:-/usr/bin/ditto}
57+
(
58+
cd "$DIST_DIR"
59+
"$DITTO_BIN" --norsrc -c -k . "$ZIP_PATH"
60+
)
61+
62+
xcrun notarytool submit "$ZIP_PATH" \
63+
--key "$API_KEY_FILE" \
64+
--key-id "$APP_STORE_CONNECT_KEY_ID" \
65+
--issuer "$APP_STORE_CONNECT_ISSUER_ID" \
66+
--wait
67+
68+
codesign --verify --strict --verbose=4 "$DIST_DIR/remindctl"
69+
if ! spctl -a -t exec -vv "$DIST_DIR/remindctl"; then
70+
echo "spctl check failed (CLI binaries often report 'not an app')." >&2
71+
fi
72+
73+
echo "Done: $ZIP_PATH"

0 commit comments

Comments
 (0)