forked from BasedHardware/omi
-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy pathqualification-swift-cache.sh
More file actions
executable file
·250 lines (230 loc) · 9.15 KB
/
Copy pathqualification-swift-cache.sh
File metadata and controls
executable file
·250 lines (230 loc) · 9.15 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
#!/usr/bin/env bash
# Prepare a stable, exact-source qualification checkout outside the Actions
# checkout. SwiftPM builds directly in that checkout's .build directory, so both
# source and output absolute paths remain identical across trusted retries.
set -euo pipefail
umask 077
usage() {
cat <<'USAGE'
Usage: qualification-swift-cache.sh prepare <40-char-source-sha> <source-repository>
Prints the persistent exact-SHA source path on stdout. Diagnostics go to stderr.
Environment (test overrides are intentionally explicit):
OMI_QUALIFICATION_SWIFT_CACHE_ROOT Root (default: ~/Library/Caches/OmiDesktop/qualification-swiftpm-v2)
OMI_QUALIFICATION_SWIFT_CACHE_XCODE xcodebuild -version override
OMI_QUALIFICATION_SWIFT_CACHE_SWIFT xcrun swift --version override
OMI_QUALIFICATION_SWIFT_CACHE_MACOS sw_vers identity override
OMI_QUALIFICATION_SWIFT_CACHE_ARCH uname -m override
USAGE
}
[[ $# -eq 3 && "$1" == "prepare" ]] || { usage >&2; exit 2; }
SOURCE_SHA="$2"
SOURCE_REPOSITORY="$3"
[[ "$SOURCE_SHA" =~ ^[0-9a-f]{40}$ ]] || {
echo "qualification Swift cache: invalid source SHA: $SOURCE_SHA" >&2
exit 2
}
git -C "$SOURCE_REPOSITORY" rev-parse --git-dir >/dev/null 2>&1 || {
echo "qualification Swift cache: source repository is not Git: $SOURCE_REPOSITORY" >&2
exit 1
}
CACHE_ROOT="${OMI_QUALIFICATION_SWIFT_CACHE_ROOT:-$HOME/Library/Caches/OmiDesktop/qualification-swiftpm-v2}"
CACHE_DIR="$CACHE_ROOT/$SOURCE_SHA"
PERSISTENT_SOURCE="$CACHE_DIR/source"
PACKAGE_DIR="$PERSISTENT_SOURCE/desktop/macos/Desktop"
PACKAGE_SWIFT="$PACKAGE_DIR/Package.swift"
PACKAGE_RESOLVED="$PACKAGE_DIR/Package.resolved"
CACHE_BUILD="$PACKAGE_DIR/.build"
MANIFEST="$CACHE_DIR/manifest.json"
COMPLETE="$CACHE_DIR/complete"
XCODE="${OMI_QUALIFICATION_SWIFT_CACHE_XCODE:-$(xcodebuild -version 2>&1)}"
SWIFT="${OMI_QUALIFICATION_SWIFT_CACHE_SWIFT:-$(xcrun swift --version 2>&1)}"
MACOS="${OMI_QUALIFICATION_SWIFT_CACHE_MACOS:-$(printf '%s (%s)' "$(sw_vers -productVersion)" "$(sw_vers -buildVersion)")}"
ARCH="${OMI_QUALIFICATION_SWIFT_CACHE_ARCH:-$(uname -m)}"
TEMP_DIR=""
LOCK_DIR=""
LOCK_HELD=0
cleanup() {
if [[ -n "$TEMP_DIR" && -d "$TEMP_DIR" ]]; then
rm -rf "$TEMP_DIR"
fi
if [[ "$LOCK_HELD" -eq 1 && -n "$LOCK_DIR" && -d "$LOCK_DIR" && ! -L "$LOCK_DIR" ]]; then
rm -rf "$LOCK_DIR"
fi
}
trap cleanup EXIT
symlinked_component="$(python3 - "$CACHE_ROOT" <<'PY'
import os
import sys
path = os.path.abspath(os.path.expanduser(sys.argv[1]))
current = os.path.sep
for component in path.split(os.path.sep)[1:]:
current = os.path.join(current, component)
if os.path.lexists(current) and os.path.islink(current):
print(current)
raise SystemExit(0)
raise SystemExit(1)
PY
)" && {
echo "qualification Swift cache: refusing symlinked cache path component: $symlinked_component" >&2
exit 1
}
if [[ -e "$CACHE_ROOT" && ! -d "$CACHE_ROOT" ]]; then
echo "qualification Swift cache: cache root collision: $CACHE_ROOT" >&2
exit 1
fi
mkdir -p "$CACHE_ROOT"
chmod 700 "$CACHE_ROOT"
# Recheck after creation, then serialize all validation, cleanup, and publication
# for this exact SHA. mkdir is the atomic lock acquisition primitive on macOS.
symlinked_component="$(python3 - "$CACHE_ROOT" <<'PY'
import os
import sys
path = os.path.abspath(os.path.expanduser(sys.argv[1]))
current = os.path.sep
for component in path.split(os.path.sep)[1:]:
current = os.path.join(current, component)
if os.path.lexists(current) and os.path.islink(current):
print(current)
raise SystemExit(0)
raise SystemExit(1)
PY
)" && {
echo "qualification Swift cache: refusing symlinked cache path component: $symlinked_component" >&2
exit 1
}
LOCK_DIR="$CACHE_ROOT/.${SOURCE_SHA}.lock"
for _ in {1..1200}; do
if mkdir "$LOCK_DIR" 2>/dev/null; then
LOCK_HELD=1
printf '%s\n' "$$" > "$LOCK_DIR/pid"
break
fi
# mkdir failed: either another prepare legitimately holds the lock (a
# directory) or a non-directory squats the path. Read the type with a single
# lstat. The previous check evaluated `-e` and `-d` as two separate stats, so
# a concurrent holder releasing the lock (rm -rf) between them left `-e` true
# and `-d` false and was misreported as a collision — the source of the
# intermittent "lock destination collision" failures. A symlink or regular
# file is a real, persistent squat and stays fatal; a vanished directory is
# transient and simply retried.
lock_type="$(stat -f '%HT' "$LOCK_DIR" 2>/dev/null || true)"
if [[ -n "$lock_type" && "$lock_type" != "Directory" ]]; then
echo "qualification Swift cache: lock destination collision (${lock_type}): $LOCK_DIR" >&2
exit 1
fi
sleep 0.05
done
if [[ "$LOCK_HELD" -ne 1 ]]; then
echo "qualification Swift cache: timed out waiting for exact-SHA cache lock: $LOCK_DIR" >&2
exit 1
fi
if [[ -L "$CACHE_DIR" ]]; then
echo "qualification Swift cache: refusing symlinked exact-SHA cache entry: $CACHE_DIR" >&2
exit 1
fi
if [[ -e "$CACHE_DIR" && ! -d "$CACHE_DIR" ]]; then
echo "qualification Swift cache: exact-SHA cache destination collision: $CACHE_DIR" >&2
exit 1
fi
CACHE_STATE="HIT"
if [[ ! -e "$CACHE_DIR" ]]; then
CACHE_STATE="MISS"
TEMP_DIR="$(mktemp -d "$CACHE_ROOT/.${SOURCE_SHA}.prepare.XXXXXX")"
mkdir "$TEMP_DIR/source"
git clone --quiet --no-hardlinks --no-checkout "$SOURCE_REPOSITORY" "$TEMP_DIR/source"
git -C "$TEMP_DIR/source" checkout --quiet --detach "$SOURCE_SHA"
[[ "$(git -C "$TEMP_DIR/source" rev-parse HEAD)" == "$SOURCE_SHA" ]] || {
echo "qualification Swift cache: cloned source SHA does not match candidate $SOURCE_SHA" >&2
exit 1
}
mkdir -p "$TEMP_DIR/source/desktop/macos/Desktop/.build"
CACHE_DIR="$TEMP_DIR"
PERSISTENT_SOURCE="$CACHE_DIR/source"
PACKAGE_DIR="$PERSISTENT_SOURCE/desktop/macos/Desktop"
PACKAGE_SWIFT="$PACKAGE_DIR/Package.swift"
PACKAGE_RESOLVED="$PACKAGE_DIR/Package.resolved"
CACHE_BUILD="$PACKAGE_DIR/.build"
MANIFEST="$CACHE_DIR/manifest.json"
COMPLETE="$CACHE_DIR/complete"
fi
if [[ "$CACHE_STATE" == "HIT" ]]; then
if [[ ! -f "$COMPLETE" || -L "$COMPLETE" || "$(cat "$COMPLETE" 2>/dev/null)" != "complete" \
|| ! -f "$MANIFEST" || -L "$MANIFEST" \
|| ! -d "$PERSISTENT_SOURCE" || -L "$PERSISTENT_SOURCE" \
|| ! -d "$CACHE_BUILD" || -L "$CACHE_BUILD" ]]; then
echo "qualification Swift cache: incomplete exact-SHA cache entry: $CACHE_DIR" >&2
exit 1
fi
[[ -d "$PERSISTENT_SOURCE/.git" && ! -L "$PERSISTENT_SOURCE/.git" ]] || {
echo "qualification Swift cache: incomplete exact-SHA cache entry: $CACHE_DIR" >&2
exit 1
}
WORKTREE_SHA="$(git -C "$PERSISTENT_SOURCE" rev-parse HEAD 2>/dev/null || true)"
[[ "$WORKTREE_SHA" == "$SOURCE_SHA" ]] || {
echo "qualification Swift cache: persistent source SHA $WORKTREE_SHA does not match candidate $SOURCE_SHA" >&2
exit 1
}
if ! git -C "$PERSISTENT_SOURCE" diff --quiet --ignore-submodules -- \
|| ! git -C "$PERSISTENT_SOURCE" diff --cached --quiet --ignore-submodules --; then
echo "qualification Swift cache: persistent source has tracked changes: $PERSISTENT_SOURCE" >&2
exit 1
fi
# Remove all untrusted retry residue while preserving only SwiftPM's direct,
# stable scratch directory. Tracked files are not rewritten, preserving mtimes.
git -C "$PERSISTENT_SOURCE" clean -ffdx -e desktop/macos/Desktop/.build/ >/dev/null
fi
if [[ ! -f "$PACKAGE_SWIFT" || -L "$PACKAGE_SWIFT" \
|| ! -f "$PACKAGE_RESOLVED" || -L "$PACKAGE_RESOLVED" ]]; then
echo "qualification Swift cache: package identity files are missing or symlinked" >&2
exit 1
fi
EXPECTED_MANIFEST="$(python3 - "$SOURCE_SHA" "$PACKAGE_SWIFT" "$PACKAGE_RESOLVED" "$XCODE" "$SWIFT" "$MACOS" "$ARCH" <<'PY'
import hashlib
import json
import sys
from pathlib import Path
source_sha, package_swift, package_resolved, xcode, swift, macos, arch = sys.argv[1:]
def digest(path: str) -> str:
return hashlib.sha256(Path(path).read_bytes()).hexdigest()
print(json.dumps({
"cache_format": 2,
"source_sha": source_sha,
"package_swift_sha256": digest(package_swift),
"package_resolved_sha256": digest(package_resolved),
"xcode": xcode,
"swift": swift,
"macos": macos,
"architecture": arch,
}, indent=2, sort_keys=True))
PY
)"
if [[ "$CACHE_STATE" == "HIT" ]]; then
if ! python3 - "$MANIFEST" "$EXPECTED_MANIFEST" <<'PY'
import json
import sys
try:
actual = json.load(open(sys.argv[1], encoding="utf-8"))
expected = json.loads(sys.argv[2])
except (OSError, ValueError, TypeError):
raise SystemExit(1)
raise SystemExit(0 if actual == expected else 1)
PY
then
echo "qualification Swift cache: cache provenance mismatch: $CACHE_DIR" >&2
exit 1
fi
else
printf '%s\n' "$EXPECTED_MANIFEST" > "$MANIFEST"
printf '%s\n' complete > "$COMPLETE"
chmod 700 "$CACHE_DIR" "$PERSISTENT_SOURCE" "$CACHE_BUILD"
FINAL_DIR="$CACHE_ROOT/$SOURCE_SHA"
if [[ -e "$FINAL_DIR" || -L "$FINAL_DIR" ]] || ! mv "$CACHE_DIR" "$FINAL_DIR" 2>/dev/null; then
echo "qualification Swift cache: exact-SHA cache destination collision: $FINAL_DIR" >&2
exit 1
fi
TEMP_DIR=""
CACHE_DIR="$FINAL_DIR"
PERSISTENT_SOURCE="$CACHE_DIR/source"
fi
echo "qualification Swift cache $CACHE_STATE: source=$SOURCE_SHA path=$PERSISTENT_SOURCE" >&2
printf '%s\n' "$PERSISTENT_SOURCE"