Repository navigation
229 lines (212 loc) · 8.8 KB
/
Copy pathMistDemo-Integration.yml
File metadata and controls
229 lines (212 loc) · 8.8 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
# MistDemo Integration Runs
#
# Live end-to-end runs against the real CloudKit container
# (iCloud.com.brightdigit.MistDemo). Triggered on push to main and
# release branches, and via manual workflow_dispatch (used to re-run
# after rotating CLOUDKIT_WEB_AUTH_TOKEN / CLOUDKIT_SHAREE_WEB_AUTH_TOKEN).
#
# Required repo secrets:
# CLOUDKIT_API_TOKEN - Web Services API token
# CLOUDKIT_WEB_AUTH_TOKEN - Sharer web-auth token (rotate when stale)
# CLOUDKIT_SHAREE_WEB_AUTH_TOKEN - Sharee web-auth token (different Apple ID)
# CLOUDKIT_SHAREE_EMAIL - Sharee iCloud email (invite lookup)
# CLOUDKIT_KEY_ID - Server-to-server key ID
# CLOUDKIT_PRIVATE_KEY_BASE64 - Server-to-server PEM, base64-encoded
# (base64 -w0 key.pem)
# CLOUDKIT_CONTAINER_ID - iCloud.com.brightdigit.MistDemo
#
# Capture both web-auth tokens with `mistdemo auth-tokens --sharee-email …`
# after rotating, then re-run via workflow_dispatch.
#
# Two-job design:
# 1) `build` runs in `swiftlang/swift:nightly-6.4.x-noble` (MistDemo
# declares swift-tools-version: 6.4; no 6.4 RELEASE image yet),
# installs the matching 6.4.x-branch Static Linux SDK (musl), and
# produces a self-contained statically linked mistdemo binary.
# Bump container + SDK URL/checksum together; switch to a 6.4
# RELEASE pin when Apple ships one.
# 2) `integration` runs on plain `ubuntu-24.04` (no Swift toolchain,
# no LD_LIBRARY_PATH needed) and executes the live CloudKit
# phases against the static binary.
#
# Secrets are NOT exposed to fork pull requests — there is no
# pull_request trigger on this workflow by design.
name: MistDemo Integration
on:
push:
branches:
- main
- 'v*.*.*'
workflow_dispatch:
concurrency:
group: mistdemo-integration-${{ github.ref }}
cancel-in-progress: false
jobs:
build:
name: Build static mistdemo
runs-on: ubuntu-24.04
# Pin nightly container + 6.4.x-branch Static Linux SDK together.
# Snapshot URL/checksum match SyndiKit-style 6.4.x-branch SDK pins.
container: swiftlang/swift:nightly-6.4.x-noble
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
timeout-minutes: 45
defaults:
run:
shell: bash
steps:
- uses: actions/checkout@v6
- name: Install Swift Static Linux SDK
run: |
set -euo pipefail
swift sdk install \
https://download.swift.org/swift-6.4.x-branch/static-sdk/swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-09-01-a/swift-6.4.x-DEVELOPMENT-SNAPSHOT-2026-09-01-a_static-linux-0.1.0.artifactbundle.tar.gz \
--checksum 68a0c0345f715c4272eda1ef79d53e86c0b19a5600bc678ef48a641b22538f85
swift sdk list
- name: swift build --swift-sdk x86_64-swift-linux-musl -c release
working-directory: Examples/MistDemo
run: swift build -c release --swift-sdk x86_64-swift-linux-musl
- name: Verify static linkage
working-directory: Examples/MistDemo
run: |
set -euo pipefail
# Ask SwiftPM where it put the binary rather than hardcoding the
# layout - the cross-compiled output directory is toolchain
# dependent and has moved between releases.
BIN_DIR=$(swift build -c release --swift-sdk x86_64-swift-linux-musl --show-bin-path)
BIN="$BIN_DIR/mistdemo"
if [ ! -f "$BIN" ]; then
echo "::error::mistdemo not found at $BIN"
echo "Contents of $BIN_DIR:"
ls -la "$BIN_DIR" || true
echo "Executables under .build:"
find .build -type f -name mistdemo || true
exit 1
fi
ls -lh "$BIN"
# A statically linked binary either reports "not a dynamic
# executable" (musl) or "statically linked" (glibc). Anything
# else means we picked up shared deps and broke the goal.
LDD_OUTPUT=$(ldd "$BIN" 2>&1 || true)
echo "$LDD_OUTPUT"
if ! echo "$LDD_OUTPUT" | grep -qE 'not a dynamic executable|statically linked'; then
echo "::error::Binary has dynamic dependencies; static link failed"
exit 1
fi
# Stage at a fixed path so the upload step does not have to know
# the toolchain-specific build layout.
mkdir -p "$RUNNER_TEMP/artifact"
cp "$BIN" "$RUNNER_TEMP/artifact/mistdemo"
- uses: actions/upload-artifact@v4
with:
name: mistdemo
path: ${{ runner.temp }}/artifact/mistdemo
retention-days: 1
# Single executable; default zip compression is fine.
integration:
name: Live CloudKit Integration
needs: build
runs-on: ubuntu-24.04
if: ${{ !contains(github.event.head_commit.message, 'ci skip') }}
timeout-minutes: 30
defaults:
run:
shell: bash
steps:
- uses: actions/download-artifact@v4
with:
name: mistdemo
- name: Prep binary
run: |
set -euo pipefail
chmod +x ./mistdemo
mkdir -p integration-logs
# Smoke check the binary before touching the network.
./mistdemo --help >/dev/null
- name: Decode server-to-server private key
env:
CLOUDKIT_PRIVATE_KEY_BASE64: ${{ secrets.CLOUDKIT_PRIVATE_KEY_BASE64 }}
run: |
set -euo pipefail
KEY_PATH="$RUNNER_TEMP/cloudkit_s2s.pem"
printf '%s' "$CLOUDKIT_PRIVATE_KEY_BASE64" | base64 -d > "$KEY_PATH"
chmod 600 "$KEY_PATH"
# Defensive: mask the decoded PEM line-by-line so it can never
# land in step logs.
while IFS= read -r line; do
[ -n "$line" ] && echo "::add-mask::$line"
done < "$KEY_PATH"
echo "CLOUDKIT_PRIVATE_KEY_PATH=$KEY_PATH" >> "$GITHUB_ENV"
- name: test-public
timeout-minutes: 10
env:
CLOUDKIT_API_TOKEN: ${{ secrets.CLOUDKIT_API_TOKEN }}
CLOUDKIT_KEY_ID: ${{ secrets.CLOUDKIT_KEY_ID }}
CLOUDKIT_CONTAINER_ID: ${{ secrets.CLOUDKIT_CONTAINER_ID }}
CLOUDKIT_ENVIRONMENT: development
run: |
set -o pipefail
./mistdemo test-public \
--record-count 5 \
--asset-size 50 \
2>&1 | tee integration-logs/test-public.log
- name: test-private
timeout-minutes: 10
env:
CLOUDKIT_API_TOKEN: ${{ secrets.CLOUDKIT_API_TOKEN }}
CLOUDKIT_WEB_AUTH_TOKEN: ${{ secrets.CLOUDKIT_WEB_AUTH_TOKEN }}
CLOUDKIT_SHAREE_WEB_AUTH_TOKEN: ${{ secrets.CLOUDKIT_SHAREE_WEB_AUTH_TOKEN }}
CLOUDKIT_SHAREE_EMAIL: ${{ secrets.CLOUDKIT_SHAREE_EMAIL }}
CLOUDKIT_CONTAINER_ID: ${{ secrets.CLOUDKIT_CONTAINER_ID }}
CLOUDKIT_ENVIRONMENT: development
run: |
set -o pipefail
./mistdemo test-private \
--record-count 5 \
--asset-size 50 \
2>&1 | tee integration-logs/test-private.log
- name: demo-errors
timeout-minutes: 10
env:
CLOUDKIT_API_TOKEN: ${{ secrets.CLOUDKIT_API_TOKEN }}
CLOUDKIT_WEB_AUTH_TOKEN: ${{ secrets.CLOUDKIT_WEB_AUTH_TOKEN }}
CLOUDKIT_KEY_ID: ${{ secrets.CLOUDKIT_KEY_ID }}
CLOUDKIT_CONTAINER_ID: ${{ secrets.CLOUDKIT_CONTAINER_ID }}
CLOUDKIT_ENVIRONMENT: development
run: |
set -o pipefail
./mistdemo demo-errors --scenario all \
2>&1 | tee integration-logs/demo-errors.log
# demo-errors exits 0 by design — assert all scenarios actually
# ran by checking for the per-scenario completion markers.
for marker in "401" "404" "409"; do
grep -q "$marker" integration-logs/demo-errors.log || {
echo "::error::demo-errors did not exercise scenario $marker"
exit 1
}
done
- name: demo-in-filter
timeout-minutes: 10
env:
CLOUDKIT_API_TOKEN: ${{ secrets.CLOUDKIT_API_TOKEN }}
CLOUDKIT_WEB_AUTH_TOKEN: ${{ secrets.CLOUDKIT_WEB_AUTH_TOKEN }}
CLOUDKIT_KEY_ID: ${{ secrets.CLOUDKIT_KEY_ID }}
CLOUDKIT_CONTAINER_ID: ${{ secrets.CLOUDKIT_CONTAINER_ID }}
CLOUDKIT_ENVIRONMENT: development
run: |
set -o pipefail
./mistdemo demo-in-filter \
2>&1 | tee integration-logs/demo-in-filter.log
- name: Upload integration logs
if: always()
uses: actions/upload-artifact@v4
with:
name: integration-logs
path: integration-logs/
retention-days: 14
if-no-files-found: warn
- name: Cleanup decoded private key
if: always()
run: |
if [ -n "${CLOUDKIT_PRIVATE_KEY_PATH:-}" ] && [ -f "$CLOUDKIT_PRIVATE_KEY_PATH" ]; then
rm -f "$CLOUDKIT_PRIVATE_KEY_PATH"
fi