Skip to content

Commit a528efe

Browse files
committed
macos: add packaging, install, CI wiring, and strict signed local docs
1 parent 3618c2b commit a528efe

16 files changed

Lines changed: 1164 additions & 3 deletions

.github/workflows/manual-release.yml

Lines changed: 157 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,7 @@ permissions:
1313
packages: write
1414

1515
env:
16+
MACOS_XCODE_VERSION: 'latest-stable'
1617
REGISTRY: ghcr.io
1718
IMAGE_NAME: ${{ github.repository }}
1819

@@ -39,9 +40,157 @@ jobs:
3940
exit 1
4041
fi
4142
42-
release:
43+
macos-release-sign-notarize:
4344
needs: validate-tag
4445
if: needs.validate-tag.outputs.tag-exists == 'true'
46+
runs-on: macos-latest
47+
outputs:
48+
release-ready: ${{ steps.apple-secrets.outputs.ready }}
49+
steps:
50+
- name: Checkout code
51+
uses: actions/checkout@v4
52+
with:
53+
ref: ${{ github.event.inputs.tag_version }}
54+
fetch-depth: 0
55+
56+
- name: Check Apple release secrets
57+
id: apple-secrets
58+
shell: bash
59+
env:
60+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
61+
APPLE_DEV_ID_APPLICATION_CERT_BASE64: ${{ secrets.APPLE_DEV_ID_APPLICATION_CERT_BASE64 }}
62+
APPLE_DEV_ID_APPLICATION_CERT_PASSWORD: ${{ secrets.APPLE_DEV_ID_APPLICATION_CERT_PASSWORD }}
63+
APPLE_DEV_ID_INSTALLER_CERT_BASE64: ${{ secrets.APPLE_DEV_ID_INSTALLER_CERT_BASE64 }}
64+
APPLE_DEV_ID_INSTALLER_CERT_PASSWORD: ${{ secrets.APPLE_DEV_ID_INSTALLER_CERT_PASSWORD }}
65+
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
66+
APPLE_NOTARYTOOL_KEY_ID: ${{ secrets.APPLE_NOTARYTOOL_KEY_ID }}
67+
APPLE_NOTARYTOOL_ISSUER_ID: ${{ secrets.APPLE_NOTARYTOOL_ISSUER_ID }}
68+
APPLE_NOTARYTOOL_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARYTOOL_KEY_P8_BASE64 }}
69+
run: |
70+
missing=()
71+
for var in \
72+
APPLE_TEAM_ID \
73+
APPLE_DEV_ID_APPLICATION_CERT_BASE64 \
74+
APPLE_DEV_ID_APPLICATION_CERT_PASSWORD \
75+
APPLE_DEV_ID_INSTALLER_CERT_BASE64 \
76+
APPLE_DEV_ID_INSTALLER_CERT_PASSWORD \
77+
APPLE_KEYCHAIN_PASSWORD \
78+
APPLE_NOTARYTOOL_KEY_ID \
79+
APPLE_NOTARYTOOL_ISSUER_ID \
80+
APPLE_NOTARYTOOL_KEY_P8_BASE64
81+
do
82+
if [ -z "${!var}" ]; then
83+
missing+=("$var")
84+
fi
85+
done
86+
87+
if [ "${#missing[@]}" -gt 0 ]; then
88+
echo "ready=false" >> "$GITHUB_OUTPUT"
89+
{
90+
echo "### macOS release packaging skipped"
91+
echo
92+
echo "Missing Apple release secrets:"
93+
for item in "${missing[@]}"; do
94+
echo "- $item"
95+
done
96+
} >> "$GITHUB_STEP_SUMMARY"
97+
exit 0
98+
fi
99+
100+
echo "ready=true" >> "$GITHUB_OUTPUT"
101+
echo "### macOS release packaging enabled" >> "$GITHUB_STEP_SUMMARY"
102+
103+
- name: Select Xcode
104+
if: steps.apple-secrets.outputs.ready == 'true'
105+
uses: maxim-lobanov/setup-xcode@v1
106+
with:
107+
xcode-version: ${{ env.MACOS_XCODE_VERSION }}
108+
109+
- name: Capture Xcode version
110+
if: steps.apple-secrets.outputs.ready == 'true'
111+
id: xcode
112+
run: |
113+
VERSION=$(xcodebuild -version | tr '\n' ' ' | sed 's/ */ /g')
114+
echo "version=$VERSION" >> $GITHUB_OUTPUT
115+
xcodebuild -version
116+
117+
- name: Install XcodeGen
118+
if: steps.apple-secrets.outputs.ready == 'true'
119+
run: |
120+
brew list xcodegen >/dev/null 2>&1 || brew install xcodegen
121+
xcodegen version
122+
123+
- name: Cache macOS DerivedData
124+
if: steps.apple-secrets.outputs.ready == 'true'
125+
uses: actions/cache@v4
126+
with:
127+
path: .build/macos/DerivedData
128+
key: macos-release-deriveddata-${{ runner.os }}-${{ steps.xcode.outputs.version }}-${{ hashFiles('platform/macos/PrivilegeServices/project.yml', 'platform/macos/PrivilegeServices/**/*.swift', 'platform/macos/PrivilegeServices/**/*.plist', 'platform/macos/PrivilegeServices/**/*.entitlements', 'platform/macos/PrivilegeServices/**/*.template', 'scripts/*macos-privilege-services*.sh', 'Makefile') }}
129+
restore-keys: |
130+
macos-release-deriveddata-${{ runner.os }}-${{ steps.xcode.outputs.version }}-
131+
132+
- name: Import Developer ID certificates
133+
if: steps.apple-secrets.outputs.ready == 'true'
134+
shell: bash
135+
env:
136+
APPLE_KEYCHAIN_PASSWORD: ${{ secrets.APPLE_KEYCHAIN_PASSWORD }}
137+
APPLE_DEV_ID_APPLICATION_CERT_BASE64: ${{ secrets.APPLE_DEV_ID_APPLICATION_CERT_BASE64 }}
138+
APPLE_DEV_ID_APPLICATION_CERT_PASSWORD: ${{ secrets.APPLE_DEV_ID_APPLICATION_CERT_PASSWORD }}
139+
APPLE_DEV_ID_INSTALLER_CERT_BASE64: ${{ secrets.APPLE_DEV_ID_INSTALLER_CERT_BASE64 }}
140+
APPLE_DEV_ID_INSTALLER_CERT_PASSWORD: ${{ secrets.APPLE_DEV_ID_INSTALLER_CERT_PASSWORD }}
141+
run: |
142+
KEYCHAIN_PATH="$RUNNER_TEMP/thand-build.keychain-db"
143+
APP_CERT_PATH="$RUNNER_TEMP/dev-id-application.p12"
144+
INSTALLER_CERT_PATH="$RUNNER_TEMP/dev-id-installer.p12"
145+
146+
echo "$APPLE_DEV_ID_APPLICATION_CERT_BASE64" | base64 --decode > "$APP_CERT_PATH"
147+
echo "$APPLE_DEV_ID_INSTALLER_CERT_BASE64" | base64 --decode > "$INSTALLER_CERT_PATH"
148+
149+
security create-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
150+
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
151+
security unlock-keychain -p "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
152+
security import "$APP_CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_DEV_ID_APPLICATION_CERT_PASSWORD" -T /usr/bin/codesign -T /usr/bin/security
153+
security import "$INSTALLER_CERT_PATH" -k "$KEYCHAIN_PATH" -P "$APPLE_DEV_ID_INSTALLER_CERT_PASSWORD" -T /usr/bin/productsign -T /usr/bin/security
154+
security list-keychains -d user -s "$KEYCHAIN_PATH" login.keychain-db
155+
security default-keychain -d user -s "$KEYCHAIN_PATH"
156+
security set-key-partition-list -S apple-tool:,apple: -s -k "$APPLE_KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
157+
158+
- name: Configure notarytool profile
159+
if: steps.apple-secrets.outputs.ready == 'true'
160+
shell: bash
161+
env:
162+
APPLE_NOTARYTOOL_KEY_ID: ${{ secrets.APPLE_NOTARYTOOL_KEY_ID }}
163+
APPLE_NOTARYTOOL_ISSUER_ID: ${{ secrets.APPLE_NOTARYTOOL_ISSUER_ID }}
164+
APPLE_NOTARYTOOL_KEY_P8_BASE64: ${{ secrets.APPLE_NOTARYTOOL_KEY_P8_BASE64 }}
165+
run: |
166+
KEY_PATH="$RUNNER_TEMP/AuthKey_${APPLE_NOTARYTOOL_KEY_ID}.p8"
167+
echo "$APPLE_NOTARYTOOL_KEY_P8_BASE64" | base64 --decode > "$KEY_PATH"
168+
xcrun notarytool store-credentials thand-ci-notary \
169+
--key "$KEY_PATH" \
170+
--key-id "$APPLE_NOTARYTOOL_KEY_ID" \
171+
--issuer "$APPLE_NOTARYTOOL_ISSUER_ID"
172+
echo "NOTARYTOOL_PROFILE=thand-ci-notary" >> "$GITHUB_ENV"
173+
174+
- name: Package signed macOS installer
175+
if: steps.apple-secrets.outputs.ready == 'true'
176+
env:
177+
APPLE_TEAM_ID: ${{ secrets.APPLE_TEAM_ID }}
178+
run: |
179+
PACKAGE_VERSION="${{ github.event.inputs.tag_version }}"
180+
PACKAGE_VERSION="${PACKAGE_VERSION#v}"
181+
export PACKAGE_VERSION
182+
./scripts/package-macos-privilege-services-release.sh
183+
184+
- name: Upload macOS installer artifact
185+
if: steps.apple-secrets.outputs.ready == 'true'
186+
uses: actions/upload-artifact@v4
187+
with:
188+
name: thand-macos-privilege-services-pkg
189+
path: .build/macos/PrivilegeServices/release/ThandPrivilegeServices.pkg
190+
191+
release:
192+
needs: [validate-tag, macos-release-sign-notarize]
193+
if: needs.validate-tag.outputs.tag-exists == 'true'
45194
runs-on: ubuntu-latest
46195
steps:
47196
- name: Checkout code
@@ -94,6 +243,13 @@ jobs:
94243
repository: ${{ github.repository }}
95244
run-id: ${{ steps.find-run.outputs.run-id }}
96245

