-
Notifications
You must be signed in to change notification settings - Fork 1
254 lines (222 loc) · 9.87 KB
/
release.yml
File metadata and controls
254 lines (222 loc) · 9.87 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
name: Release
on:
push:
tags:
- 'v*'
workflow_dispatch:
inputs:
tag:
description: 'Tag to release (e.g. v1.0.0)'
required: true
type: string
permissions:
contents: write
env:
SIGN_IDENTITY: "Developer ID Application: FOURNINE CLOUD SOLUTIONS PRIVATE LIMITED (XUKA94C5G4)"
jobs:
build:
name: Build, sign, notarize & publish
runs-on: macos-15
timeout-minutes: 60
steps:
- name: Checkout
uses: actions/checkout@v4
with:
ref: ${{ github.event.inputs.tag || github.ref }}
fetch-depth: 0
- name: Select Xcode
# Xcode 16 ships Swift 6.0+, which fixes the SwiftPM multi-arch
# bug that blocks `package` access-level symbols (swift-crypto)
# on Xcode 15.4 / Swift 5.10. Fall back to default if the pinned
# version drifts on the runner image.
run: sudo xcode-select -s /Applications/Xcode_16.2.app/Contents/Developer 2>/dev/null || sudo xcode-select -s /Applications/Xcode.app/Contents/Developer
- name: Show toolchain
run: |
xcodebuild -version
swift --version
uname -m
- name: Resolve Swift packages
run: swift package resolve
- name: Run tests
run: make test
# Imports the Developer ID Application certificate (exported as a
# .p12 from Keychain Access) into a fresh ephemeral keychain on
# the runner, makes that keychain the default so codesign/stapler
# pick up the identity automatically, and cleans up at job end.
- name: Import Developer ID certificate
env:
P12_BASE64: ${{ secrets.MACOS_CERTIFICATE_P12_BASE64 }}
P12_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }}
KEYCHAIN_PASSWORD: ${{ secrets.MACOS_KEYCHAIN_PASSWORD }}
run: |
set -euo pipefail
KEYCHAIN_PATH="$RUNNER_TEMP/build.keychain-db"
CERT_PATH="$RUNNER_TEMP/cert.p12"
# Strip CR/LF (most common paste artifact). Spaces and tabs
# could be valid password chars, so leave them.
P12_PASSWORD="$(printf '%s' "$P12_PASSWORD" | tr -d '\r\n')"
# Diagnostics that don't leak the password: length and a short
# SHA-256 prefix. Compute the same locally to compare:
# printf '%s' 'YOUR_PASSWORD' | shasum -a 256 | cut -c1-12
PWD_LEN=${#P12_PASSWORD}
PWD_FP=$(printf '%s' "$P12_PASSWORD" | shasum -a 256 | cut -c1-12)
echo "Password length: $PWD_LEN"
echo "Password sha256 prefix: $PWD_FP"
# Decode .p12 and sanity-check it isn't empty/truncated.
printf '%s' "$P12_BASE64" | base64 --decode > "$CERT_PATH"
P12_SIZE=$(wc -c < "$CERT_PATH")
echo "Decoded .p12 is $P12_SIZE bytes."
if [ "$P12_SIZE" -lt 1000 ]; then
echo "::error::Decoded .p12 looks too small — secret MACOS_CERTIFICATE_P12_BASE64 may be truncated or mis-encoded."
exit 1
fi
# Verify the password against the .p12 BEFORE attempting the
# keychain import, so a wrong password produces a clear error
# instead of the cryptic "SecKeychainItemImport" failure.
# Keychain Access exports use legacy RC2-40-CBC. Apple's
# /usr/bin/openssl on macos-15 runners is LibreSSL, which
# handles RC2 natively and doesn't accept -legacy. Homebrew /
# OpenSSL 3 needs -legacy. Try plain first, fall back to
# -legacy so this works regardless of which flavor is on PATH.
if /usr/bin/openssl pkcs12 -in "$CERT_PATH" -nokeys \
-passin "env:P12_PASSWORD" >/dev/null 2>&1; then
echo "Password accepted by openssl (LibreSSL or OpenSSL 3 with modern .p12)."
elif /usr/bin/openssl pkcs12 -in "$CERT_PATH" -nokeys -legacy \
-passin "env:P12_PASSWORD" >/dev/null 2>&1; then
echo "Password accepted by openssl (OpenSSL 3 + -legacy)."
else
echo "::error::Password rejected by openssl. The CI sha256 prefix above must match printf '%s' 'YOUR_PASSWORD' | shasum -a 256 | cut -c1-12 on your machine. If they differ, the value pasted into MACOS_CERTIFICATE_PASSWORD is not what you typed."
exit 1
fi
security create-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security set-keychain-settings -lut 21600 "$KEYCHAIN_PATH"
security unlock-keychain -p "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security import "$CERT_PATH" \
-P "$P12_PASSWORD" \
-A -t cert -f pkcs12 \
-k "$KEYCHAIN_PATH" \
-T /usr/bin/codesign -T /usr/bin/security
security set-key-partition-list \
-S apple-tool:,apple:,codesign: \
-s -k "$KEYCHAIN_PASSWORD" "$KEYCHAIN_PATH"
security list-keychains -d user -s "$KEYCHAIN_PATH" $(security list-keychains -d user | tr -d '"')
security default-keychain -s "$KEYCHAIN_PATH"
rm "$CERT_PATH"
echo "Available signing identities:"
security find-identity -v -p codesigning
- name: Build universal app + zip (signed)
run: make zip
- name: Build per-arch zips (signed)
run: make zip-all
# Submits all three zips to Apple's notary service in parallel,
# waits for each verdict, staples the bundle, and re-zips so the
# distributed archive carries the staple. Apple accepts concurrent
# submissions; total wall time ≈ slowest single submission.
- name: Notarize and staple all zips
env:
NOTARY_APPLE_ID: ${{ secrets.NOTARY_APPLE_ID }}
NOTARY_PASSWORD: ${{ secrets.NOTARY_PASSWORD }}
NOTARY_TEAM_ID: ${{ secrets.NOTARY_TEAM_ID }}
run: |
set -euo pipefail
submit_and_staple() {
local zip="$1"
local app="$2"
local label
label="$(basename "$zip")"
# Compute the absolute zip output path before cd-ing, so
# ditto writes to build/CloudTunnels-arm64.zip rather than
# build/arm64/CloudTunnels-arm64.zip.
local zip_abs="$PWD/$zip"
echo "[$label] submitting to notary..."
xcrun notarytool submit "$zip" \
--apple-id "$NOTARY_APPLE_ID" \
--password "$NOTARY_PASSWORD" \
--team-id "$NOTARY_TEAM_ID" \
--wait
echo "[$label] stapling..."
xcrun stapler staple "$app"
xcrun stapler validate "$app"
rm -f "$zip"
( cd "$(dirname "$app")" && \
ditto -c -k --sequesterRsrc --keepParent \
"$(basename "$app")" "$zip_abs" )
echo "[$label] done."
}
submit_and_staple build/CloudTunnels.zip build/CloudTunnels.app & P1=$!
submit_and_staple build/CloudTunnels-arm64.zip build/arm64/CloudTunnels.app & P2=$!
submit_and_staple build/CloudTunnels-x86_64.zip build/x86_64/CloudTunnels.app & P3=$!
wait $P1
wait $P2
wait $P3
- name: Verify Gatekeeper acceptance
run: |
for app in build/CloudTunnels.app build/arm64/CloudTunnels.app build/x86_64/CloudTunnels.app; do
echo "=== $app ==="
xcrun stapler validate "$app"
spctl -a -vvv -t install "$app" || true
done
- name: Build universal ctun CLI
run: make cli
- name: Package ctun CLI tarball
run: |
mkdir -p build/cli
cp .build/apple/Products/Release/ctun build/cli/ctun
chmod +x build/cli/ctun
cd build/cli && tar -czf ../ctun-universal.tar.gz ctun
- name: Compute checksums
run: |
cd build
shasum -a 256 \
CloudTunnels.zip \
CloudTunnels-arm64.zip \
CloudTunnels-x86_64.zip \
ctun-universal.tar.gz \
> SHA256SUMS.txt
cat SHA256SUMS.txt
- name: Clean up keychain
if: always()
run: |
security delete-keychain "$RUNNER_TEMP/build.keychain-db" || true
- name: Determine tag
id: tag
env:
INPUT_TAG: ${{ github.event.inputs.tag }}
REF_NAME: ${{ github.ref_name }}
run: |
TAG="${INPUT_TAG:-$REF_NAME}"
echo "tag=${TAG}" >> "$GITHUB_OUTPUT"
echo "Releasing $TAG"
- name: Create GitHub Release
uses: softprops/action-gh-release@v2
with:
tag_name: ${{ steps.tag.outputs.tag }}
name: ${{ steps.tag.outputs.tag }}
draft: false
prerelease: false
generate_release_notes: true
fail_on_unmatched_files: true
body: |
## CloudTunnels ${{ steps.tag.outputs.tag }}
Native macOS menu bar app for managing GCP IAP, AWS SSM, Cloud SQL Proxy, and SSH port-forwarding tunnels.
### Downloads
| Asset | Description |
|---|---|
| `CloudTunnels.zip` | Universal binary (Apple Silicon + Intel) |
| `CloudTunnels-arm64.zip` | Apple Silicon only |
| `CloudTunnels-x86_64.zip` | Intel only |
| `ctun-universal.tar.gz` | `ctun` CLI binary (universal) |
| `SHA256SUMS.txt` | Checksums for all assets |
### Install
```bash
unzip CloudTunnels.zip -d /Applications/
open /Applications/CloudTunnels.app
```
All builds are signed with Apple Developer ID and notarized by Apple — Gatekeeper accepts them on first launch with no warnings.
See the [User Guide](https://github.com/FournineCS/cloud-tunnels/blob/main/USERGUIDE.md) for full setup instructions.
files: |
build/CloudTunnels.zip
build/CloudTunnels-arm64.zip
build/CloudTunnels-x86_64.zip
build/ctun-universal.tar.gz
build/SHA256SUMS.txt