Subject: [PATCH v2 12/17] github workflows: define rust versions and targets in the same place

From: "Ezekiel Newren via GitGitGadget" <[email-masked]>
Date: Fri, 15 Aug 2025 01:22:47 +0000

From: Ezekiel Newren <[email-masked]>

Consolidate the Rust toolchain definitions in main.yaml. Prefer using
actions-rs/toolchain@v1 where possible, but for docker targets use
a script to install the Rust toolchain. Four overrides are used in
main.yaml:

  * On Windows: Rust didn't resolve the bcrypt library on Windows
    correctly until version 1.78.0. Also since rustup mis-identifies
    the Rust toolchain, the Rust target triple must be set to
    x86_64-pc-windows-gnu.
  * On musl: libc differences, such as ftruncate64 vs ftruncate, were
    not accounted for until Rust version 1.72.0. No older version of
    Rust will work on musl for our needs.
  * In a 32-bit docker container running on a 64-bit host, we need to
    override the Rust target triple. This is because rustup asks the
    kernel for the bitness of the system and it says 64, even though
    the container will only run 32-bit. This also allows us to remove
    the BITNESS environment variable in ci/lib.sh.

Signed-off-by: Ezekiel Newren <[email-masked]>
---
 .github/workflows/main.yml | 34 +++++++++++++++++++++++++++++++++-
 build_rust.sh              | 11 +++--------
 ci/install-rust.sh         | 33 +++++++++++++++++----------------
 ci/lib.sh                  |  7 -------
 ci/make-test-artifacts.sh  | 12 ++++++------
 ci/run-build-and-tests.sh  |  8 +++++---
 meson.build                |  1 +
 7 files changed, 65 insertions(+), 41 deletions(-)
 mode change 100644 => 100755 ci/install-rust.sh

diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index aa18742f08c4..ef4d6348edcd 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -4,7 +4,6 @@ on: [push, pull_request]
 
 env:
   DEVELOPER: 1
-  RUST_VERSION: 1.87.0
 
 # If more than one workflow run is triggered for the very same commit hash
 # (which happens when multiple branches pointing to the same commit), only
@@ -27,6 +26,12 @@ jobs:
     outputs:
       enabled: ${{ steps.check-ref.outputs.enabled }}${{ steps.skip-if-redundant.outputs.enabled }}
       skip_concurrent: ${{ steps.check-ref.outputs.skip_concurrent }}
+      rust_version_minimum: 1.61.0
+      rust_version_windows: 1.78.0
+      rust_version_musl: 1.72.0
+      ## the rust target is inferred by rustup unless specified
+      rust_target_windows: x86_64-pc-windows-gnu
+      rust_target_32bit_linux: i686-unknown-linux-gnu
     steps:
       - name: try to clone ci-config branch
         run: |
@@ -123,11 +128,19 @@ jobs:
           /c/Program\ Files/Git/mingw64/bin/curl -Lo libuserenv.a \
             https://github.com/git-for-windows/git-sdk-64/raw/HEAD/mingw64/lib/libuserenv.a
         }
+    - name: Install Rust
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: ${{ needs.ci-config.outputs.rust_version_windows }}
+        target: ${{ needs.ci-config.outputs.rust_target_windows }}
+        profile: minimal
+        override: true
     - name: build
       shell: bash
       env:
         HOME: ${{runner.workspace}}
         NO_PERL: 1
+        CARGO_HOME: "/c/Users/runneradmin/.cargo"
       run: . /etc/profile && ci/make-test-artifacts.sh artifacts
     - name: zip up tracked files
       run: git archive -o artifacts/tracked.tar.gz HEAD
@@ -269,6 +282,13 @@ jobs:
     steps:
     - uses: actions/checkout@v4
     - uses: actions/setup-python@v5
+    - name: Install Rust
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: ${{ needs.ci-config.outputs.rust_version_windows }}
+        target: ${{ needs.ci-config.outputs.rust_target_windows }}
+        profile: minimal
+        override: true
     - name: Set up dependencies
       shell: pwsh
       run: pip install meson ninja