246+
- name: Download macOS installer artifact
247+
if: needs.macos-release-sign-notarize.outputs.release-ready == 'true'
248+
uses: actions/download-artifact@v4
249+
with:
250+
name: thand-macos-privilege-services-pkg
251+
path: dist/
252+
97253
- name: Generate changelog
98254
id: changelog
99255
run: |

.github/workflows/test-and-build.yml

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,7 @@ permissions:
2626
env:
2727
GO_VERSION: '1.26'
2828
GOEXPERIMENT: 'jsonv2'
29+
MACOS_XCODE_VERSION: 'latest-stable'
2930
REGISTRY: ghcr.io
3031
IMAGE_NAME: ${{ github.repository }}
3132

@@ -142,6 +143,62 @@ jobs:
142143
path: bin/thand-linux-amd64
143144
retention-days: 1
144145

146+
macos-validation:
147+
runs-on: macos-latest
148+
if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main')
149+
150+
steps:
151+
- name: Checkout code
152+
uses: actions/checkout@v4
153+
with:
154+
fetch-depth: 1
155+
submodules: recursive
156+
157+
- name: Select Xcode
158+
uses: maxim-lobanov/setup-xcode@v1
159+
with:
160+
xcode-version: ${{ env.MACOS_XCODE_VERSION }}
161+
162+
- name: Capture Xcode version
163+
id: xcode
164+
run: |
165+
VERSION=$(xcodebuild -version | tr '\n' ' ' | sed 's/ */ /g')
166+
echo "version=$VERSION" >> $GITHUB_OUTPUT
167+
xcodebuild -version
168+
169+
- name: Set up Go
170+
uses: actions/setup-go@v5
171+
with:
172+
go-version: ${{ env.GO_VERSION }}
173+
cache: true
174+
175+
- name: Install XcodeGen
176+
run: |
177+
brew list xcodegen >/dev/null 2>&1 || brew install xcodegen
178+
xcodegen version
179+
180+
- name: Install buf
181+
run: |
182+
brew list buf >/dev/null 2>&1 || brew install buf
183+
buf --version
184+
185+
- name: Cache macOS DerivedData
186+
uses: actions/cache@v4
187+
with:
188+
path: .build/macos/DerivedData
189+
key: macos-deriveddata-${{ runner.os }}-${{ steps.xcode.outputs.version }}-${{ hashFiles('platform/macos/PrivilegeServices/project.yml', 'platform/macos/PrivilegeServices/**/*.swift', 'platform/macos/PrivilegeServices/**/*.plist', 'platform/macos/PrivilegeServices/**/*.entitlements', 'platform/macos/PrivilegeServices/**/*.template', 'scripts/*macos-privilege-services*.sh', 'Makefile') }}
190+
restore-keys: |
191+
macos-deriveddata-${{ runner.os }}-${{ steps.xcode.outputs.version }}-
192+
193+
- name: Build
194+
run: make build
195+
196+
- name: Test
197+
run: make test
198+
199+
- name: Verify unsigned package layout
200+
run: THAND_MACOS_SKIP_SIGNING=1 make package-macos-privilege-services-dev
201+
145202
integration-frontend:
146203
runs-on: ubuntu-latest
147204
if: github.event_name == 'pull_request' || (github.event_name == 'push' && github.ref == 'refs/heads/main')

0 commit comments

Comments
 (0)