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
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++
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++
-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
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
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
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_***>
```
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
```
# 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"]
[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
+++ /dev/null
-[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
--- /dev/null
+{
+ "sdk": {
+ "version": "6.0.421"
+ }
+}
--- /dev/null
+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>
+