@@ -136,7 +136,9 @@ jobs:
136136 # Main executable must be signed last after all its dependencies
137137 find dist/cycode-cli -type f ! -name "cycode-cli" | while read -r file; do
138138 if file -b "$file" | grep -q "Mach-O"; then
139- codesign --force --sign "$APPLE_CERT_NAME" --timestamp --options runtime "$file"
139+ # override identifier to avoid framework-style identifiers (e.g. org.python.python)
140+ # that cause --strict verification to expect a missing Info.plist
141+ codesign --force --sign "$APPLE_CERT_NAME" --identifier "com.cycode.$(basename "$file")" --timestamp --options runtime "$file"
140142 fi
141143 done
142144
@@ -176,15 +178,35 @@ jobs:
176178
177179 # we can't staple the app because it's executable
178180
181+ - name : Verify macOS code signatures
182+ if : runner.os == 'macOS'
183+ run : |
184+ # verify all Mach-O binaries in the output are properly signed
185+ FAILED=false
186+ while IFS= read -r file; do
187+ if file -b "$file" | grep -q "Mach-O"; then
188+ if ! codesign --verify --strict "$file" 2>&1; then
189+ echo "INVALID signature: $file"
190+ codesign -dv "$file" 2>&1 || true
191+ FAILED=true
192+ fi
193+ fi
194+ done < <(find dist/cycode-cli -type f)
195+
196+ if [ "$FAILED" = true ]; then
197+ echo "Found binaries with invalid signatures!"
198+ exit 1
199+ fi
200+
201+ # verify main executable signature in detail
202+ codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE
203+
179204 - name : Test macOS signed executable
180205 if : runner.os == 'macOS'
181206 run : |
182207 file -b $PATH_TO_CYCODE_CLI_EXECUTABLE
183208 time $PATH_TO_CYCODE_CLI_EXECUTABLE version
184209
185- # verify signature
186- codesign -dv --verbose=4 $PATH_TO_CYCODE_CLI_EXECUTABLE
187-
188210 - name : Import cert for Windows and setup envs
189211 if : runner.os == 'Windows'
190212 env :
@@ -236,6 +258,46 @@ jobs:
236258 name : ${{ env.ARTIFACT_NAME }}
237259 path : dist
238260
261+ - name : Verify macOS artifact end-to-end
262+ if : runner.os == 'macOS' && matrix.mode == 'onedir'
263+ uses : actions/download-artifact@v4
264+ with :
265+ name : ${{ env.ARTIFACT_NAME }}
266+ path : /tmp/artifact-verify
267+
268+ - name : Verify macOS artifact signatures and run with quarantine
269+ if : runner.os == 'macOS' && matrix.mode == 'onedir'
270+ run : |
271+ # extract the onedir zip exactly as an end user would
272+ ARCHIVE=$(find /tmp/artifact-verify -name "*.zip" | head -1)
273+ echo "Verifying archive: $ARCHIVE"
274+ ditto -x -k "$ARCHIVE" /tmp/artifact-extracted
275+
276+ # verify all Mach-O code signatures (strict mode)
277+ FAILED=false
278+ while IFS= read -r file; do
279+ if file -b "$file" | grep -q "Mach-O"; then
280+ if ! codesign --verify --strict "$file" 2>&1; then
281+ echo "INVALID: $file"
282+ codesign -dv "$file" 2>&1 || true
283+ FAILED=true
284+ else
285+ echo "OK: $file"
286+ fi
287+ fi
288+ done < <(find /tmp/artifact-extracted -type f)
289+
290+ if [ "$FAILED" = true ]; then
291+ echo "Artifact contains binaries with invalid signatures!"
292+ exit 1
293+ fi
294+
295+ # simulate download quarantine and test execution
296+ find /tmp/artifact-extracted -type f -exec xattr -w com.apple.quarantine "0081;$(printf '%x' $(date +%s));CI;$(uuidgen)" {} \;
297+ EXECUTABLE=$(find /tmp/artifact-extracted -name "cycode-cli" -type f | head -1)
298+ echo "Testing quarantined executable: $EXECUTABLE"
299+ time "$EXECUTABLE" version
300+
239301 - name : Upload files to release
240302 if : ${{ github.event_name == 'workflow_dispatch' && inputs.publish }}
241303 uses : svenstaro/upload-release-action@v2
0 commit comments