.hearthforge-ci.toml
⎇
Raw
1# Steps run in file order, in one container, sharing /ci/build.
2# Alpine, so the published binary is the same static musl build the
3# Containerfile ships.
4
5image = "docker.io/rust:1.90-alpine3.22"
6work_dir = "/ci/build"
7clone_project_to = "/ci/build/project"
8
9# busybox ash, which does support `set -o pipefail`.
10shell = ["/bin/sh", "-c"]
11
12# CARGO_TARGET_DIR must stay outside clone_project_to: the cache volume is
13# mounted before the clone runs, and git refuses to clone into a non-empty
14# directory.
15shell_setup = """
16set -euo pipefail
17export CARGO_TARGET_DIR=/ci/cache/target
18"""
19
20timeout = 5400
21memory_limit = "6g"
22
23# Caps are a safety valve against unbounded growth, not a budget: hitting one
24# costs a full rebuild. Sized roughly twice the working set. A local target
25# holding all three profiles measures 26G, of which debug is 22G.
26cache = [
27 { path = "/ci/cache/target", max_size = "20g" },
28 { path = "/usr/local/cargo/registry", max_size = "4g" },
29 { path = "/root/.bun/install/cache", max_size = "2g" },
30]
31
32[on]
33push = ["master"]
34tag = true
35
36# bun publishes no musl installer, so take the binary from the official image.
37# This mirrors the Containerfile's COPY --from. The -alpine tag is required:
38# the glibc build will not run here. bun also needs libstdc++, which setup
39# installs before anything executes it.
40[[copy]]
41image = "docker.io/oven/bun:1.4.0-alpine"
42from = "/usr/local/bin/bun"
43to = "/usr/local/bin"
44
45[variables]
46
47 [variables.TRUNK_VERSION]
48 default = "0.21.14"
49 description = "Trunk release that builds the wasm frontend. Matches the Containerfile."
50
51# ── toolchain ────────────────────────────────────────────────────────────────
52# binaryen supplies wasm-opt. Without it trunk downloads a glibc build that
53# cannot run on musl. just comes from apk here, so no install script.
54[[steps]]
55name = "setup"
56timeout = 900
57run_sh = """
58apk add --no-cache musl-dev binaryen just curl libstdc++
59
60# The official rust images use rustup's minimal profile, so rustfmt and
61# clippy are absent. `just lint` needs both.
62rustup component add rustfmt clippy
63rustup target add wasm32-unknown-unknown
64
65url="https://github.com/trunk-rs/trunk/releases/download/v${TRUNK_VERSION}/trunk-x86_64-unknown-linux-musl.tar.gz"
66curl -fsSL -o /tmp/trunk.tar.gz "$url"
67curl -fsSL "$url.sha256" | awk '{print $1 " /tmp/trunk.tar.gz"}' | sha256sum -c -
68tar xzf /tmp/trunk.tar.gz -C /usr/local/bin
69rm -f /tmp/trunk.tar.gz
70
71just --version && bun --version && trunk --version
72cargo fmt --version && cargo clippy --version
73"""
74
75# ── checks ───────────────────────────────────────────────────────────────────
76# Formatting and clippy findings are reported, not gated. The CI toolchain is
77# 1.90 and local machines run newer, so clippy disagrees across versions.
78[[steps]]
79name = "lint"
80run_sh = "cd project && just lint"
81warn_on_fail = true
82
83# `just test` covers server and api-types only, hence the extra web run.
84[[steps]]
85name = "test"
86run_sh = "cd project && just test && cargo test -p web"
87
88# ── build ────────────────────────────────────────────────────────────────────
89[[steps]]
90name = "build"
91timeout = 2400
92run_sh = "cd project && just build"
93publish_file = ["/ci/cache/target/release/filebrowser-ng"]
94
95# `just e2e` would rebuild the frontend. The build step already staged
96# server/dist, so run the embedded suite against it directly.
97[[steps]]
98name = "e2e"
99run_sh = "cd project && cargo test -p server --features embedded"
100
101# ── release ──────────────────────────────────────────────────────────────────
102[[steps]]
103name = "release"
104run_if = 'test -n "${CI_COMMIT_TAG}"'
105run_sh = """
106cd project
107install -Dm755 "${CARGO_TARGET_DIR}/release/filebrowser-ng" \
108 "dist/filebrowser-ng-${CI_COMMIT_TAG}-x86_64-linux-musl"
109"""
110publish_gzip = ["/ci/build/project/dist/"]
111