Skip to content

Commit 47d1e6d

Browse files
committed
Prepare signed release workflow
1 parent f1c8f3f commit 47d1e6d

6 files changed

Lines changed: 133 additions & 13 deletions

File tree

.github/workflows/build.yml

Lines changed: 65 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,10 @@ permissions:
1515
jobs:
1616
build:
1717
runs-on: macos-latest
18+
env:
19+
APP_NAME: ContextEditor.app
20+
APP_PATH: build-universal/output/ContextEditor.app
21+
ZIP_PATH: ContextEditor-macOS.zip
1822

1923
steps:
2024
- name: Checkout
@@ -26,20 +30,78 @@ jobs:
2630
- name: Generate Xcode project
2731
run: xcodegen generate
2832

33+
- name: Import signing certificate
34+
if: startsWith(github.ref, 'refs/tags/v')
35+
env:
36+
BUILD_CERTIFICATE_BASE64: ${{ secrets.BUILD_CERTIFICATE_BASE64 }}
37+
P12_PASSWORD: ${{ secrets.P12_PASSWORD }}
38+
KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }}
39+
run: |
40+
CERT_PATH="$RUNNER_TEMP/build_certificate.p12"
41+
KEYCHAIN_PATH="$RUNNER_TEMP/contexteditor-build.keychain-db"
42+
43+
echo "$BUILD_CERTIFICATE_BASE64" | base64 -D > "$CERT_PATH"
44+
45+
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
46+
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
47+
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
48+
security import "$CERT_PATH" -P "$P12_PASSWORD" -A -t cert -f pkcs12 -k "$KEYCHAIN_PATH"
49+
security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db
50+
security set-key-partition-list -S apple-tool:,apple: -s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
51+
2952
- name: Build app
53+
env:
54+
SIGNING_IDENTITY: ${{ startsWith(github.ref, 'refs/tags/v') && 'Developer ID Application' || '' }}
3055
run: ./scripts/build_universal.sh
3156

57+
- name: Verify signature
58+
if: startsWith(github.ref, 'refs/tags/v')
59+
run: codesign -dv --verbose=4 "${APP_PATH}"
60+
61+
- name: Store notarization credentials
62+
if: startsWith(github.ref, 'refs/tags/v')
63+
env:
64+
APPLE_ID: ${{ secrets.APPLE_ID }}
65+
APPLE_APP_SPECIFIC_PASSWORD: ${{ secrets.APPLE_APP_SPECIFIC_PASSWORD }}
66+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
67+
KEYCHAIN_PATH: ${{ runner.temp }}/contexteditor-build.keychain-db
68+
run: |
69+
xcrun notarytool store-credentials "ContextEditorNotary" \
70+
--apple-id "$APPLE_ID" \
71+
--password "$APPLE_APP_SPECIFIC_PASSWORD" \
72+
--team-id "$APPLE_TEAM_ID" \
73+
--keychain "$KEYCHAIN_PATH"
74+
75+
- name: Notarize app
76+
if: startsWith(github.ref, 'refs/tags/v')
77+
env:
78+
KEYCHAIN_PATH: ${{ runner.temp }}/contexteditor-build.keychain-db
79+
run: |
80+
ditto -c -k --sequesterRsrc --keepParent "${APP_PATH}" "${ZIP_PATH}"
81+
xcrun notarytool submit "${ZIP_PATH}" \
82+
--keychain-profile "ContextEditorNotary" \
83+
--keychain "$KEYCHAIN_PATH" \
84+
--wait
85+
xcrun stapler staple "${APP_PATH}"
86+
xcrun stapler validate "${APP_PATH}"
87+
spctl -a -vv "${APP_PATH}"
88+
3289
- name: Package app
33-
run: ditto -c -k --sequesterRsrc --keepParent build-universal/output/ContextEditor.app ContextEditor-macOS.zip
90+
if: ${{ !startsWith(github.ref, 'refs/tags/v') }}
91+
run: ditto -c -k --sequesterRsrc --keepParent "${APP_PATH}" "${ZIP_PATH}"
92+
93+
- name: Package notarized app
94+
if: startsWith(github.ref, 'refs/tags/v')
95+
run: ditto -c -k --sequesterRsrc --keepParent "${APP_PATH}" "${ZIP_PATH}"
3496

