]> git.ipfire.org Git - thirdparty/zlib-ng.git/commitdiff
Update s390x CI setup.
authorHans Kristian Rosbach <hk-git@circlestorm.org>
Wed, 24 Apr 2024 19:14:20 +0000 (21:14 +0200)
committerHans Kristian Rosbach <hk-github@circlestorm.org>
Fri, 17 May 2024 13:21:05 +0000 (15:21 +0200)
- New dockerfile
  - Using native actions-runner instead of relying on qemu.
  - To support s390x, we include patches to actions-runner.
  - Using Almalinux 9 instead of Ubuntu, with functional .Net.
- Update CI workflow.
- Update readme guide.

.github/workflows/cmake.yml
.github/workflows/configure.yml
arch/s390/README.md
arch/s390/self-hosted-builder/actions-runner.Dockerfile
arch/s390/self-hosted-builder/actions-runner.service
arch/s390/self-hosted-builder/qemu-user-static.service [deleted file]
arch/s390/self-hosted-builder/runner-global.json [new file with mode: 0644]
arch/s390/self-hosted-builder/runner-s390x.patch [new file with mode: 0644]

index 93ed9b19b2e36cc860e6e550e7b31118dda12d35..b4583e2a73de12c502cf85f6063007018dad0e20 100644 (file)
@@ -292,8 +292,6 @@ jobs:
             packages: qemu qemu-user gcc-s390x-linux-gnu g++-s390x-linux-gnu libc-dev-s390x-cross
             ldflags: -static
             codecov: ubuntu_gcc_s390x
-            # The dedicated test VM has 4 cores
-            parallels-jobs: 4
 
           - name: Ubuntu GCC S390X No vectorized CRC32 ASAN
             os: ubuntu-latest
@@ -302,10 +300,8 @@ jobs:
             packages: qemu qemu-user gcc-s390x-linux-gnu g++-s390x-linux-gnu libc-dev-s390x-cross
             ldflags: -static
             codecov: ubuntu_gcc_s390x_no_crc32
-            # The dedicated test VM has 4 cores
-            parallels-jobs: 4
 
-          - name: Ubuntu GCC S390X DFLTCC ASAN
+          - name: ${{ github.repository == 'zlib-ng/zlib-ng' && 'EL9' || 'Ubuntu' }} GCC S390X DFLTCC ASAN
             os: ${{ github.repository == 'zlib-ng/zlib-ng' && 'z15' || 'ubuntu-latest' }}
             compiler: gcc
             cxx-compiler: g++
@@ -315,11 +311,11 @@ jobs:
             packages: qemu qemu-user gcc-s390x-linux-gnu g++-s390x-linux-gnu libc-dev-s390x-cross
             asan-options: detect_leaks=0
             ldflags: -static
-            codecov: ubuntu_gcc_s390x_dfltcc
+            codecov: ${{ github.repository == 'zlib-ng/zlib-ng' && 'el9_gcc_s390x_dfltcc' || 'ubuntu_gcc_s390x_dfltcc' }}
             # The dedicated z15 test VM has 4 cores
             parallels-jobs: 4
 
-          - name: Ubuntu GCC S390X DFLTCC UBSAN
+          - name: ${{ github.repository == 'zlib-ng/zlib-ng' && 'EL9' || 'Ubuntu' }} GCC S390X DFLTCC UBSAN
             os: ${{ github.repository == 'zlib-ng/zlib-ng' && 'z15' || 'ubuntu-latest' }}
             compiler: gcc
             cxx-compiler: g++
@@ -328,14 +324,14 @@ jobs:
               -DWITH_DFLTCC_DEFLATE=ON -DWITH_DFLTCC_INFLATE=ON -DWITH_SANITIZER=Undefined
             packages: qemu qemu-user gcc-s390x-linux-gnu g++-s390x-linux-gnu libc-dev-s390x-cross
             ldflags: -static
