@@ -132,13 +132,27 @@ 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+ rm dist/cycode-cli/_internal/Python
144+ cp "$FRAMEWORK_PYTHON" dist/cycode-cli/_internal/Python
145+ fi
146+ rm -rf dist/cycode-cli/_internal/Python.framework
147+ fi
148+
149+ # Sign all Mach-O binaries (excluding the main executable)
150+ while IFS= read -r file; do
138151 if file -b "$file" | grep -q "Mach-O"; then
152+ echo "Signing: $file"
139153 codesign --force --sign "$APPLE_CERT_NAME" --timestamp --options runtime "$file"
140154 fi
141- done
155+ done < <(find dist/cycode-cli -type f ! -name "cycode-cli")
142156
143157 # Re-sign the main executable with entitlements (must be last)
144158 codesign --force --sign "$APPLE_CERT_NAME" --timestamp --options runtime --entitlements entitlements.plist dist/cycode-cli/cycode-cli
@@ -176,15 +190,35 @@ jobs:
176190
177191 # we can't staple the app because it's executable
178192
193+ - name : Verify macOS code signatures
194+ if : runner.os == 'macOS'
195+ run : |
196+ FAILED=false
197+ while IFS= read -r file; do
198+ if file -b "$file" | grep -q "Mach-O"; then
199+ if ! codesign --verify "$file" 2>&1; then
200+ echo "INVALID: $file"
201+ codesign -dv "$file" 2>&1 || true
202+ FAILED=true
203+ else
204+ echo "OK: $file"
205+ fi
206+ fi
207+ done < <(find dist/cycode-cli -type f)
208+
209+ if [ "$FAILED" = true ]; then
210+ echo "Found binaries with invalid signatures!"
211+ exit 1
212+ fi
213+
214+ codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE
215+
179216 - name : Test macOS signed executable
180217 if : runner.os == 'macOS'
181218 run : |
182219 file -b $PATH_TO_CYCODE_CLI_EXECUTABLE
183220 time $PATH_TO_CYCODE_CLI_EXECUTABLE version
184221
185- # verify signature
186- codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE
187-
188222 - name : Import cert for Windows and setup envs
189223 if : runner.os == 'Windows'
190224 env :
@@ -236,6 +270,47 @@ jobs:
236270 name : ${{ env.ARTIFACT_NAME }}
237271 path : dist
238272
273+ - name : Verify macOS artifact end-to-end
274+ if : runner.os == 'macOS' && matrix.mode == 'onedir'
275+ uses : actions/download-artifact@v4
276+ with :
277+ name : ${{ env.ARTIFACT_NAME }}
278+ path : /tmp/artifact-verify
279+
280+ - name : Verify macOS artifact signatures and run with quarantine
281+ if : runner.os == 'macOS' && matrix.mode == 'onedir'
282+ run : |
283+ # extract the onedir zip exactly as an end user would
284+ ARCHIVE=$(find /tmp/artifact-verify -name "*.zip" | head -1)
285+ echo "Verifying archive: $ARCHIVE"
286+ unzip "$ARCHIVE" -d /tmp/artifact-extracted
287+
288+ # verify all Mach-O code signatures
289+ FAILED=false
290+ while IFS= read -r file; do
291+ if file -b "$file" | grep -q "Mach-O"; then
292+ if ! codesign --verify "$file" 2>&1; then
293+ echo "INVALID: $file"
294+ codesign -dv "$file" 2>&1 || true
295+ FAILED=true
296+ else
297+ echo "OK: $file"
298+ fi
299+ fi
300+ done < <(find /tmp/artifact-extracted -type f)
301+
302+ if [ "$FAILED" = true ]; then
303+ echo "Artifact contains binaries with invalid signatures!"
304+ exit 1
305+ fi
306+
307+ # simulate download quarantine and test execution
308+ # this is the definitive test — it triggers the same dlopen checks end users experience
309+ find /tmp/artifact-extracted -type f -exec xattr -w com.apple.quarantine "0081;$(printf '%x' $(date +%s));CI;$(uuidgen)" {} \;
310+ EXECUTABLE=$(find /tmp/artifact-extracted -name "cycode-cli" -type f | head -1)
311+ echo "Testing quarantined executable: $EXECUTABLE"
312+ time "$EXECUTABLE" version
313+
239314 - name : Upload files to release
240315 if : ${{ github.event_name == 'workflow_dispatch' && inputs.publish }}
241316 uses : svenstaro/upload-release-action@v2
0 commit comments