Skip to content

Commit 3c0554e

Browse files
authored
Merge pull request #31 from github/vdye/sign-macos-artifacts
Add MacOS signing & notarization to release workflow
2 parents 6c259ad + 051d636 commit 3c0554e

File tree

5 files changed

+223
-3
lines changed

5 files changed

+223
-3
lines changed

.github/workflows/release.yml

+78-1
Original file line numberDiff line numberDiff line change
@@ -31,16 +31,19 @@ jobs:
3131
goarch: amd64
3232
pool: macos-latest
3333
artifact: _dist/*.pkg
34+
environment: release
3435
- jobname: Create MacOS .pkg (ARM64)
3536
goarch: arm64
3637
pool: macos-latest
3738
artifact: _dist/*.pkg
39+
environment: release
3840
- jobname: Create binary Debian package (x86_64)
3941
goarch: amd64
4042
pool: ubuntu-latest
4143
artifact: _dist/*.deb
4244
env:
4345
GOARCH: ${{matrix.jobs.goarch}}
46+
environment: ${{matrix.jobs.environment}}
4447
runs-on: ${{matrix.jobs.pool}}
4548
steps:
4649
- name: Setup Go
@@ -53,8 +56,82 @@ jobs:
5356
- run: gem install asciidoctor
5457
- name: Clone repository
5558
uses: actions/checkout@v3
59+
- name: Configure MacOS signing
60+
if: ${{ matrix.jobs.pool == 'macos-latest' }}
61+
env:
62+
A1: ${{ secrets.APPLICATION_CERTIFICATE_BASE64 }}
63+
A2: ${{ secrets.APPLICATION_CERTIFICATE_PASSWORD }}
64+
A3: ${{ secrets.APPLE_APPLICATION_SIGNING_IDENTITY }}
65+
I1: ${{ secrets.INSTALLER_CERTIFICATE_BASE64 }}
66+
I2: ${{ secrets.INSTALLER_CERTIFICATE_PASSWORD }}
67+
I3: ${{ secrets.APPLE_INSTALLER_SIGNING_IDENTITY }}
68+
N1: ${{ secrets.APPLE_TEAM_ID }}
69+
N2: ${{ secrets.APPLE_DEVELOPER_ID }}
70+
N3: ${{ secrets.APPLE_DEVELOPER_PASSWORD }}
71+
N4: ${{ secrets.APPLE_KEYCHAIN_PROFILE }}
72+
run: |
73+
# Environment configured for signing?
74+
if [[ -n "$A1" && -n "$A2" && -n "$A3" && -n "$I1" && -n "$I2" && -n "$I3" ]]
75+
then
76+
echo "DO_SIGN=1" >> $GITHUB_ENV
77+
else
78+
echo "::warning::MacOS signing environment is not fully specified. Skipping configuration."
79+
exit 0
80+
fi
81+
82+
# Signing
83+
echo "Setting up signing certificates"
84+
security create-keychain -p pwd $RUNNER_TEMP/buildagent.keychain
85+
security default-keychain -s $RUNNER_TEMP/buildagent.keychain
86+
security unlock-keychain -p pwd $RUNNER_TEMP/buildagent.keychain
87+
88+
echo $A1 | base64 -D > $RUNNER_TEMP/cert.p12
89+
security import $RUNNER_TEMP/cert.p12 \
90+
-k $RUNNER_TEMP/buildagent.keychain \
91+
-P $A2 \
92+
-T /usr/bin/codesign
93+
security set-key-partition-list \
94+
-S apple-tool:,apple:,codesign: \
95+
-s -k pwd \
96+
$RUNNER_TEMP/buildagent.keychain
97+
98+
echo $I1 | base64 -D > $RUNNER_TEMP/cert.p12
99+
security import $RUNNER_TEMP/cert.p12 \
100+
-k $RUNNER_TEMP/buildagent.keychain \
101+
-P $I2 \
102+
-T /usr/bin/productbuild
103+
security set-key-partition-list \
104+
-S apple-tool:,apple:,productbuild: \
105+
-s -k pwd \
106+
$RUNNER_TEMP/buildagent.keychain
107+
108+
# Environment configured for notarization?
109+
if [[ -n "$N1" && -n "$N2" && -n "$N3" && -n "$N4" ]]
110+
then
111+
echo "DO_NOTARIZE=1" >> $GITHUB_ENV
112+
else
113+
echo "::warning::Successfully configured MacOS signing, but cannot set up notarization. Skipping configuration."
114+
exit 0
115+
fi
116+
117+
# Notarizing
118+
echo "Setting up notarytool"
119+
xcrun notarytool store-credentials \
120+
--team-id $N1 \
121+
--apple-id $N2 \
122+
--password $N3 \
123+
"$N4"
56124
- name: Build the release artifact
57-
run: make package VERSION=${{ needs.prereqs.outputs.tag_version }}
125+
env:
126+
A3: ${{ secrets.APPLE_APPLICATION_SIGNING_IDENTITY }}
127+
I3: ${{ secrets.APPLE_INSTALLER_SIGNING_IDENTITY }}
128+
N4: ${{ secrets.APPLE_KEYCHAIN_PROFILE }}
129+
shell: bash
130+
run: |
131+
make package VERSION=${{ needs.prereqs.outputs.tag_version }} \
132+
APPLE_APP_IDENTITY="$([[ -n "$DO_SIGN" ]] && echo "$A3" || echo '')" \
133+
APPLE_INST_IDENTITY="$([[ -n "$DO_SIGN" ]] && echo "$I3" || echo '')" \
134+
APPLE_KEYCHAIN_PROFILE="$([[ -n "$DO_NOTARIZE" ]] && echo "$N4" || echo '')"
58135
- name: Get the release artifact
59136
shell: bash
60137
run: |

Makefile

+36-1
Original file line numberDiff line numberDiff line change
@@ -22,6 +22,11 @@ GOARCH := $(shell go env GOARCH)
2222
SUPPORTED_PACKAGE_GOARCHES := amd64 arm64
2323
PACKAGE_ARCH := $(GOARCH)
2424

25+
# Guard against environment variables
26+
APPLE_APP_IDENTITY =
27+
APPLE_INST_IDENTITY =
28+
APPLE_KEYCHAIN_PROFILE =
29+
2530
# Build targets
2631
.PHONY: build
2732
build:
@@ -95,7 +100,8 @@ else ifeq ($(GOOS),darwin)
95100
# Steps:
96101
# 1. Layout files in _dist/pkg/payload/ as they'll be installed (including
97102
# uninstall.sh script).
98-
# 2. Create the product archive in _dist/.
103+
# 2. (Optional) Codesign the package contents in place.
104+
# 3. Create the product archive in _dist/.
99105

100106
# Platform-specific variables
101107
PKGDIR := $(DISTDIR)/pkg
@@ -110,13 +116,42 @@ $(PKGDIR)/payload: check-arch build doc
110116
--uninstaller="$(CURDIR)/scripts/uninstall.sh" \
111117
--install-root="$(PKGDIR)/payload"
112118

119+
ifdef APPLE_APP_IDENTITY
120+
.PHONY: codesign
121+
codesign: $(PKGDIR)/payload
122+
@echo
123+
@echo "======== Codesigning package contents ========"
124+
@build/package/pkg/codesign.sh --payload="$(PKGDIR)/payload" \
125+
--identity="$(APPLE_APP_IDENTITY)" \
126+
--entitlements="$(CURDIR)/build/package/pkg/entitlements.xml"
127+
128+
$(PKG_FILENAME): codesign
129+
endif
130+
113131
$(PKG_FILENAME): check-version $(PKGDIR)/payload
114132
@echo
115133
@echo "======== Creating product archive package ========"
116134
@build/package/pkg/pack.sh --version="$(VERSION)" \
117135
--payload="$(PKGDIR)/payload" \
136+
--identity="$(APPLE_INST_IDENTITY)" \
118137
--output="$(PKG_FILENAME)"
119138

139+
# Notarization can only happen if the package is fully signed
140+
ifdef APPLE_APP_IDENTITY
141+
ifdef APPLE_INST_IDENTITY
142+
ifdef APPLE_KEYCHAIN_PROFILE
143+
.PHONY: notarize
144+
notarize: $(PKG_FILENAME)
145+
@echo
146+
@echo "======== Notarizing package ========"
147+
@build/package/pkg/notarize.sh --package="$(PKG_FILENAME)" \
148+
--keychain-profile="$(APPLE_KEYCHAIN_PROFILE)"
149+
150+
package: notarize
151+
endif
152+
endif
153+
endif
154+
120155
.PHONY: package
121156
package: $(PKG_FILENAME)
122157

build/package/pkg/codesign.sh

+65
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,65 @@
1+
#!/bin/bash
2+
3+
sign_directory () {
4+
(
5+
cd $1
6+
for f in *
7+
do
8+
macho=$(file --mime $f | grep mach)
9+
# Runtime sign dylibs and Mach-O binaries
10+
if [[ $f == *.dylib ]] || [ ! -z "$macho" ];
11+
then
12+
echo "Runtime Signing $f"
13+
codesign -s "$IDENTITY" $f --timestamp --force --options=runtime --entitlements $ENTITLEMENTS_FILE
14+
elif [ -d "$f" ];
15+
then
16+
echo "Signing files in subdirectory $f"
17+
sign_directory $f
18+
19+
else
20+
echo "Signing $f"
21+
codesign -s "$IDENTITY" $f --timestamp --force
22+
fi
23+
done
24+
)
25+
}
26+
27+
for i in "$@"
28+
do
29+
case "$i" in
30+
--payload=*)
31+
SIGN_DIR="${i#*=}"
32+
shift # past argument=value
33+
;;
34+
--identity=*)
35+
IDENTITY="${i#*=}"
36+
shift # past argument=value
37+
;;
38+
--entitlements=*)
39+
ENTITLEMENTS_FILE="${i#*=}"
40+
shift # past argument=value
41+
;;
42+
*)
43+
die "unknown option '$i'"
44+
;;
45+
esac
46+
done
47+
48+
if [ -z "$SIGN_DIR" ]; then
49+
echo "error: missing directory argument"
50+
exit 1
51+
elif [ -z "$IDENTITY" ]; then
52+
echo "error: missing signing identity argument"
53+
exit 1
54+
elif [ -z "$ENTITLEMENTS_FILE" ]; then
55+
echo "error: missing entitlements file argument"
56+
exit 1
57+
fi
58+
59+
echo "======== INPUTS ========"
60+
echo "Directory: $SIGN_DIR"
61+
echo "Signing identity: $IDENTITY"
62+
echo "Entitlements: $ENTITLEMENTS_FILE"
63+
echo "======== END INPUTS ========"
64+
65+
sign_directory "$SIGN_DIR"

build/package/pkg/notarize.sh

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
#!/bin/bash
2+
3+
for i in "$@"
4+
do
5+
case "$i" in
6+
--package=*)
7+
PACKAGE="${i#*=}"
8+
shift # past argument=value
9+
;;
10+
--keychain-profile=*)
11+
KEYCHAIN_PROFILE="${i#*=}"
12+
shift # past argument=value
13+
;;
14+
*)
15+
die "unknown option '$i'"
16+
;;
17+
esac
18+
done
19+
20+
if [ -z "$PACKAGE" ]; then
21+
echo "error: missing package argument"
22+
exit 1
23+
elif [ -z "$KEYCHAIN_PROFILE" ]; then
24+
echo "error: missing keychain profile argument"
25+
exit 1
26+
fi
27+
28+
# Exit as soon as any line fails
29+
set -e
30+
31+
# Send the notarization request
32+
xcrun notarytool submit -v "$PACKAGE" -p "$KEYCHAIN_PROFILE" --wait
33+
34+
# Staple the notarization ticket (to allow offline installation)
35+
xcrun stapler staple -v "$PACKAGE"

build/package/pkg/pack.sh

+9-1
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,9 @@ THISDIR="$( cd "$(dirname "$0")" ; pwd -P )"
1111
IDENTIFIER="com.github.gitbundleserver"
1212
INSTALL_LOCATION="/usr/local/git-bundle-server"
1313

14+
# Defaults
15+
IDENTITY=
16+
1417
# Parse script arguments
1518
for i in "$@"
1619
do
@@ -23,6 +26,10 @@ case "$i" in
2326
PAYLOAD="${i#*=}"
2427
shift # past argument=value
2528
;;
29+
--identity=*)
30+
IDENTITY="${i#*=}"
31+
shift # past argument=value
32+
;;
2633
--output=*)
2734
PKGOUT="${i#*=}"
2835
shift # past argument=value
@@ -59,7 +66,7 @@ fi
5966
mkdir -p "$(dirname "$PKGOUT")"
6067

6168
# Build the component package
62-
PKGTMP="$PKGOUT.tmp"
69+
PKGTMP="$PKGOUT.component"
6370

6471
# Remove any unwanted .DS_Store files
6572
echo "Removing unnecessary files..."
@@ -91,6 +98,7 @@ echo "Building product package..."
9198
--package "$PKGTMP" \
9299
--identifier "$IDENTIFIER" \
93100
--version "$VERSION" \
101+
${IDENTITY:+"--sign"} ${IDENTITY:+"$IDENTITY"} \
94102
"$PKGOUT"
95103

96104
echo "Product build complete."

0 commit comments

Comments
 (0)