@@ -13,12 +13,15 @@ concurrency:
1313 cancel-in-progress : false
1414
1515# Required repository configuration before pushing a v* tag:
16- # Variable:
16+ # Variables:
17+ # APPLE_TEAM_ID: free Personal Team identifier used by the Apple Development certificate.
1718# SPARKLE_PUBLIC_ED_KEY: output from Sparkle's generate_keys tool.
18- # Secret:
19+ # Secrets:
20+ # APPLE_DEVELOPMENT_CERTIFICATE: base64-encoded Apple Development .p12 export.
21+ # APPLE_DEVELOPMENT_CERTIFICATE_PASSWORD: password chosen when exporting the .p12.
1922# SPARKLE_PRIVATE_ED_KEY: contents of the file exported by generate_keys -x.
2023# The public and private Sparkle keys must be generated as one pair.
21- # Releases are intentionally Apple-unsigned and are authenticated by Sparkle EdDSA.
24+ # Releases are Apple Development-signed (not notarized) and Sparkle EdDSA-authenticated .
2225
2326jobs :
2427 release :
2730 runs-on : macos-26
2831 env :
2932 DEVELOPER_DIR : /Applications/Xcode_26.3.app/Contents/Developer
33+ APPLE_TEAM_ID : ${{ vars.APPLE_TEAM_ID }}
3034 SPARKLE_PUBLIC_ED_KEY : ${{ vars.SPARKLE_PUBLIC_ED_KEY }}
35+ APPLE_DEVELOPMENT_CERTIFICATE : ${{ secrets.APPLE_DEVELOPMENT_CERTIFICATE }}
36+ APPLE_DEVELOPMENT_CERTIFICATE_PASSWORD : ${{ secrets.APPLE_DEVELOPMENT_CERTIFICATE_PASSWORD }}
3137 SPARKLE_PRIVATE_ED_KEY : ${{ secrets.SPARKLE_PRIVATE_ED_KEY }}
3238 GH_TOKEN : ${{ github.token }}
3339
5157 run : |
5258 set -euo pipefail
5359 missing=()
54- for name in SPARKLE_PUBLIC_ED_KEY SPARKLE_PRIVATE_ED_KEY; do
60+ for name in APPLE_TEAM_ID SPARKLE_PUBLIC_ED_KEY APPLE_DEVELOPMENT_CERTIFICATE APPLE_DEVELOPMENT_CERTIFICATE_PASSWORD SPARKLE_PRIVATE_ED_KEY; do
5561 if [[ -z "${!name:-}" ]]; then
5662 missing+=("$name")
5763 fi
6975 echo "SPARKLE_PUBLIC_ED_KEY must decode to 32 bytes, found $decoded_length." >&2
7076 exit 1
7177 }
78+ [[ "$APPLE_TEAM_ID" =~ ^[A-Z0-9]{10}$ ]] || {
79+ echo "APPLE_TEAM_ID must be a 10-character team identifier." >&2
80+ exit 1
81+ }
82+
83+ - name : Import Apple Development certificate
84+ shell : bash
85+ run : |
86+ set -euo pipefail
87+ keychain="$RUNNER_TEMP/release-signing.keychain-db"
88+ certificate="$RUNNER_TEMP/apple-development.p12"
89+ keychain_password="$(openssl rand -hex 24)"
90+ printf '%s' "$APPLE_DEVELOPMENT_CERTIFICATE" | /usr/bin/base64 -D > "$certificate"
91+ test -s "$certificate"
92+ security create-keychain -p "$keychain_password" "$keychain"
93+ security set-keychain-settings -lut 21600 "$keychain"
94+ security unlock-keychain -p "$keychain_password" "$keychain"
95+ security import "$certificate" \
96+ -k "$keychain" \
97+ -P "$APPLE_DEVELOPMENT_CERTIFICATE_PASSWORD" \
98+ -T /usr/bin/codesign \
99+ -T /usr/bin/security
100+ security set-key-partition-list \
101+ -S apple-tool:,apple: \
102+ -s \
103+ -k "$keychain_password" \
104+ "$keychain"
105+ security list-keychains -d user -s "$keychain"
106+ identity_output="$(security find-identity -v -p codesigning "$keychain")"
107+ printf '%s\n' "$identity_output"
108+ grep -q '"Apple Development:' <<< "$identity_output" || {
109+ echo "Imported .p12 does not contain a usable Apple Development identity." >&2
110+ exit 1
111+ }
112+ echo "RELEASE_KEYCHAIN=$keychain" >> "$GITHUB_ENV"
72113
73114 - name : Verify tag matches app version
74115 shell : bash
88129 echo "APP_VERSION=$version" >> "$GITHUB_ENV"
89130 echo "APP_BUILD=$build" >> "$GITHUB_ENV"
90131
91- - name : Build Apple-unsigned application
132+ - name : Build Apple Development-signed application
92133 shell : bash
93134 run : |
94135 set -euo pipefail
@@ -106,24 +147,29 @@ jobs:
106147 -skipMacroValidation \
107148 -skipPackagePluginValidation \
108149 -onlyUsePackageVersionsFromResolvedFile \
109- CODE_SIGNING_ALLOWED=NO \
110- CODE_SIGNING_REQUIRED=NO \
111- CODE_SIGN_INJECT_BASE_ENTITLEMENTS=NO \
112- ENABLE_HARDENED_RUNTIME=NO \
150+ CODE_SIGN_STYLE=Manual \
151+ CODE_SIGN_IDENTITY='Apple Development' \
152+ DEVELOPMENT_TEAM="$APPLE_TEAM_ID" \
153+ OTHER_CODE_SIGN_FLAGS="--keychain $RELEASE_KEYCHAIN" \
113154 SPARKLE_PUBLIC_ED_KEY="$SPARKLE_PUBLIC_ED_KEY"
114155
115156 app="$PWD/build/DerivedData/Build/Products/Release/Elapse Recorder.app"
116157 [[ -d "$app" ]] || {
117- echo "Unsigned app was not produced at $app" >&2
158+ echo "Signed app was not produced at $app" >&2
118159 exit 1
119160 }
120- # Mach-O binaries may receive an automatic linker ad-hoc signature even when Xcode
121- # code signing is disabled. Remove it so Sparkle uses EdDSA as the sole trust anchor.
122- codesign --remove-signature "$app "
123- if codesign -dv "$app" >/dev/null 2>&1; then
124- echo "Expected an Apple-unsigned host app, but a code signature is present ." >&2
161+ codesign --verify --deep --strict --verbose=2 "$app"
162+ signature_info="$(codesign -dv --verbose=4 "$app" 2>&1)"
163+ printf '%s\n' "$signature_info "
164+ grep -q '^Authority=Apple Development:' <<< "$signature_info" || {
165+ echo "Built app is not signed with an Apple Development identity ." >&2
125166 exit 1
126- fi
167+ }
168+ embedded_team="$(awk -F= '/^TeamIdentifier=/ { print $2 }' <<< "$signature_info")"
169+ [[ "$embedded_team" == "$APPLE_TEAM_ID" ]] || {
170+ echo "Built app team $embedded_team does not match APPLE_TEAM_ID $APPLE_TEAM_ID." >&2
171+ exit 1
172+ }
127173 embedded_key="$(/usr/libexec/PlistBuddy -c 'Print SUPublicEDKey' "$app/Contents/Info.plist")"
128174 [[ "$embedded_key" == "$SPARKLE_PUBLIC_ED_KEY" ]] || {
129175 echo "Built app does not contain the configured Sparkle public key." >&2
@@ -135,15 +181,15 @@ jobs:
135181 echo "Built app version $embedded_version ($embedded_build) does not match $APP_VERSION ($APP_BUILD)." >&2
136182 exit 1
137183 }
138- echo "UNSIGNED_APP =$app" >> "$GITHUB_ENV"
184+ echo "SIGNED_APP =$app" >> "$GITHUB_ENV"
139185
140186 - name : Package Sparkle update
141187 shell : bash
142188 run : |
143189 set -euo pipefail
144190 mkdir -p build/updates
145191 archive="build/updates/ElapseRecorder-${APP_VERSION}.zip"
146- ditto -c -k --sequesterRsrc --keepParent "$UNSIGNED_APP " "$archive"
192+ ditto -c -k --sequesterRsrc --keepParent "$SIGNED_APP " "$archive"
147193 echo "UPDATE_ARCHIVE=$archive" >> "$GITHUB_ENV"
148194
149195 - name : Create draft GitHub release
@@ -202,3 +248,11 @@ jobs:
202248 set -euo pipefail
203249 gh release upload "$GITHUB_REF_NAME" build/updates/appcast.xml --clobber
204250 gh release edit "$GITHUB_REF_NAME" --draft=false
251+
252+ - name : Remove release keychain
253+ if : always()
254+ shell : bash
255+ run : |
256+ if [[ -n "${RELEASE_KEYCHAIN:-}" ]]; then
257+ security delete-keychain "$RELEASE_KEYCHAIN" || true
258+ fi
0 commit comments