-            codecov: ubuntu_gcc_s390x_dfltcc_ubsan
+            codecov: ${{ github.repository == 'zlib-ng/zlib-ng' && 'el9_gcc_s390x_dfltcc' || 'ubuntu_gcc_s390x_dfltcc' }}
             # The dedicated z15 test VM has 4 cores
             parallels-jobs: 4
 
-          - name: Ubuntu Clang S390X DFLTCC ${{ (github.repository == 'zlib-ng/zlib-ng' && 'MSAN') || 'Compat' }}
+          - name: ${{ github.repository == 'zlib-ng/zlib-ng' && 'EL9' || 'Ubuntu' }} Clang S390X DFLTCC ${{ (github.repository == 'zlib-ng/zlib-ng' && 'MSAN') || 'Compat' }}
             os: ${{ github.repository == 'zlib-ng/zlib-ng' && 'z15' || 'ubuntu-latest' }}
-            compiler: clang-11
-            cxx-compiler: clang++-11
+            compiler: ${{ github.repository == 'zlib-ng/zlib-ng' && 'clang' || 'clang-11' }}
+            cxx-compiler: ${{ github.repository == 'zlib-ng/zlib-ng' && 'clang++' || 'clang++-11' }}
             cmake-args: >-
               ${{ github.repository != 'zlib-ng/zlib-ng' && '-DCMAKE_TOOLCHAIN_FILE=cmake/toolchain-s390x.cmake -DZLIB_COMPAT=ON' || '-GNinja -DWITH_SANITIZER=Memory' }}
               -DWITH_DFLTCC_DEFLATE=ON -DWITH_DFLTCC_INFLATE=ON
index 2e6754125db44d7d860321de0b1f49ab1e141868..02ed761c43ac5d5375180ac8d11ce791560aee7e 100644 (file)
@@ -167,7 +167,7 @@ jobs:
             cflags: -static
             ldflags: -static
 
-          - name: Ubuntu GCC S390X DFLTCC
+          - name: ${{ github.repository == 'zlib-ng/zlib-ng' && 'EL9' || 'Ubuntu' }} GCC S390X DFLTCC
             os: ${{ github.repository == 'zlib-ng/zlib-ng' && 'z15' || 'ubuntu-latest' }}
             compiler: ${{ github.repository == 'zlib-ng/zlib-ng' && 'gcc' || 's390x-linux-gnu-gcc' }}
             configure-args: --warn --static --with-dfltcc-deflate --with-dfltcc-inflate
@@ -176,7 +176,7 @@ jobs:
             cflags: ${{ github.repository != 'zlib-ng/zlib-ng' && '-static' || '' }}
             ldflags: ${{ github.repository != 'zlib-ng/zlib-ng' && '-static' || '' }}
 
-          - name: Ubuntu GCC S390X DFLTCC Compat
+          - name: ${{ github.repository == 'zlib-ng/zlib-ng' && 'EL9' || 'Ubuntu' }} GCC S390X DFLTCC Compat
             os: ${{ github.repository == 'zlib-ng/zlib-ng' && 'z15' || 'ubuntu-latest' }}
             compiler: ${{ github.repository == 'zlib-ng/zlib-ng' && 'gcc' || 's390x-linux-gnu-gcc' }}
             configure-args: --warn --zlib-compat --static --with-dfltcc-deflate --with-dfltcc-inflate
index f999690dedc81d1915eee1e3f1614ad2d43e38b5..269b29737da22400b4391a538127492c485bf54f 100644 (file)
@@ -213,29 +213,31 @@ DFLTCC is a non-privileged instruction, neither special VM/LPAR
 configuration nor root are required.
 
 zlib-ng CI uses an IBM-provided z15 self-hosted builder for the DFLTCC
-testing. There are no IBM Z builds of GitHub Actions runner, and
-stable qemu-user has problems with .NET apps, so the builder runs the
-x86_64 runner version with qemu-user built from the master branch.
+testing. There is no offical IBM Z builds of GitHub Actions runner, so
+we build one inspired by `anup-kodlekere/gaplib`.
+Future updates to actions-runner might need an updated patch. The .net
+version number patch has been separated into a separate file to avoid a
+need for constantly changing the patch.
 
 ## Configuring the builder.
 
 ### Install prerequisites.
 
 ```
-$ sudo dnf install docker
+sudo dnf install podman
 ```
 
