@@ -132,13 +132,26 @@ jobs:
132132 env :
133133 APPLE_CERT_NAME : ${{ secrets.APPLE_CERT_NAME }}
134134 run : |
135- # Sign all Mach-O binaries in the onedir output (excluding the main executable)
136- # Main executable must be signed last after all its dependencies
137- find dist/cycode-cli -type f ! -name "cycode-cli" | while read -r file; do
135+ # The standalone _internal/Python fails codesign --verify --strict because it was
136+ # extracted from Python.framework without Info.plist context.
137+ # Fix: remove the bare copy and replace with the framework version's binary,
138+ # then delete the framework directory (it's redundant).
139+ if [ -d dist/cycode-cli/_internal/Python.framework ]; then
140+ FRAMEWORK_PYTHON=$(find dist/cycode-cli/_internal/Python.framework/Versions -name "Python" -type f | head -1)
141+ if [ -n "$FRAMEWORK_PYTHON" ]; then
142+ echo "Replacing _internal/Python with framework binary"
143+ cp "$FRAMEWORK_PYTHON" dist/cycode-cli/_internal/Python
144+ fi
145+ rm -rf dist/cycode-cli/_internal/Python.framework
146+ fi
147+
148+ # Sign all Mach-O binaries (excluding the main executable)
149+ while IFS= read -r file; do
138150 if file -b "$file" | grep -q "Mach-O"; then
151+ echo "Signing: $file"
139152 codesign --force --sign "$APPLE_CERT_NAME" --timestamp --options runtime "$file"
140153 fi
141- done
154+ done < <(find dist/cycode-cli -type f ! -name "cycode-cli")
142155
143156 # Re-sign the main executable with entitlements (must be last)
144157 codesign --force --sign "$APPLE_CERT_NAME" --timestamp --options runtime --entitlements entitlements.plist dist/cycode-cli/cycode-cli
@@ -176,15 +189,35 @@ jobs:
176189
177190 # we can't staple the app because it's executable
178191
192+ - name : Verify macOS code signatures
193+ if : runner.os == 'macOS'
194+ run : |
195+ FAILED=false
196+ while IFS= read -r file; do
197+ if file -b "$file" | grep -q "Mach-O"; then
198+ if ! codesign --verify "$file" 2>&1; then
199+ echo "INVALID: $file"
200+ codesign -dv "$file" 2>&1 || true
201+ FAILED=true
202+ else
203+ echo "OK: $file"
204+ fi
205+ fi
206+ done < <(find dist/cycode-cli -type f)
207+
208+ if [ "$FAILED" = true ]; then
209+ echo "Found binaries with invalid signatures!"
210+ exit 1
211+ fi
212+
213+ codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE
214+
179215 - name : Test macOS signed executable
180216 if : runner.os == 'macOS'
181217 run : |
182218 file -b $PATH_TO_CYCODE_CLI_EXECUTABLE
183219 time $PATH_TO_CYCODE_CLI_EXECUTABLE version
184220
185- # verify signature
186- codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE
187-
188221 - name : Import cert for Windows and setup envs
189222 if : runner.os == 'Windows'
190223 env :
@@ -236,6 +269,47 @@ jobs:
236269 name : ${{ env.ARTIFACT_NAME }}
237270 path : dist
238271
272+ - name : Verify macOS artifact end-to-end
273+ if : runner.os == 'macOS' && matrix.mode == 'onedir'
274+ uses : actions/download-artifact@v4
275+ with :
276+ name : ${{ env.ARTIFACT_NAME }}
277+ path : /tmp/artifact-verify
278+
279+ - name : Verify macOS artifact signatures and run with quarantine
280+ if : runner.os == 'macOS' && matrix.mode == 'onedir'
281+ run : |
282+ # extract the onedir zip exactly as an end user would
283+ ARCHIVE=$(find /tmp/artifact-verify -name "*.zip" | head -1)
284+ echo "Verifying archive: $ARCHIVE"
285+ unzip "$ARCHIVE" -d /tmp/artifact-extracted
286+
287+ # verify all Mach-O code signatures
288+ FAILED=false
289+ while IFS= read -r file; do
290+ if file -b "$file" | grep -q "Mach-O"; then
291+ if ! codesign --verify "$file" 2>&1; then
292+ echo "INVALID: $file"
293+ codesign -dv "$file" 2>&1 || true
294+ FAILED=true
295+ else
296+ echo "OK: $file"
297+ fi
298+ fi
299+ done < <(find /tmp/artifact-extracted -type f)
300+
301+ if [ "$FAILED" = true ]; then
302+ echo "Artifact contains binaries with invalid signatures!"
303+ exit 1
304+ fi
305+
306+ # simulate download quarantine and test execution
307+ # this is the definitive test — it triggers the same dlopen checks end users experience
308+ find /tmp/artifact-extracted -type f -exec xattr -w com.apple.quarantine "0081;$(printf '%x' $(date +%s));CI;$(uuidgen)" {} \;
309+ EXECUTABLE=$(find /tmp/artifact-extracted -name "cycode-cli" -type f | head -1)
310+ echo "Testing quarantined executable: $EXECUTABLE"
311+ time "$EXECUTABLE" version
312+
239313 - name : Upload files to release
240314 if : ${{ github.event_name == 'workflow_dispatch' && inputs.publish }}
241315 uses : svenstaro/upload-release-action@v2
0 commit comments