@@ -342,6 +362,12 @@ jobs:
     steps:
     - uses: actions/checkout@v4
     - run: ci/install-dependencies.sh
+    - name: Install Rust
+      uses: actions-rs/toolchain@v1
+      with:
+        toolchain: ${{ needs.ci-config.outputs.rust_version_minimum }}
+        profile: minimal
+        override: true
     - run: ci/run-build-and-tests.sh
     - name: print test failures
       if: failure() && env.FAILED_TEST_ARTIFACTS != ''
@@ -402,9 +428,11 @@ jobs:
           cc: gcc
         - jobname: linux-musl-meson
           image: alpine:latest
+          rust_version_override: ${{ needs.ci-config.outputs.rust_version_musl }}
         # Supported until 2025-04-02.
         - jobname: linux32
           image: i386/ubuntu:focal
+          rust_target_override: ${{ needs.ci-config.outputs.rust_target_32bit_linux }}
         - jobname: pedantic
           image: fedora:latest
         # A RHEL 8 compatible distro.  Supported until 2029-05-31.
@@ -417,7 +445,11 @@ jobs:
       jobname: ${{matrix.vector.jobname}}
       CC: ${{matrix.vector.cc}}
       CI_JOB_IMAGE: ${{matrix.vector.image}}
+      CI_IS_DOCKER: "true"
       CUSTOM_PATH: /custom
+      RUST_VERSION: ${{ matrix.vector.rust_version_override || needs.ci-config.outputs.rust_version_minimum }}
+      RUST_TARGET: ${{ matrix.vector.rust_target_override || '' }}
+      CARGO_HOME: /home/builder/.cargo
     runs-on: ubuntu-latest
     container: ${{matrix.vector.image}}
     steps:
diff --git a/build_rust.sh b/build_rust.sh
index 694d48d857a5..80ce7eae3b00 100755
--- a/build_rust.sh
+++ b/build_rust.sh
@@ -1,13 +1,8 @@
 #!/bin/sh
 
-if [ -z "$CARGO_HOME" ]; then
-  export CARGO_HOME=$HOME/.cargo
-  echo >&2 "::warning:: CARGO_HOME is not set"
-fi
-echo "CARGO_HOME=$CARGO_HOME"
 
