Add CI for Switch port

This commit is contained in:
biroder
2026-08-16 20:26:30 +00:00
parent 4120fb6560
commit 95e7af107f
5 changed files with 229 additions and 22 deletions
+92 -3
View File
@@ -4,14 +4,13 @@ on:
push:
branches-ignore:
- cpp-rewrite
- horizon-os
- refactor
paths-ignore:
- ".gitignore"
- ".github/*"
- "**.md"
- "LICENSE"
- "drshorizon/**"
- "misc/**"
- "res/**"
workflow_dispatch:
release:
@@ -260,11 +259,87 @@ jobs:
drsandroid/target
key: android-cargo
build_horizon:
name: Horizon build
runs-on: ubuntu-latest
env:
RUSTSWITCH_IMAGE: "ghcr.io/doukutsu-rs/drshorizon-build-env:latest"
strategy:
fail-fast: true
matrix:
include:
- name: Horizon
os: ubuntu-latest
channel: stable
steps:
- uses: actions/checkout@v7
- name: Restore cache
uses: actions/cache/restore@v6
with:
path: |
~/.cache
~/.cargo
drshorizon/target
key: horizon-cargo
- name: Set version
id: set_version
run: |
if [ "${{ github.ref_type }}" == "tag" ]; then
echo "version=${{ github.ref_name }}" >> $GITHUB_OUTPUT
echo "channel=stable" >> $GITHUB_OUTPUT
elif [ "${{ github.ref_name }}" == "master" ]; then
echo "version=${{ env.VERSION }}-$((${{ github.run_number }} + 654))" >> $GITHUB_OUTPUT
echo "channel=nightly" >> $GITHUB_OUTPUT
else
echo "version=${{ env.VERSION }}-${GITHUB_SHA:0:7}" >> $GITHUB_OUTPUT
echo "channel=nightly" >> $GITHUB_OUTPUT
fi
- name: Build
run: |
if [ -z "${{ steps.set_version.outputs.version }}" ]; then
echo "version is not set"
exit 1
fi
export DRS_BUILD_VERSION_OVERRIDE="${{ steps.set_version.outputs.version }}"
export RUSTSWITCH_IMAGE="${{ env.RUSTSWITCH_IMAGE }}"
cd drshorizon
./build.sh -r --ci --docker-mode compose
- name: Prepare artifact
run: |
mkdir release
# Artifacts generated during build in the container are owned by root,
# so we cannot move the generated file as we do for other platforms
cp drshorizon/target/aarch64-nintendo-switch/release/drshorizon.nro release/doukutsu-rs.nro
cp LICENSE ./release
- name: Upload artifact
uses: actions/upload-artifact@v7
with:
name: doukutsu-rs_horizon
path: ./release/*
if-no-files-found: error
- name: Save cache
if: ${{ github.ref_name == 'master' || github.ref_type == 'tag' }}
uses: actions/cache/save@v6
with:
path: |
~/.cache
~/.cargo
drshorizon/target
key: horizon-cargo
update_metadata:
name: Update metadata
runs-on: ubuntu-latest
if: ${{ github.ref_name == 'master' || github.ref_type == 'tag' }}
needs: [build, build_android]
needs: [build, build_android, build_horizon]
permissions:
contents: write
env:
@@ -390,6 +465,20 @@ jobs:
node ./misc/ci/uploadBuild.js update --channel ${{ steps.set_version.outputs.channel }} --version "${{ steps.set_version.outputs.version }}" --platform android --arch universal --commit $GITHUB_SHA --file "temp-android/doukutsu-rs.apk"
cp ./temp-android/doukutsu-rs.apk "./release-binaries/doukutsu-rs_android_${{ steps.set_version.outputs.version }}.universal.apk"
# Horizon build
- name: Download Horizon artifact
if: ${{ needs.build_horizon.result == 'success' }}
uses: actions/download-artifact@v8
with:
name: doukutsu-rs_horizon
path: temp-horizon
- name: Process Horizon build
if: ${{ needs.build_horizon.result == 'success' }}
run: |
node ./misc/ci/uploadBuild.js update --channel ${{ steps.set_version.outputs.channel }} --version "${{ steps.set_version.outputs.version }}" --platform horizon --arch arm64 --commit $GITHUB_SHA --file "temp-horizon/doukutsu-rs.nro" --experimental true
cp ./temp-horizon/doukutsu-rs.nro "./release-binaries/doukutsu-rs_horizon_${{ steps.set_version.outputs.version }}.arm64.nro"
- name: Upload binaries to release
uses: svenstaro/upload-release-action@v2
if: ${{ github.ref_type == 'tag' }}
+9 -5
View File
@@ -1,5 +1,11 @@
FROM devkitpro/devkita64:20260219
# We set the "maintainer" label just to override the value inherited from the devkitPro image
LABEL maintainer="doukutsu-rs maintainers" \
org.opencontainers.image.authors="doukutsu-rs maintainers" \
org.opencontainers.image.description="Build environment for the Horizon port of doukutsu-rs." \
org.opencontainers.image.source="https://github.com/doukutsu-rs/doukutsu-rs"
# Setup Rust environment vars
ENV RUSTUP_HOME=/usr/local/rustup \
CARGO_HOME=/usr/local/cargo \
@@ -10,13 +16,11 @@ ENV RUSTUP_HOME=/usr/local/rustup \
RUST_DEFAULT_TOOLCHAIN=nightly
# Install build dependencies
RUN \
apt update; \
RUN apt update; \
apt install -y clang libclang-dev zstd;
# Install Rust
RUN \
set -eux; \
RUN set -eux; \
curl --proto '=https' --tlsv1.2 -fSL https://sh.rustup.rs | sh -s -- --default-toolchain $RUST_DEFAULT_TOOLCHAIN --profile minimal -c rust-src -y; \
chmod -R a+w $RUSTUP_HOME $CARGO_HOME; \
rustup default $RUST_DEFAULT_TOOLCHAIN;
@@ -26,7 +30,7 @@ RUN \
curl -fSL https://github.com/doukutsu-rs/rust-hos/releases/download/${RUST_VERSION}-horizon/${RUST_VERSION}-switch_x86_64-unknown-linux-gnu.tar.zst | \
tar -x --zstd -f - -C ${RUSTUP_HOME}/toolchains/;
# For testing local toolchains
# For testing local toolchains (comment the previous `RUN` instruction)
# COPY ${RUST_VERSION}-switch ${RUSTUP_HOME}/toolchains/${RUST_VERSION}-switch
WORKDIR /workspace/drshorizon
+48 -4
View File
@@ -8,9 +8,12 @@ ld script and `.specs` taken from devkitPro (the linker script is modified).
# Build
More detailed build instructions can be found in the [docs](https://doukutsu-rs.gitbook.io/docs/modders-handbook/initial-setup-and-compiling/building-the-ports#nintendo-switch-horizon).
## Dependencies
## Dependencies (for local build)
> [!TIP]
> If you're going to build the port in a Docker container, the only dependency is Docker itself. All the dependencies listed below are required for compiling the port on the host machine.
- Rust toolchain with the Switch patches ([link](https://github.com/doukutsu-rs/rust-hos/releases))
- Rust nightly toolchain (the `nightly` channel). The `cargo` binary will be used from it, since the patched toolchain miss it. Any other toolchain, even if it's nightly (like `nightly-2025-10-24`), won't work
- Rust nightly toolchain (the `nightly` channel). The `cargo` binary will be loaded from it, since the patched toolchain miss it. Any other toolchain, even if it's nightly (like `nightly-2025-10-24`), won't work
- Switch toolchain by [devkitPro](https://devkitpro.org/wiki/Getting_Started#Setup) (install the `switch-dev` package)
- `libclang-dev` (on Debian; for other distros, find a package that provides `libclang.so`)
- Clang (it provides some header files, such as `stddef.h`)
@@ -45,12 +48,53 @@ This will build the port in a Docker container. The cargo cache and project dire
If you have Docker Compose installed, the script will run it using config params from `composer.yaml`. The created container will be removed on exit.
Otherwise the script will build the container image with `docker build` and run it via `docker run`.
Otherwise the script will build the container image with `docker build` and run it via `docker run`. If you want to use precompiled docker image, specify its name in the `RUSTSWITCH_IMAGE` environment variable (i.e. `ghcr.io/doukutsu-rs/drshorizon-build-env:latest`).
If you want to force build via Docker Compose, specify `--docker-mode`:
```
./build.sh --docker-mode compose
```
# Notes for maintainers
1. The dependency on Clang is caused by the `deko3d-sys` crate, which ignores the presence of GCC/glibc headers.
2. The toolchain intended for use in a container should be built on a Debian Bookworm virtual machine, since this is the version used by the devkitPro container.
2. The toolchain intended for use in a container should be built on a Debian Bookworm virtual machine, since this is the version used by the devkitPro image.
Building the toolchain on newer systems will result in the toolchains libraries being linked against a newer version of glibc than the one present in the container, which in turn will make it impossible to build the port using this toolchain, as it will crash with a segfault.
Attempting to build the toolchain inside the container results either in compilation errors or in the generation of artifacts that cannot be run/loaded properly (they crash with core dump).
## Maintaining Docker image
Since the CI workflow for automatically building the Docker image and uploading it to the registry isn't configured yet, you'll need to do this manually.
### Versioning
The Docker image version is independent of the version of doukutsu-rs and the Horizon port.
The **major version number** should be incremented only if the introduced changes make it impossible to compile previous versions with the updated image.
The **minor version number** should be incremented when updating container dependencies (the base image or a patched toolset), but previous versions of the port should still be compilable.
The **patch number** may be incremented when some metadata is changed or when other minor changes are made.
### Building image
1. Ensure that [Docker Buildx plugin](https://github.com/docker/buildx#installing) is installed.
2. Enter directory where the Dockerfile is located (currently it's `drshorizon`).
3. Build the image and link it to the `latest` tag (replace `${VERSION}` with the new version of the image):
```
docker buildx build --provenance=false \
-t ghcr.io/doukutsu-rs/drshorizon-build-env:latest \
-t ghcr.io/doukutsu-rs/drshorizon-build-env:${VERSION} .
```
The `--provencance=false` argument disables generation of provenance attestation, since this attestation generates useless `unknown/unknown` platform on Github Packages page of the image.
### Pushing image to registry
1. Generate personal access token (classic) with at least `write:packages` permission.
2. Authenticate into the Github Containers registry with your Github username and the access token:
```
docker login ghcr.io
```
3. Push the image (replace `${VERSION}` with the new version of the image):
```
docker push ghcr.io/doukutsu-rs/drshorizon-build-env:${VERSION}
docker push ghcr.io/doukutsu-rs/drshorizon-build-env:latest
```
+79 -10
View File
@@ -3,12 +3,24 @@
cd "$(dirname "$0")" || exit
set -e
# In CI environments the $TERM var may be not set, but it's required for `tput`
if [ -z $TERM ] || [ "${TERM}" = "dumb" ]; then
export TERM=xterm
fi
# Base version (in the major.minor.patch format, without any suffixes)
BASE_VERSION="1.0.0"
# Strip suffix after -
VERSION=${DRS_BUILD_VERSION_OVERRIDE%%-*}
VERSION=${VERSION:-BASE_VERSION}
DARK_GRAY=$(tput setaf 8)
YELLOW=$(tput bold)$(tput setaf 3)
RESET=$(tput sgr0)
DIVIDER="===================="
function message() {
echo "${DARK_GRAY}----${RESET} ${YELLOW}$*${RESET}"
echo "${DARK_GRAY}----${RESET} ${YELLOW}$*${RESET}"
}
function is_bin_avail() {
@@ -22,20 +34,28 @@ function is_bin_avail() {
set -e
}
# Parse arguments
BUILD_MODE="debug"
CARGO_FLAGS=""
FLAGS=""
IS_CI=false
IS_DOCKER=false
DOCKER_MODE="auto"
# Check if Docker Compose is available
IS_COMPOSE_AVAIL=$(is_bin_avail "docker-compose")
function print_help() {
echo -e "$(basename "$0") [-h] [-r] [-V] [<cargo args>...]\n"
echo -e "$(basename $BASH_SOURCE) [-h] [-r] [-V] [<cargo args>...]\n"
echo -e "Build script for the doukutsu-rs Horizon port.\n"
echo "Arguments:"
echo " --ci Mark that the script is running in CI environment."
echo " --docker Run build in the Docker container."
echo " --docker-mode Run build via a specific tool: 'docker', 'compose', or 'auto'."
echo " Default value: 'auto'."
echo ""
echo " -r, --release Build in release mode."
echo " -V, --version Print version of build tools."
echo " -?, -h, --help Prints help message."
@@ -44,10 +64,21 @@ function print_help() {
function print_mode() {
echo "Build mode: ${BUILD_MODE}"
if $IS_DOCKER && $IS_COMPOSE_AVAIL; then
echo -e "Running via Docker Compose\n"
echo -n "Running via Docker Compose"
elif $IS_DOCKER; then
echo -e "Running via Docker\n"
echo -n "Running via Docker"
fi
if [ "$DOCKER_MODE" != "auto" ]; then
echo -n " (forcely)"
fi
echo -n -e "\n"
if $IS_CI; then
echo -e "Environment: CI\n"
fi
echo $DIVIDER
}
function print_version() {
@@ -72,7 +103,7 @@ function print_version() {
echo -e "\n===DOCKER==="
docker --version
if $(is_bin_avail "docker-compose"); then
if $IS_COMPOSE_AVAIL; then
docker-compose --version
fi
fi
@@ -83,22 +114,58 @@ function run_docker() {
if $IS_COMPOSE_AVAIL; then
docker compose run --rm --remove-orphans rust-switch ./build.sh $FLAGS
else
docker build -t rust-switch .
if [ -z $RUSTSWITCH_IMAGE ]; then
echo "Building Docker image from the scratch"
RUSTSWITCH_IMAGE=rust-switch
docker build -t $RUSTSWITCH_IMAGE .
else
echo "Using prebuilt docker image: $RUSTSWITCH_IMAGE"
fi
PROJECT_DIR="$(dirname $PWD)"
docker run -it \
-v "../:/workspace" \
-v "${PROJECT_DIR}":/workspace \
-v "${CARGO_HOME:-${HOME}/.cargo}/registry:/usr/local/cargo/registry" \
-v "${CARGO_HOME:-${HOME}/.cargo}/git:/usr/local/cargo/git" \
rust-switch \
./build.sh $FLAGS
$RUSTSWITCH_IMAGE \
$BASH_SOURCE \
$FLAGS
fi
}
while [[ $# -gt 0 ]]; do
case $1 in
--ci)
IS_CI=true
shift
;;
--docker)
IS_DOCKER=true
shift
;;
--docker-mode)
case $2 in
auto)
;;
compose)
DOCKER_MODE="compose"
IS_DOCKER=true
IS_COMPOSE_AVAIL=true
;;
docker)
DOCKER_MODE="docker"
IS_DOCKER=true
IS_COMPOSE_AVAIL=false
;;
*)
echo "Invalid value provided for argument \"--docker-mode\": $2" >&2
exit 1
;;
esac
shift
shift
;;
-r|--release)
BUILD_MODE="release"
CARGO_FLAGS+=" -r"
@@ -127,6 +194,8 @@ if $IS_DOCKER; then
exit 0
fi
# Building the port
message "Compiling shaders..."
uam -s vert -o ../src/framework/shaders/deko3d/vertex_basic.dksh ../src/framework/shaders/deko3d/vertex_basic.glsl
@@ -140,7 +209,7 @@ rm -f target/aarch64-nintendo-switch/$BUILD_MODE/drshorizon.nro
rm -f target/aarch64-nintendo-switch/$BUILD_MODE/drshorizon.nacp
message "Creating NACP..."
nacptool --create 'doukutsu-rs' 'doukutsu-rs contributors' '1.0.0' target/aarch64-nintendo-switch/$BUILD_MODE/drshorizon.nacp
nacptool --create 'doukutsu-rs' 'doukutsu-rs contributors' $VERSION target/aarch64-nintendo-switch/$BUILD_MODE/drshorizon.nacp
message "Running elf2nro..."
elf2nro target/aarch64-nintendo-switch/$BUILD_MODE/drshorizon.elf target/aarch64-nintendo-switch/$BUILD_MODE/drshorizon.nro \
+1
View File
@@ -1,5 +1,6 @@
services:
rust-switch:
image: ${RUSTSWITCH_IMAGE:-ghcr.io/doukutsu-rs/drshorizon-build-env:1.0.0}
build:
context: .