-### Add services.
+### Add actions-runner service.
 
 ```
-$ sudo cp self-hosted-builder/*.service /etc/systemd/system/
-sudo systemctl daemon-reload
+sudo cp self-hosted-builder/actions-runner.service /etc/systemd/system/
+sudo systemctl daemon-reload
 ```
 
-### Create a config file.
+### Create a config file, needs github personal access token.
 
 ```
-$ sudo tee /etc/actions-runner
+# Create file /etc/actions-runner
 repo=<owner>/<name>
 access_token=<ghp_***>
 ```
@@ -244,40 +246,32 @@ Access token should have the repo scope, consult
 https://docs.github.com/en/rest/reference/actions#create-a-registration-token-for-a-repository
 for details.
 
-### Autostart the x86_64 emulation support.
+### Autostart actions-runner.
 
 ```
-$ sudo systemctl enable --now qemu-user-static
+$ sudo systemctl enable --now actions-runner
 ```
 
-### Autostart the runner.
+## Rebuilding the container
 
+In order to update the `gaplib-actions-runner` podman container, e.g. to get the
+latest OS security fixes, follow these steps:
 ```
-$ sudo systemctl enable --now actions-runner
-```
+# Stop actions-runner service
+sudo systemctl stop actions-runner
 
-## Rebuilding the image
+# Delete old container
+sudo podman container rm gaplib-actions-runner
 
-In order to update the `iiilinuxibmcom/actions-runner` image, e.g. to get the
-latest OS security fixes, use the following commands:
+# Delete old image
+sudo podman image rm localhost/zlib-ng/actions-runner
 
-```
-$ sudo docker build \
-      --pull \
-      -f self-hosted-builder/actions-runner.Dockerfile \
-      -t iiilinuxibmcom/actions-runner
-$ sudo systemctl restart actions-runner
-```
-
-## Removing persistent data
+# Build image
+sudo podman build --squash -f Dockerfile.zlib-ng --tag zlib-ng/actions-runner --build-arg .
 
-The `actions-runner` service stores various temporary data, such as runner
-registration information, work directories and logs, in the `actions-runner`
-volume. In order to remove it and start from scratch, e.g. when switching the
-runner to a different repository, use the following commands:
+# Build container
+sudo podman create --name=gaplib-actions-runner --env-file=/etc/actions-runner --init --interactive --volume=actions-runner-temp:/home/actions-runner zlib-ng/actions-runner
 
-```
-$ sudo systemctl stop actions-runner
-$ sudo docker rm -f actions-runner
-$ sudo docker volume rm actions-runner
+# Start actions-runner service
+sudo systemctl start actions-runner
 ```
index 136eec77a58c1cca4b5f5840f82f5d01bd49a75d..cf5c3e7271b87d6aa090ed40187f2950b9852ea5 100644 (file)
@@ -1,45 +1,47 @@
 # Self-Hosted IBM Z Github Actions Runner.
 