3597
- name: Upload artifact
3698
uses: actions/upload-artifact@v4
3799
with:
38100
name: ContextEditor-macOS
39-
path: ContextEditor-macOS.zip
101+
path: ${{ env.ZIP_PATH }}
40102

41103
- name: Publish release asset
42104
if: startsWith(github.ref, 'refs/tags/v')
43105
uses: softprops/action-gh-release@v2
44106
with:
45-
files: ContextEditor-macOS.zip
107+
files: ${{ env.ZIP_PATH }}

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,3 +10,4 @@ DerivedData/
1010
*.xcodeproj/xcuserdata/
1111
*.xcscmblueprint
1212
.contexteditor
13+
build-signed

ContextEditor.xcodeproj/project.pbxproj

Lines changed: 8 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -119,9 +119,6 @@
119119
BuildIndependentTargetsInParallel = YES;
120120
LastUpgradeCheck = 1430;
121121
TargetAttributes = {
122-
2AC92662DE49FD4E1E56E454 = {
123-
ProvisioningStyle = Automatic;
124-
};
125122
};
126123
};
127124
buildConfigurationList = 4FB13F630C155A66BF2DD8B8 /* Build configuration list for PBXProject "ContextEditor" */;
@@ -292,15 +289,15 @@
292289
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
293290
CODE_SIGN_STYLE = Automatic;
294291
COMBINE_HIDPI_IMAGES = YES;
295-
CURRENT_PROJECT_VERSION = 1;
292+
CURRENT_PROJECT_VERSION = 2;
296293
ENABLE_HARDENED_RUNTIME = YES;
297294
INFOPLIST_FILE = ContextEditor/Info.plist;
298295
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
299296
LD_RUNPATH_SEARCH_PATHS = (
300297
"$(inherited)",
301298
"@executable_path/../Frameworks",
302299
);
303-
MARKETING_VERSION = 1.0;
300+
MARKETING_VERSION = 0.1.1;
304301
PRODUCT_BUNDLE_IDENTIFIER = com.driade.contexteditor;
305302
SDKROOT = macosx;
306303
SUPPORTS_MACCATALYST = NO;
@@ -311,17 +308,20 @@
311308
isa = XCBuildConfiguration;
312309
buildSettings = {
313310
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
314-
CODE_SIGN_STYLE = Automatic;
311+
CODE_SIGN_IDENTITY = "Developer ID Application";
312+
CODE_SIGN_INJECT_BASE_ENTITLEMENTS = NO;
313+
CODE_SIGN_STYLE = Manual;
315314
COMBINE_HIDPI_IMAGES = YES;
316-
CURRENT_PROJECT_VERSION = 1;
315+
CURRENT_PROJECT_VERSION = 2;
316+
DEVELOPMENT_TEAM = KYNN4JTWJN;
317317
ENABLE_HARDENED_RUNTIME = YES;
318318
INFOPLIST_FILE = ContextEditor/Info.plist;
319319
INFOPLIST_KEY_LSApplicationCategoryType = "public.app-category.developer-tools";
320320
LD_RUNPATH_SEARCH_PATHS = (
321321
"$(inherited)",
322322
"@executable_path/../Frameworks",
323323
);
324-
MARKETING_VERSION = 1.0;
324+
MARKETING_VERSION = 0.1.1;
325325
PRODUCT_BUNDLE_IDENTIFIER = com.driade.contexteditor;
326326
SDKROOT = macosx;
327327
SUPPORTS_MACCATALYST = NO;

project.yml

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,19 @@ targets:
1919
PRODUCT_BUNDLE_IDENTIFIER: com.driade.contexteditor
2020
INFOPLIST_FILE: ContextEditor/Info.plist
2121
ASSETCATALOG_COMPILER_APPICON_NAME: AppIcon
22-
CODE_SIGN_STYLE: Automatic
2322
CURRENT_PROJECT_VERSION: 2
2423
MARKETING_VERSION: 0.1.1
2524
ENABLE_HARDENED_RUNTIME: YES
2625
INFOPLIST_KEY_LSApplicationCategoryType: public.app-category.developer-tools
2726
SUPPORTS_MACCATALYST: NO
27+
configs:
28+
Debug:
29+
CODE_SIGN_STYLE: Automatic
30+
Release:
31+
CODE_SIGN_STYLE: Manual
32+
CODE_SIGN_IDENTITY: Developer ID Application
33+
CODE_SIGN_INJECT_BASE_ENTITLEMENTS: NO
34+
DEVELOPMENT_TEAM: KYNN4JTWJN
2835
ContextEditorTests:
2936
type: bundle.unit-test
3037
platform: macOS

scripts/build_universal.sh

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,12 @@ X86_PRODUCTS="$BUILD_DIR/products-x86_64"
1313
ARM64_APP="$ARM64_PRODUCTS/$APP_NAME"
1414
X86_APP="$X86_PRODUCTS/$APP_NAME"
1515
UNIVERSAL_APP="$OUTPUT_DIR/$APP_NAME"
16+
SIGNING_IDENTITY="${SIGNING_IDENTITY:-}"
17+
XCODEBUILD_SIGNING_ARGS=()
18+
19+
if [[ -z "$SIGNING_IDENTITY" ]]; then
20+
XCODEBUILD_SIGNING_ARGS=(CODE_SIGNING_ALLOWED=NO)
21+
fi
1622

1723
rm -rf "$OUTPUT_DIR"
1824
mkdir -p "$OUTPUT_DIR"
@@ -24,6 +30,7 @@ xcodebuild \
2430
-derivedDataPath "$ARM64_DERIVED" \
2531
-destination "platform=macOS,arch=arm64" \
2632
CONFIGURATION_BUILD_DIR="$ARM64_PRODUCTS" \
33+
"${XCODEBUILD_SIGNING_ARGS[@]}" \
2734
build
2835

2936
xcodebuild \
@@ -33,6 +40,7 @@ xcodebuild \
3340
-derivedDataPath "$X86_DERIVED" \
3441
-destination "platform=macOS,arch=x86_64" \
3542
CONFIGURATION_BUILD_DIR="$X86_PRODUCTS" \
43+
"${XCODEBUILD_SIGNING_ARGS[@]}" \
3644
build
3745

3846
ditto "$ARM64_APP" "$UNIVERSAL_APP"
@@ -42,6 +50,10 @@ lipo -create \
4250
"$X86_APP/Contents/MacOS/ContextEditor" \
4351
-output "$UNIVERSAL_APP/Contents/MacOS/ContextEditor"
4452

45-
codesign --force --sign - --deep --timestamp=none "$UNIVERSAL_APP"
53+
if [[ -n "$SIGNING_IDENTITY" ]]; then
54+
codesign --force --deep --options runtime --timestamp --sign "$SIGNING_IDENTITY" "$UNIVERSAL_APP"
55+
else
56+
codesign --force --sign - --deep --timestamp=none "$UNIVERSAL_APP"
57+
fi
4658

4759
echo "Universal app created at: $UNIVERSAL_APP"

scripts/notarize_local.sh

Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
#!/bin/zsh
2+
3+
set -euo pipefail
4+
5+
if [[ $# -lt 2 ]]; then
6+
echo "Usage: $0 <app-path> <keychain-profile> [output-zip-path]" >&2
7+
exit 1
8+
fi
9+
10+
APP_PATH="$1"
11+
PROFILE_NAME="$2"
12+
ZIP_PATH="${3:-${APP_PATH:r}.zip}"
13+
14+
if [[ ! -d "$APP_PATH" ]]; then
15+
echo "App not found: $APP_PATH" >&2
16+
exit 1
17+
fi
18+
19+
rm -f "$ZIP_PATH"
20+
21+
echo "Packaging app for notarization..."
22+
ditto -c -k --sequesterRsrc --keepParent "$APP_PATH" "$ZIP_PATH"
23+
24+
echo "Submitting archive to Apple notarization service..."
25+
xcrun notarytool submit "$ZIP_PATH" \
26+
--keychain-profile "$PROFILE_NAME" \
27+
--wait
28+
29+
echo "Stapling notarization ticket..."
30+
xcrun stapler staple "$APP_PATH"
31+
32+
echo "Validating stapled ticket..."
33+
xcrun stapler validate "$APP_PATH"
34+
35+
echo "Gatekeeper assessment..."
36+
spctl -a -vv "$APP_PATH"
37+
38+
echo "Notarization complete for: $APP_PATH"

0 commit comments

Comments
 (0)