-rustc -vV
-cargo --version
+rustc -vV || exit $?
+cargo --version || exit $?
 
 dir_git_root=${0%/*}
 dir_build=$1
@@ -55,7 +50,7 @@ dst=$dir_build/$libfile
 if [ "$dir_git_root" != "$dir_build" ]; then
   src=$dir_rust/target/$rust_target/$libfile
   if [ ! -f $src ]; then
-    echo >&2 "::error:: cannot find path of static library"
+    echo >&2 "::error:: cannot find path of static library $src is not a file or does not exist"
     exit 5
   fi
 
diff --git a/ci/install-rust.sh b/ci/install-rust.sh
old mode 100644
new mode 100755
index c22baa629ceb..133aa8cc878a
--- a/ci/install-rust.sh
+++ b/ci/install-rust.sh
@@ -1,36 +1,37 @@
 #!/bin/sh
 
+## github workflows actions-rs/toolchain@v1 doesn't work for docker
+## targets. This script should only be used if the ci pipeline
+## doesn't support installing rust on a particular target.
+
 if [ "$(id -u)" -eq 0 ]; then
   echo >&2 "::warning:: installing rust as root"
 fi
 
-if [ "$CARGO_HOME" = "" ]; then
-  echo >&2 "::warning:: CARGO_HOME is not set"
-  export CARGO_HOME=$HOME/.cargo
-fi
-
-export RUSTUP_HOME=$CARGO_HOME
-
 if [ "$RUST_VERSION" = "" ]; then
   echo >&2 "::error:: RUST_VERSION is not set"
+  exit 1
+fi
+
+if [ "$CARGO_HOME" = "" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
   exit 2
 fi
 
+export RUSTUP_HOME=$CARGO_HOME
+
 ## install rustup
 curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- --default-toolchain none -y
 if [ ! -f $CARGO_HOME/env ]; then
   echo "PATH=$CARGO_HOME/bin:\$PATH" > $CARGO_HOME/env
 fi
+. $CARGO_HOME/env
+
 ## install a specific version of rust
-if [ "$BITNESS" = "32" ]; then
-  $CARGO_HOME/bin/rustup set default-host i686-unknown-linux-gnu || exit $?
-  $CARGO_HOME/bin/rustup install $RUST_VERSION || exit $?
-  $CARGO_HOME/bin/rustup default --force-non-host $RUST_VERSION || exit $?
+if [ "$RUST_TARGET" != "" ]; then
+  rustup default --force-non-host "$RUST_VERSION-$RUST_TARGET" || exit $?
 else
-  $CARGO_HOME/bin/rustup default $RUST_VERSION || exit $?
-  if [ "$CI_OS_NAME" = "windows" ]; then
-    $CARGO_HOME/bin/rustup target add x86_64-pc-windows-gnu || exit $?
-  fi
+  rustup default "$RUST_VERSION" || exit $?
 fi
 
-. $CARGO_HOME/env
+rustc -vV || exit $?
diff --git a/ci/lib.sh b/ci/lib.sh
index ad0e49a68dcb..a7992b22fdc9 100755
--- a/ci/lib.sh
+++ b/ci/lib.sh
@@ -1,13 +1,6 @@
 # Library of functions shared by all CI scripts

-export BITNESS="64"
-if command -v getconf >/dev/null && [ "$(getconf LONG_BIT 2>/dev/null)" = "32" ]; then
-  export BITNESS="32"
-fi
-echo "BITNESS=$BITNESS"
-
-
 if test true = "$GITHUB_ACTIONS"
 then
 	begin_group () {
diff --git a/ci/make-test-artifacts.sh b/ci/make-test-artifacts.sh
index 56aa7efb1d53..70d0dfbc0e8b 100755
--- a/ci/make-test-artifacts.sh
+++ b/ci/make-test-artifacts.sh
@@ -7,13 +7,13 @@ mkdir -p "$1" # in case ci/lib.sh decides to quit early
 
 . ${0%/*}/lib.sh
 
-## install rust per user rather than system wide
-. ${0%/*}/install-rust.sh
+if [ -z "$CARGO_HOME" ]; then
+  echo >&2 "::error:: CARGO_HOME is not set"
+  exit 1
+fi
 
-group Build make artifacts-tar ARTIFACTS_DIRECTORY="$1"
+export PATH="$CARGO_HOME/bin:$PATH"
 
-if [ -d "$CARGO_HOME" ]; then
-  rm -rf $CARGO_HOME
-fi
+group Build make artifacts-tar ARTIFACTS_DIRECTORY="$1"
 
 check_unignored_build_artifacts
diff --git a/ci/run-build-and-tests.sh b/ci/run-build-and-tests.sh
index dbab1cb2f936..0f1f704d583e 100755
--- a/ci/run-build-and-tests.sh
+++ b/ci/run-build-and-tests.sh
@@ -5,10 +5,12 @@
 
 . ${0%/*}/lib.sh
 
-## install rust per user rather than system wide
-. ${0%/*}/install-rust.sh
+## actions-rs/toolchain@v1 doesn't work for docker targets.
+if [ "$CI_IS_DOCKER" = "true" ]; then
+  . ${0%/*}/install-rust.sh
+fi
 
-rustc -vV
+rustc -vV || exit $?
 cargo --version || exit $?
 
 run_tests=t
diff --git a/meson.build b/meson.build
index af015f04763f..a2f9f063bef2 100644
--- a/meson.build
+++ b/meson.build
@@ -293,6 +293,7 @@ rust_build_xdiff = custom_target('rust_build_xdiff',
     meson.project_source_root() / 'build_rust.sh',
     meson.current_build_dir(), rust_target, 'xdiff',
   ],
+  env: script_environment,
   install: false,
 )
 
-- 
gitgitgadget