-# Temporary image: amd64 dependencies.
-FROM amd64/ubuntu:20.04 as ld-prefix
-ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update && apt-get -y install ca-certificates libicu66 libssl1.1
-
-# Main image.
-FROM s390x/ubuntu:20.04
-
-# Packages for zlib-ng testing.
-ENV DEBIAN_FRONTEND=noninteractive
-RUN apt-get update && apt-get -y install \
-        clang-11 \
-        cmake \
-        curl \
-        gcc \
-        git \
-        jq \
-        libxml2-dev \
-        libxslt-dev \
-        llvm-11-tools \
-        ninja-build \
-        python-is-python3 \
-        python3 \
-        python3-dev \
-        python3-pip
-
-# amd64 dependencies.
-COPY --from=ld-prefix / /usr/x86_64-linux-gnu/
-RUN ln -fs ../lib/x86_64-linux-gnu/ld-linux-x86-64.so.2 /usr/x86_64-linux-gnu/lib64/
-RUN ln -fs /etc/resolv.conf /usr/x86_64-linux-gnu/etc/
-ENV QEMU_LD_PREFIX=/usr/x86_64-linux-gnu
-
-# amd64 Github Actions Runner.
-RUN useradd -m actions-runner
-USER actions-runner
-WORKDIR /home/actions-runner
-RUN curl -L https://github.com/actions/runner/releases/download/v2.287.1/actions-runner-linux-x64-2.287.1.tar.gz | tar -xz
-VOLUME /home/actions-runner
+FROM    almalinux:9
+
+RUN     dnf update -y -q && \
+        dnf install -y -q --enablerepo=crb wget git which sudo jq \
+            cmake make automake autoconf m4 libtool ninja-build python3-pip \
+            gcc gcc-c++ clang llvm-toolset glibc-all-langpacks langpacks-en \
+            glibc-static libstdc++-static libstdc++-devel libxslt-devel libxml2-devel
+
+RUN     dnf install -y -q dotnet-sdk-6.0 && \
+        echo "Using SDK - `dotnet --version`"
+
+COPY    runner-s390x.patch /tmp/runner.patch
+COPY    runner-global.json /tmp/global.json
+
+RUN     cd /tmp && \
+        git clone -q https://github.com/actions/runner && \
+        cd runner && \
+        git checkout $(git describe --tags $(git rev-list --tags --max-count=1)) -b build && \
+        git apply /tmp/runner.patch && \
+        cp -f /tmp/global.json src/global.json
+
+
+RUN     cd /tmp/runner/src && \
+        ./dev.sh layout && \
+        ./dev.sh package && \
+        rm -rf /root/.dotnet /root/.nuget
+
+RUN     useradd -c "Action Runner" -m actions-runner && \
+        usermod -L actions-runner
+
+RUN     tar -xf /tmp/runner/_package/*.tar.gz -C /home/actions-runner && \
+        chown -R actions-runner:actions-runner /home/actions-runner
+
+#VOLUME  /home/actions-runner
+
+RUN     rm -rf /tmp/runner /var/cache/dnf/* /tmp/runner.patch /tmp/global.json && \
+        dnf clean all
+
+USER    actions-runner
 
 # Scripts.
-COPY fs/ /
+COPY    fs/ /
+WORKDIR /home/actions-runner
 ENTRYPOINT ["/usr/bin/entrypoint"]
-CMD ["/usr/bin/actions-runner"]
+CMD     ["/usr/bin/actions-runner"]
index 71053a79d6e7fea77f7177e11b570731fc00e900..b6c20b65ecc6bdd13d664ddfac4cd9d1bbc1ce43 100644 (file)
@@ -1,24 +1,18 @@
 [Unit]
-Description=Self-Hosted IBM Z Github Actions Runner
-Wants=qemu-user-static
-After=qemu-user-static
-StartLimitIntervalSec=0
+Description=Podman container: Gaplib Github Actions Runner
+Wants=network-online.target
+After=network-online.target
+StartLimitIntervalSec=1
+RequiresMountsFor=/run/user/1001/containers
 
 [Service]
-Type=simple
+Environment=PODMAN_SYSTEMD_UNIT=%n
 Restart=always
-ExecStartPre=-/usr/bin/docker rm --force actions-runner
-ExecStart=/usr/bin/docker run \
-              --env-file=/etc/actions-runner \
-              --init \
-              --interactive \
-              --name=actions-runner \
-              --rm \
-              --volume=actions-runner:/home/actions-runner \
-              iiilinuxibmcom/actions-runner
-ExecStop=/bin/sh -c "docker exec actions-runner kill -INT -- -1"
-ExecStop=/bin/sh -c "docker wait actions-runner"
-ExecStop=/bin/sh -c "docker rm actions-runner"
+TimeoutStopSec=61
+ExecStart=/usr/bin/podman start gaplib-actions-runner
+ExecStop=/usr/bin/podman stop -t 1 gaplib-actions-runner
+ExecStopPost=/usr/bin/podman stop -t 1 gaplib-actions-runner
+Type=forking
 
 [Install]
-WantedBy=multi-user.target
+WantedBy=default.target
diff --git a/arch/s390/self-hosted-builder/qemu-user-static.service b/arch/s390/self-hosted-builder/qemu-user-static.service
deleted file mode 100644 (file)
index 301f3ed..0000000
+++ /dev/null
@@ -1,11 +0,0 @@
-[Unit]
-Description=Support for transparent execution of non-native binaries with QEMU user emulation
-
-[Service]
-Type=oneshot
-# The source code for iiilinuxibmcom/qemu-user-static is at https://github.com/iii-i/qemu-user-static/tree/v6.1.0-1
-# TODO: replace it with multiarch/qemu-user-static once version >6.1 is available
-ExecStart=/usr/bin/docker run --rm --interactive --privileged iiilinuxibmcom/qemu-user-static --reset -p yes
-
-[Install]
-WantedBy=multi-user.target
diff --git a/arch/s390/self-hosted-builder/runner-global.json b/arch/s390/self-hosted-builder/runner-global.json
new file mode 100644 (file)
index 0000000..e7028fe
--- /dev/null
@@ -0,0 +1,5 @@
+{
+  "sdk": {
+    "version": "6.0.421"
+  }
+}
diff --git a/arch/s390/self-hosted-builder/runner-s390x.patch b/arch/s390/self-hosted-builder/runner-s390x.patch
new file mode 100644 (file)
index 0000000..8260f3c
--- /dev/null
@@ -0,0 +1,243 @@
+diff --git a/src/Directory.Build.props b/src/Directory.Build.props
+index 9db5fac..f02e235 100644
+--- a/src/Directory.Build.props
++++ b/src/Directory.Build.props
+@@ -44,6 +44,9 @@
+   <PropertyGroup Condition="'$(BUILD_OS)' == 'Linux' AND '$(PackageRuntime)' == 'linux-arm64'">
+     <DefineConstants>$(DefineConstants);ARM64</DefineConstants>
+   </PropertyGroup>
++  <PropertyGroup Condition="'$(BUILD_OS)' == 'Linux' AND '$(PackageRuntime)' == 'linux-s390x'">
++    <DefineConstants>$(DefineConstants);S390X</DefineConstants>
++  </PropertyGroup>
+   <!-- Set TRACE/DEBUG vars -->
+   <PropertyGroup>
+diff --git a/src/Misc/externals.sh b/src/Misc/externals.sh
+index 383221e..1555f67 100755
+--- a/src/Misc/externals.sh
++++ b/src/Misc/externals.sh
+@@ -189,3 +189,8 @@ if [[ "$PACKAGERUNTIME" == "linux-arm" ]]; then
+     acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-armv7l.tar.gz" node16 fix_nested_dir
+     acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-armv7l.tar.gz" node20 fix_nested_dir
+ fi
++
++if [[ "$PACKAGERUNTIME" == "linux-s390x" ]]; then
++    acquireExternalTool "$NODE_URL/v${NODE16_VERSION}/node-v${NODE16_VERSION}-linux-s390x.tar.gz" node16 fix_nested_dir
++    acquireExternalTool "$NODE_URL/v${NODE20_VERSION}/node-v${NODE20_VERSION}-linux-s390x.tar.gz" node20 fix_nested_dir
++fi
+diff --git a/src/Misc/layoutroot/config.sh b/src/Misc/layoutroot/config.sh
+index 14cc6ba..9b5b8e6 100755
+--- a/src/Misc/layoutroot/config.sh
++++ b/src/Misc/layoutroot/config.sh
+@@ -20,25 +20,29 @@ then
+     message="Execute sudo ./bin/installdependencies.sh to install any missing Dotnet Core 6.0 dependencies."
+-    ldd ./bin/libcoreclr.so | grep 'not found'
+-    if [ $? -eq 0 ]; then
+-        echo "Dependencies is missing for Dotnet Core 6.0"
+-        echo $message
+-        exit 1
+-    fi
++    ARCH=`uname -m`
++    if [ "${ARCH}" != "s390x" -a "${ARCH}" != "ppc64le" ]
++    then
++        ldd ./bin/libcoreclr.so | grep 'not found'
++        if [ $? -eq 0 ]; then
++            echo "Dependencies is missing for Dotnet Core 6.0"
++            echo $message
++            exit 1
++        fi
+-    ldd ./bin/libSystem.Security.Cryptography.Native.OpenSsl.so | grep 'not found'
+-    if [ $? -eq 0 ]; then
+-        echo "Dependencies is missing for Dotnet Core 6.0"
+-        echo $message
+-        exit 1
+-    fi
++        ldd ./bin/libSystem.Security.Cryptography.Native.OpenSsl.so | grep 'not found'
++        if [ $? -eq 0 ]; then
++            echo "Dependencies is missing for Dotnet Core 6.0"
++            echo $message
++            exit 1
++        fi
+-    ldd ./bin/libSystem.IO.Compression.Native.so | grep 'not found'
+-    if [ $? -eq 0 ]; then
+-        echo "Dependencies is missing for Dotnet Core 6.0"
+-        echo $message
+-        exit 1
++        ldd ./bin/libSystem.IO.Compression.Native.so | grep 'not found'
++        if [ $? -eq 0 ]; then
++            echo "Dependencies is missing for Dotnet Core 6.0"
++            echo $message
++            exit 1
++        fi
+     fi
+     if ! [ -x "$(command -v ldconfig)" ]; then
+diff --git a/src/Runner.Common/Constants.cs b/src/Runner.Common/Constants.cs
+index 177e3c9..9545981 100644
+--- a/src/Runner.Common/Constants.cs
++++ b/src/Runner.Common/Constants.cs
+@@ -58,7 +58,8 @@ namespace GitHub.Runner.Common
+             X86,
+             X64,
+             Arm,
+-            Arm64
++            Arm64,
++          S390x
+         }
+         public static class Runner
+@@ -81,6 +82,8 @@ namespace GitHub.Runner.Common
+             public static readonly Architecture PlatformArchitecture = Architecture.Arm;
+ #elif ARM64
+             public static readonly Architecture PlatformArchitecture = Architecture.Arm64;
++#elif S390X
++            public static readonly Architecture PlatformArchitecture = Architecture.S390x;
+ #else
+             public static readonly Architecture PlatformArchitecture = Architecture.X64;
+ #endif
+diff --git a/src/Runner.Common/Util/VarUtil.cs b/src/Runner.Common/Util/VarUtil.cs
+index 97273a1..2a34430 100644
+--- a/src/Runner.Common/Util/VarUtil.cs
++++ b/src/Runner.Common/Util/VarUtil.cs
+@@ -53,6 +53,8 @@ namespace GitHub.Runner.Common.Util
+                         return "ARM";
+                     case Constants.Architecture.Arm64:
+                         return "ARM64";
++                    case Constants.Architecture.S390x:
++                        return "S390X";
+                     default:
+                         throw new NotSupportedException(); // Should never reach here.
+                 }
+diff --git a/src/Test/L0/ConstantGenerationL0.cs b/src/Test/L0/ConstantGenerationL0.cs
+index 2042485..a9d8b46 100644
+--- a/src/Test/L0/ConstantGenerationL0.cs
++++ b/src/Test/L0/ConstantGenerationL0.cs
+@@ -20,6 +20,7 @@ namespace GitHub.Runner.Common.Tests
+                 "linux-x64",
+                 "linux-arm",
+                 "linux-arm64",
++                "linux-s390x",
+                 "osx-x64",
+                 "osx-arm64"
+             };
+diff --git a/src/Test/L0/Listener/SelfUpdaterL0.cs b/src/Test/L0/Listener/SelfUpdaterL0.cs
+index 26ba65e..6791df3 100644
+--- a/src/Test/L0/Listener/SelfUpdaterL0.cs
++++ b/src/Test/L0/Listener/SelfUpdaterL0.cs
+@@ -1,4 +1,4 @@
+-#if !(OS_WINDOWS && ARM64)
++#if !(OS_WINDOWS && ARM64) && !S390X
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+@@ -16,6 +16,7 @@ using Xunit;
+ namespace GitHub.Runner.Common.Tests.Listener
+ {
++#if !S390X // Self-update is not currently supported on S390X
+     public sealed class SelfUpdaterL0
+     {
+         private Mock<IRunnerServer> _runnerServer;
+@@ -291,5 +292,6 @@ namespace GitHub.Runner.Common.Tests.Listener
+             }
+         }
+     }
++#endif
+ }
+ #endif
+diff --git a/src/Test/L0/Listener/SelfUpdaterV2L0.cs b/src/Test/L0/Listener/SelfUpdaterV2L0.cs
+index 5115a6b..dd8d198 100644
+--- a/src/Test/L0/Listener/SelfUpdaterV2L0.cs
++++ b/src/Test/L0/Listener/SelfUpdaterV2L0.cs
+@@ -1,4 +1,4 @@
+-#if !(OS_WINDOWS && ARM64)
++#if !(OS_WINDOWS && ARM64) && !S390X
+ using System;
+ using System.Collections.Generic;
+ using System.IO;
+diff --git a/src/Test/L0/Worker/StepHostL0.cs b/src/Test/L0/Worker/StepHostL0.cs
+index f6b5889..26f8e21 100644
+--- a/src/Test/L0/Worker/StepHostL0.cs
++++ b/src/Test/L0/Worker/StepHostL0.cs
+@@ -31,7 +31,7 @@ namespace GitHub.Runner.Common.Tests.Worker
+             return hc;
+         }
+-#if OS_LINUX
++#if OS_LINUX && !S390X
+         [Fact]
+         [Trait("Level", "L0")]
+         [Trait("Category", "Worker")]
+diff --git a/src/dev.sh b/src/dev.sh
+index fa637d1..8c66f37 100755
+--- a/src/dev.sh
++++ b/src/dev.sh
+@@ -54,6 +54,7 @@ elif [[ "$CURRENT_PLATFORM" == 'linux' ]]; then
+         case $CPU_NAME in
+             armv7l) RUNTIME_ID="linux-arm";;
+             aarch64) RUNTIME_ID="linux-arm64";;
++            s390x) RUNTIME_ID="linux-s390x";;
+         esac
+     fi
+ elif [[ "$CURRENT_PLATFORM" == 'darwin' ]]; then
+@@ -80,7 +81,7 @@ if [[ "$CURRENT_PLATFORM" == 'windows' ]]; then
+         exit 1
+     fi
+ elif [[ "$CURRENT_PLATFORM" == 'linux' ]]; then
+-    if [[ ("$RUNTIME_ID" != 'linux-x64') && ("$RUNTIME_ID" != 'linux-x86') && ("$RUNTIME_ID" != 'linux-arm64') && ("$RUNTIME_ID" != 'linux-arm') ]]; then
++      if [[ ("$RUNTIME_ID" != 'linux-x64') && ("$RUNTIME_ID" != 'linux-x86') && ("$RUNTIME_ID" != 'linux-arm64') && ("$RUNTIME_ID" != 'linux-arm')  && ("$RUNTIME_ID" != 'linux-s390x') ]]; then
+        echo "Failed: Can't build $RUNTIME_ID package $CURRENT_PLATFORM" >&2
+        exit 1
+     fi
+@@ -199,7 +200,8 @@ function package ()
+     popd > /dev/null
+ }
+-if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}") || (! -e "${DOTNETSDK_INSTALLDIR}/dotnet") ]]; then
++if [[ "${RUNTIME_ID}" != "linux-s390x" && ((! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}") || (! -e "${DOTNETSDK_INSTALLDIR}/dotnet")) ]]; then
++
+     # Download dotnet SDK to ../_dotnetsdk directory
+     heading "Ensure Dotnet SDK"
+@@ -224,8 +226,10 @@ if [[ (! -d "${DOTNETSDK_INSTALLDIR}") || (! -e "${DOTNETSDK_INSTALLDIR}/.${DOTN
+     echo "${DOTNETSDK_VERSION}" > "${DOTNETSDK_INSTALLDIR}/.${DOTNETSDK_VERSION}"
+ fi
+-echo "Prepend ${DOTNETSDK_INSTALLDIR} to %PATH%"
+-export PATH=${DOTNETSDK_INSTALLDIR}:$PATH
++if [[ -d "${DOTNETSDK_INSTALLDIR}" ]]; then
++    echo "Prepend ${DOTNETSDK_INSTALLDIR} to %PATH%"
++    export PATH=${DOTNETSDK_INSTALLDIR}:$PATH
++fi
+ heading "Dotnet SDK Version"
+ dotnet --version
+diff --git a/src/dir.proj b/src/dir.proj
+index 056a312..8370922 100644
+--- a/src/dir.proj
++++ b/src/dir.proj
+@@ -41,8 +41,18 @@
+     </ItemGroup>
+     <Target Name="Build" DependsOnTargets="GenerateConstant">
+-        <MSBuild Targets="Restore" Projects="@(ProjectFiles)" StopOnFirstFailure="true" />
+-        <MSBuild Targets="Publish" Projects="@(ProjectFiles)" BuildInParallel="false" StopOnFirstFailure="true" Properties="Configuration=$(BUILDCONFIG);PackageRuntime=$(PackageRuntime);Version=$(RunnerVersion);RuntimeIdentifier=$(PackageRuntime);PublishDir=$(MSBuildProjectDirectory)/../_layout/bin" />
++        <PropertyGroup>
++            <!-- Normally we want to publish a self-contained app for $(PackageRuntime) -->
++            <PublishRuntimeIdentifier>RuntimeIdentifier=$(PackageRuntime)</PublishRuntimeIdentifier>
++            <!-- However, on s390x there are no apphost or runtime packages on nuget.org, so self-contained publishing is not supported.
++                 Perform a non-self-contained publish using the current runtime identifier (normally something like rhel.8-s390x) instead.
++                 In addition, when not using an explicit runtime identifier, the SDK will copy runtime assets from dependent packages;
++                 as this would confuse the expected layout, disable that behavior as well.  -->
++            <PublishRuntimeIdentifier Condition="'$(PackageRuntime)' == 'linux-s390x'">SelfContained=false;CopyLocalRuntimeTargetAssets=false</PublishRuntimeIdentifier>
++        </PropertyGroup>
++
++        <MSBuild Targets="Restore" Projects="@(ProjectFiles)" StopOnFirstFailure="true" Properties="$(PublishRuntimeIdentifier)" />
++        <MSBuild Targets="Publish" Projects="@(ProjectFiles)" BuildInParallel="false" StopOnFirstFailure="true" Properties="Configuration=$(BUILDCONFIG);PackageRuntime=$(PackageRuntime);Version=$(RunnerVersion);$(PublishRuntimeIdentifier);PublishDir=$(MSBuildProjectDirectory)/../_layout/bin" />
+         <Exec Command="%22$(DesktopMSBuild)%22 Runner.Service/Windows/RunnerService.csproj /p:Configuration=$(BUILDCONFIG) /p:PackageRuntime=$(PackageRuntime) /p:OutputPath=%22$(MSBuildProjectDirectory)/../_layout/bin%22" ConsoleToMSBuild="true" Condition="'$(PackageRuntime)' == 'win-x64' Or '$(PackageRuntime)' == 'win-x86' Or '$(PackageRuntime)' == 'win-arm64'" />
+     </Target>
+