alinaliBQ commented on code in PR #55321:
URL: https://github.com/apache/arrow/pull/55321#discussion_r4448723132


##########
.github/workflows/package_odbc.yml:
##########
@@ -179,3 +225,253 @@ jobs:
           gh release upload $env:GITHUB_REF_NAME `
             --clobber `
             Apache-Arrow-Flight-SQL-ODBC-*-win64.msi
+
+  # Upload unsigned non-universal DYLIBs
+  odbc-macos-upload-dylib:
+    name: ODBC macOS Upload Unsigned ${{ matrix.architecture }} DYLIB
+    runs-on: macos-${{ matrix.macos-version }}
+    if: github.event_name == 'push'
+    timeout-minutes: 120
+    strategy:
+      fail-fast: false
+      matrix:
+        include:
+          - { architecture: AMD64, macos-version: "26-intel" }
+          - { architecture: ARM64, macos-version: "26" }
+    env:
+      ARROW_BUILD_TESTS: OFF
+      ARROW_BUILD_TYPE: release
+      ARROW_DEPENDENCY_SOURCE: BUNDLED
+      ARROW_DEPENDENCY_USE_SHARED: OFF
+      ARROW_FLIGHT_SQL_ODBC: ON
+      ARROW_FLIGHT_SQL_ODBC_INSTALLER: ON
+      ARROW_HOME: /tmp/local
+      ARROW_MIMALLOC: OFF
+      MACOSX_DEPLOYMENT_TARGET: "14.0"
+    steps:
+      - name: Checkout Arrow
+        uses: actions/checkout@v7
+        with:
+          persist-credentials: false
+          fetch-depth: 0
+          submodules: recursive
+      - name: Build ODBC macOS
+        uses: ./.github/actions/odbc-mac
+        with:
+          architecture: ${{ matrix.architecture }}
+      - name: Copy Unsigned ODBC DYLIB
+        run: |
+          # The unversioned name libarrow_flight_sql_odbc.dylib is a symlink to
+          # libarrow_flight_sql_odbc.<version>.dylib. Copy the real file, 
keeping
+          # its versioned name, so the upload doesn't hit a dangling symlink.
+          mkdir ${{ matrix.architecture }}-dylib
+          cp "$(realpath build/cpp/${{ env.ARROW_BUILD_TYPE 
}}/libarrow_flight_sql_odbc.dylib)" \
+            ${{ matrix.architecture }}-dylib/
+      - name: Upload ODBC DYLIB to the job
+        uses: actions/upload-artifact@v7
+        with:
+          name: flight-sql-odbc-dylib-${{ matrix.architecture }}
+          path: ${{ matrix.architecture 
}}-dylib/libarrow_flight_sql_odbc.*.dylib
+          if-no-files-found: error
+
+  # Upload unsigned universal DYLIB from ARM and AMD DYLIBs
+  odbc-macos-universal-dylib:
+    needs: odbc-macos-upload-dylib
+    name: ODBC macOS Create Unsigned Universal DYLIB
+    runs-on: macos-26
+    timeout-minutes: 30
+    steps:
+      - name: Download ARM64 DYLIB
+        uses: actions/download-artifact@v8
+        with:
+          name: flight-sql-odbc-dylib-ARM64
+          path: arm64-dylib
+      - name: Download AMD64 DYLIB
+        uses: actions/download-artifact@v8
+        with:
+          name: flight-sql-odbc-dylib-AMD64
+          path: amd64-dylib
+      - name: Create universal DYLIB
+        run: |
+          set -euo pipefail
+
+          arm64_dylib="$(ls arm64-dylib/libarrow_flight_sql_odbc.*.dylib)"
+          amd64_dylib="$(ls amd64-dylib/libarrow_flight_sql_odbc.*.dylib)"
+          dylib_name="$(basename "${arm64_dylib}")"
+          if [ "$(basename "${amd64_dylib}")" != "${dylib_name}" ]; then
+            echo "ARM64 and AMD64 DYLIB names differ: ${arm64_dylib} vs 
${amd64_dylib}"
+            exit 1
+          fi
+
+          # Rename unsigned DYLIB 
+          mkdir universal-dylib
+          
unsigned_path="universal-dylib/libarrow_flight_sql_odbc_unsigned.${dylib_name#libarrow_flight_sql_odbc.}"
+          lipo -create "${arm64_dylib}" "${amd64_dylib}" -output 
"${unsigned_path}"
+          unsigned_archs="$(lipo -archs "${unsigned_path}")"
+          echo "${unsigned_path}: ${unsigned_archs}"
+      - name: Upload universal DYLIB to the job
+        uses: actions/upload-artifact@v7
+        with:
+          name: flight-sql-odbc-dylib-universal
+          path: universal-dylib/libarrow_flight_sql_odbc_unsigned.*.dylib
+          if-no-files-found: error
+
+  odbc-dylib-release:
+    needs: odbc-macos-universal-dylib
+    name: Upload Unsigned ODBC DYLIB to Release
+    runs-on: ubuntu-latest
+    permissions:
+      # Upload to GitHub Release
+      contents: write
+    steps:
+      - name: Checkout Arrow
+        uses: actions/checkout@v7
+        with:
+          persist-credentials: false
+          fetch-depth: 0
+          submodules: recursive
+      - name: Download the universal DYLIB
+        uses: actions/download-artifact@v8
+        with:
+          name: flight-sql-odbc-dylib-universal
+      - name: Wait for creating GitHub Release
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          dev/release/utils-watch-gh-workflow.sh \
+            ${GITHUB_REF_NAME} \
+            release_candidate.yml
+      - name: Upload the artifacts to GitHub Release
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          gh release upload ${GITHUB_REF_NAME} \
+            --clobber \
+            libarrow_flight_sql_odbc_unsigned.*.dylib
+
+  # The ARM64 base PKG is not published to the release, so it is rebuilt here
+  # (the release step runs as a separate workflow run, so artifacts from the
+  # tag-triggered run are not available) and the signed universal dylib from
+  # the release is swapped in.
+  odbc-macos-upload-pkg:
+    needs: odbc-release-check
+    name: ODBC macOS Build & Upload Unsigned Universal PKG
+    runs-on: macos-26
+    if: inputs.odbc_release_step && needs.odbc-release-check.outputs.pkg != 
'true'
+    timeout-minutes: 120
+    permissions:
+      # Upload to GitHub Release
+      contents: write
+    env:
+      ARROW_BUILD_TESTS: OFF
+      ARROW_BUILD_TYPE: release
+      ARROW_DEPENDENCY_SOURCE: BUNDLED
+      ARROW_DEPENDENCY_USE_SHARED: OFF
+      ARROW_FLIGHT_SQL_ODBC: ON
+      ARROW_FLIGHT_SQL_ODBC_INSTALLER: ON
+      ARROW_HOME: /tmp/local
+      ARROW_MIMALLOC: OFF
+      MACOSX_DEPLOYMENT_TARGET: "14.0"
+    steps:
+      - name: Checkout Arrow
+        uses: actions/checkout@v7
+        with:
+          persist-credentials: false
+          fetch-depth: 0
+          submodules: recursive
+      - name: Build ODBC macOS
+        uses: ./.github/actions/odbc-mac
+        with:
+          architecture: ARM64
+          generate-installer: "true"
+      - name: Download signed universal ODBC DYLIB
+        env:
+          GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
+        run: |
+          gh release download ${GITHUB_REF_NAME} \
+            --repo ${{ github.repository }} \
+            --pattern 'libarrow_flight_sql_odbc.*.dylib' \
+            --clobber
+      - name: Build universal macOS installer
+        run: |
+          set -euo pipefail
+
+          pkg_path="$(ls build/cpp/ArrowFlightSQLODBC-*.pkg)"
+          pkg_name="$(basename "${pkg_path}" .pkg)"
+
+          # Use the ARM64 .pkg and swap in the signed universal ODBC dylib.
+          # Use sudo for every step that touches the expanded payload so the
+          # root-owned files in the PKG keep their ownership instead of being
+          # re-stamped with the runner user's uid/gid.
+          sudo pkgutil --expand-full "${pkg_path}" expanded
+
+          # The unversioned name libarrow_flight_sql_odbc.dylib is a symlink
+          # to the versioned dylib that the install name
+          # (@rpath/libarrow_flight_sql_odbc.<SOVERSION>.dylib) resolves to.
+          # Replace the real file so the symlinks stay intact.
+          symlink_path="$(find expanded -name libarrow_flight_sql_odbc.dylib)"
+          lib_dir="$(dirname "${symlink_path}")"
+          dylib_path="$(realpath "${symlink_path}")"
+          signed_dylib="$(ls libarrow_flight_sql_odbc.*.dylib)"
+          if [ "${signed_dylib}" != "$(basename "${dylib_path}")" ]; then
+            echo "Signed DYLIB ${signed_dylib} doesn't match PKG DYLIB 
$(basename "${dylib_path}")"
+            exit 1
+          fi
+          echo "Replacing ${dylib_path} (via ${symlink_path}) with 
${signed_dylib}"
+          # cp onto an existing file keeps its owner and mode.
+          sudo cp "${signed_dylib}" "${dylib_path}"
+
+          # Verify every dylib (including the symlinks) is universal before 
re-packaging.
+          for lib in "${lib_dir}"/libarrow_flight_sql_odbc*.dylib; do
+            dylib_archs="$(lipo -archs "${lib}")"
+            echo "${lib}: ${dylib_archs}"
+            for arch in arm64 x86_64; do
+              if ! grep -qw "${arch}" <<< "${dylib_archs}"; then
+                echo "${lib} is missing '${arch}' (got: ${dylib_archs})"
+                exit 1
+              fi
+            done
+          done
+
+          # --flatten-full regenerates each component's Bom and PackageInfo
+          # installKBytes, but leaves Distribution with the ARM64 base PKG's
+          # sizes. Copy the regenerated sizes into Distribution, then
+          # re-flatten without -full so the payloads are reused as-is.
+          sudo pkgutil --flatten-full expanded flattened.pkg
+          sudo pkgutil --expand flattened.pkg shallow
+          for component in shallow/*.pkg; do
+            component_name="$(basename "${component}")"
+            kbytes="$(sed -n 's/.*installKBytes="\([0-9]*\)".*/\1/p' 
"${component}/PackageInfo")"
+            sudo sed -i '' \
+              "/>#${component_name}<\/pkg-ref>/ 
s/installKBytes=\"[0-9]*\"/installKBytes=\"${kbytes}\"/" \
+              shallow/Distribution
+          done
+          mkdir universal
+          sudo pkgutil --flatten shallow "universal/${pkg_name}.pkg"
+          sudo chown "$(id -u):$(id -g)" "universal/${pkg_name}.pkg"
+      - name: Verify universal macOS installer
+        run: |
+          set -euo pipefail
+
+          pkg_path="$(ls universal/ArrowFlightSQLODBC-*.pkg)"
+          pkgutil --check-signature "${pkg_path}" || true
+          pkgutil --expand "${pkg_path}" verify
+          # Installed files must not be owned by the runner user.
+          runner_uid="$(id -u)"
+          for bom in verify/*.pkg/Bom; do
+            echo "${bom}:"
+            lsbom -p MugF "${bom}"
+            if lsbom -p u "${bom}" | grep -qx "${runner_uid}"; then
+              echo "${bom} contains files owned by the runner user (uid 
${runner_uid})"
+              exit 1
+            fi
+          done

Review Comment:
   > Can you check the file permissions final universal pkg contents and the 
installed files? Looking at the code here I think it's possible some of the 
files could end up owned by the GHA runner user.
   
   Added check here to verify. I will also need to verify on my machine once 
the GA runs on my fork finish. 



-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]

Reply via email to