.hearthforge-ci.toml
⎇
Raw
1# Steps run in file order, in one container, sharing /ci/build.
2# Same image as the Containerfile's build stage, so the binary links against
3# the glibc of the trixie runtime image.
4
5image = "docker.io/library/rust:1-trixie"
6work_dir = "/ci/build"
7clone_project_to = "/ci/build/project"
8shell_setup = """
9set -euo pipefail
10export CARGO_TARGET_DIR=/ci/cache/target
11export ANDROID_HOME=/ci/cache/android-sdk
12"""
13
14timeout = 3600
15memory_limit = "6g"
16
17# CARGO_TARGET_DIR must stay outside clone_project_to: the checkout is
18# extracted over that directory.
19cache = [
20 { path = "/ci/cache/target", max_size = "16g" },
21 { path = "/usr/local/cargo/registry", max_size = "4g" },
22 # trunk downloads wasm-opt here.
23 { path = "/root/.cache/trunk", max_size = "1g" },
24 { path = "/ci/cache/android-sdk", max_size = "3g" },
25 { path = "/root/.gradle", max_size = "4g" },
26]
27
28[on]
29push = ["master"]
30tag = true
31
32[variables]
33
34 [variables.TRUNK_VERSION]
35 default = "0.21.14"
36 description = "Trunk release that builds the wasm frontend. Matches the Containerfile."
37
38[[steps]]
39name = "setup"
40timeout = 900
41run_sh = """
42apt-get update -qq && apt-get install -y -qq --no-install-recommends \
43 podman-remote openjdk-21-jdk-headless > /dev/null
44
45rustup component add rustfmt clippy
46rustup target add wasm32-unknown-unknown
47
48url="https://github.com/trunk-rs/trunk/releases/download/v${TRUNK_VERSION}/trunk-x86_64-unknown-linux-gnu.tar.gz"
49curl -fsSL -o /tmp/trunk.tar.gz "$url"
50curl -fsSL "$url.sha256" | awk '{print $1 " /tmp/trunk.tar.gz"}' | sha256sum -c -
51tar xzf /tmp/trunk.tar.gz -C /usr/local/bin
52rm -f /tmp/trunk.tar.gz
53
54# Only for the license files. Gradle installs the platform and build-tools
55# that the app needs on its own.
56if [ ! -x "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" ]; then
57 curl -fsSL -o /tmp/clt.zip \
58 "https://dl.google.com/android/repository/commandlinetools-linux-15859902_latest.zip"
59 echo "040d3996a65543d22ec4bf73e4c37aa37a8d4af4 /tmp/clt.zip" | sha1sum -c -
60 unzip -q /tmp/clt.zip -d /tmp/clt
61 mkdir -p "$ANDROID_HOME/cmdline-tools"
62 mv /tmp/clt/cmdline-tools "$ANDROID_HOME/cmdline-tools/latest"
63 rm -r /tmp/clt /tmp/clt.zip
64fi
65(yes || true) | "$ANDROID_HOME/cmdline-tools/latest/bin/sdkmanager" --licenses > /dev/null
66
67trunk --version && cargo fmt --version && cargo clippy --version && java -version
68"""
69
70# Reported, not gated: clippy findings change between toolchain versions.
71[[steps]]
72name = "lint"
73warn_on_fail = true
74run_sh = """
75cd project
76cargo fmt --check
77cargo clippy --workspace --all-targets -- -D warnings
78"""
79
80[[steps]]
81name = "test"
82run_sh = "cd project && cargo test --workspace"
83
84[[steps]]
85name = "build"
86timeout = 2400
87run_sh = """
88cd project
89(cd web && trunk build --release)
90cargo build --locked --release -p server
91"""
92
93[[steps]]
94name = "android"
95timeout = 1800
96run_sh = """
97cd project/android
98./gradlew --no-daemon testDebugUnitTest assembleDebug
99cp app/build/outputs/apk/debug/app-debug.apk /ci/build/opentracker-debug.apk
100"""
101publish_file = ["/ci/build/opentracker-debug.apk"]
102
103[[steps]]
104name = "android-lint"
105warn_on_fail = true
106run_sh = "cd project/android && ./gradlew --no-daemon lintDebug"
107
108# Packages what the build step made via the Containerfile's prebuilt stage.
109# Needs CI_ENGINE_SOCKET=1 on the server and the CI secret REGISTRY_PASSWORD
110# (admin password). Every run pushes the short sha and "edge". A tag run also
111# pushes the tag and "latest".
112[[steps]]
113name = "image"
114engine_socket = true
115timeout = 900
116run_sh = """
117cd project
118mkdir -p ci-bin
119cp "${CARGO_TARGET_DIR}/release/otserver" ci-bin/otserver
120cp -r web/dist ci-bin/web
121
122echo "$REGISTRY_PASSWORD" | podman-remote login "${CI_REGISTRY%%/*}" -u admin --password-stdin
123
124# A remote build sends a seccomp profile path that the server opens. The
125# client's default path may not exist on the server, so ask the server.
126prof=$(podman-remote info --format '{{.Host.Security.SECCOMPProfilePath}}' 2>/dev/null || true)
127if [ -n "$prof" ]; then
128 seccomp="seccomp=$prof"
129else
130 seccomp="seccomp=unconfined"
131fi
132
133img="$CI_REGISTRY:$CI_COMMIT_SHORT_SHA"
134podman-remote build --security-opt "$seccomp" \
135 -f Containerfile -t "$img" --build-arg BIN_STAGE=prebuilt .
136podman-remote push "$img"
137
138if [ -n "${CI_COMMIT_TAG:-}" ]; then
139 tags="$CI_COMMIT_TAG latest"
140else
141 tags="edge"
142fi
143for t in $tags; do
144 podman-remote tag "$img" "$CI_REGISTRY:$t"
145 podman-remote push "$CI_REGISTRY:$t"
146done
147"""
148