diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 25c11fabec..569765afce 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -186,6 +186,177 @@ jobs: SLACK_MESSAGE: "Please check the logs for the run at ${{ env.WORKFLOW_URL }}/${{ github.run_id }}" SLACK_TITLE: "Release Failed" + sign-macos-binaries: + if: ${{ + github.repository_owner == 'maidsafe' && + (github.ref == 'refs/heads/stable' || startsWith(github.ref, 'refs/heads/rc')) + }} + name: sign macos binaries + runs-on: macos-latest + needs: [ build ] + environment: ${{ github.ref == 'refs/heads/stable' && 'stable' || 'release-candidate' }} + strategy: + matrix: + target: [x86_64-apple-darwin, aarch64-apple-darwin] + + steps: + - uses: actions/checkout@v5 + + # Download unsigned macOS binaries from build job + - uses: actions/download-artifact@master + with: + name: autonomi-${{ matrix.target }} + path: artifacts/${{ matrix.target }}/release + + # Install Apple certificate from secrets + - name: Import code signing certificate + env: + MACOS_CERTIFICATE: ${{ secrets.MACOS_CERTIFICATE }} + MACOS_CERTIFICATE_PASSWORD: ${{ secrets.MACOS_CERTIFICATE_PASSWORD }} + KEYCHAIN_PASSWORD: ${{ secrets.KEYCHAIN_PASSWORD }} + run: | + # Create a temporary keychain + KEYCHAIN_PATH=$RUNNER_TEMP/app-signing.keychain-db + security create-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + security set-keychain-settings -lut 21600 $KEYCHAIN_PATH + security unlock-keychain -p "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # Import certificate to keychain + echo "$MACOS_CERTIFICATE" | base64 --decode > certificate.p12 + security import certificate.p12 -k $KEYCHAIN_PATH -P "$MACOS_CERTIFICATE_PASSWORD" -T /usr/bin/codesign + security set-key-partition-list -S apple-tool:,apple:,codesign: -s -k "$KEYCHAIN_PASSWORD" $KEYCHAIN_PATH + + # Add keychain to search list + security list-keychain -d user -s $KEYCHAIN_PATH + + # Sign all macOS binaries in the artifacts directory + - name: Sign macOS binaries + env: + MACOS_SIGNING_IDENTITY: ${{ secrets.MACOS_SIGNING_IDENTITY }} + run: | + ARTIFACTS_PATH="artifacts/${{ matrix.target }}/release" + + # Find all binaries (executables without .dylib extension) + BINARIES=$(find "$ARTIFACTS_PATH" -type f -perm +111 ! -name "*.dylib" ! -name "*.d" ! -name ".cargo-lock") + + if [ -z "$BINARIES" ]; then + echo "Error: No binaries found in $ARTIFACTS_PATH" + exit 1 + fi + + echo "Found binaries to sign:" + echo "$BINARIES" | while read -r binary; do + echo " - $(basename "$binary")" + done + echo "" + + # Sign each binary + echo "$BINARIES" | while read -r binary; do + echo "Signing: $(basename "$binary")" + codesign --force --sign "$MACOS_SIGNING_IDENTITY" \ + --options runtime \ + --timestamp \ + --verbose \ + "$binary" + + if [ $? -ne 0 ]; then + echo "Error: Failed to sign $(basename "$binary")" + exit 1 + fi + done + + echo "" + echo "All binaries signed successfully!" + + # Verify code signatures + - name: Verify signatures + run: | + ARTIFACTS_PATH="artifacts/${{ matrix.target }}/release" + BINARIES=$(find "$ARTIFACTS_PATH" -type f -perm +111 ! -name "*.dylib" ! -name "*.d" ! -name ".cargo-lock") + + echo "Verifying signatures:" + FAILED=() + + echo "$BINARIES" | while read -r binary; do + echo "Verifying: $(basename "$binary")" + if codesign --verify --deep --strict --verbose=2 "$binary" 2>&1; then + echo " ✓ Signature valid" + codesign -dvv "$binary" 2>&1 | grep -E "(Authority|Timestamp|Identifier)" + else + echo " ✗ Signature verification failed" + FAILED+=("$(basename "$binary")") + fi + echo "" + done + + if [ ${#FAILED[@]} -gt 0 ]; then + echo "Error: Signature verification failed for: ${FAILED[*]}" + exit 1 + fi + + echo "All signatures verified successfully!" + + # Notarize binaries with Apple + - name: Notarize binaries + env: + MACOS_NOTARIZATION_APPLE_ID: ${{ secrets.MACOS_NOTARIZATION_APPLE_ID }} + MACOS_NOTARIZATION_TEAM_ID: ${{ secrets.MACOS_NOTARIZATION_TEAM_ID }} + MACOS_NOTARIZATION_PASSWORD: ${{ secrets.MACOS_NOTARIZATION_PASSWORD }} + run: | + ARTIFACTS_PATH="artifacts/${{ matrix.target }}/release" + BINARIES=$(find "$ARTIFACTS_PATH" -type f -perm +111 ! -name "*.dylib" ! -name "*.d" ! -name ".cargo-lock") + + echo "Notarizing binaries..." + + # Create a temporary directory for zip files + TEMP_DIR=$(mktemp -d) + + echo "$BINARIES" | while read -r binary; do + BINARY_NAME=$(basename "$binary") + ZIP_NAME="${BINARY_NAME}.zip" + + echo "Preparing $BINARY_NAME for notarization..." + ditto -c -k --keepParent "$binary" "$TEMP_DIR/$ZIP_NAME" + + echo "Submitting $BINARY_NAME to Apple notary service..." + xcrun notarytool submit "$TEMP_DIR/$ZIP_NAME" \ + --apple-id "$MACOS_NOTARIZATION_APPLE_ID" \ + --team-id "$MACOS_NOTARIZATION_TEAM_ID" \ + --password "$MACOS_NOTARIZATION_PASSWORD" \ + --wait \ + --timeout 30m + + if [ $? -ne 0 ]; then + echo "Error: Notarization failed for $BINARY_NAME" + exit 1 + fi + + # Staple the notarization ticket to the binary + echo "Stapling notarization ticket to $BINARY_NAME..." + xcrun stapler staple "$binary" || echo "Warning: Stapling failed for $BINARY_NAME (this is expected for command-line tools)" + + echo "" + done + + # Clean up temporary directory + rm -rf "$TEMP_DIR" + + echo "All binaries notarized successfully!" + + # Upload signed binaries + - uses: actions/upload-artifact@v4 + with: + name: autonomi-${{ matrix.target }}-signed + path: artifacts/${{ matrix.target }}/release + + - name: post notification to slack on failure + if: ${{ failure() }} + uses: bryannice/gitactions-slack-notification@2.0.0 + env: + SLACK_INCOMING_WEBHOOK: ${{ secrets.SLACK_GH_ACTIONS_WEBHOOK_URL }} + SLACK_MESSAGE: "Please check the logs for the run at ${{ env.WORKFLOW_URL }}/${{ github.run_id }}" + SLACK_TITLE: "Release Signing Failed" + s3-release: if: ${{ github.repository_owner == 'maidsafe' && @@ -193,7 +364,7 @@ jobs: }} name: s3 release runs-on: ubuntu-latest - needs: [ build ] + needs: [ build, sign-macos-binaries ] env: AWS_ACCESS_KEY_ID: ${{ secrets.S3_DEPLOY_AWS_ACCESS_KEY_ID }} AWS_SECRET_ACCESS_KEY: ${{ secrets.S3_DEPLOY_AWS_SECRET_ACCESS_KEY }} @@ -210,11 +381,11 @@ jobs: path: artifacts/x86_64-unknown-linux-musl/release - uses: actions/download-artifact@master with: - name: autonomi-x86_64-apple-darwin + name: autonomi-x86_64-apple-darwin-signed path: artifacts/x86_64-apple-darwin/release - uses: actions/download-artifact@master with: - name: autonomi-aarch64-apple-darwin + name: autonomi-aarch64-apple-darwin-signed path: artifacts/aarch64-apple-darwin/release - uses: actions/download-artifact@master with: @@ -257,7 +428,7 @@ jobs: }} name: github release runs-on: ubuntu-latest - needs: [ build ] + needs: [ build, sign-macos-binaries ] steps: - uses: actions/checkout@v5 - uses: actions/download-artifact@master @@ -270,11 +441,11 @@ jobs: path: artifacts/x86_64-unknown-linux-musl/release - uses: actions/download-artifact@master with: - name: autonomi-x86_64-apple-darwin + name: autonomi-x86_64-apple-darwin-signed path: artifacts/x86_64-apple-darwin/release - uses: actions/download-artifact@master with: - name: autonomi-aarch64-apple-darwin + name: autonomi-aarch64-apple-darwin-signed path: artifacts/aarch64-apple-darwin/release - uses: actions/download-artifact@master with: