.hearthforge-ci.toml
⎇
Raw
1# Hearthforge CI for VidArchive.
2# Steps share one container and run in file order, so what "setup" installs
3# stays available to every later step.
4
5image = "docker.io/golang:1.26"
6work_dir = "/ci/build"
7clone_project_to = "/ci/build/project"
8shell_setup = "set -euo pipefail"
9timeout = 1800
10cpu_limit = 2.0
11# The race detector needs several times the normal heap.
12memory_limit = "4g"
13
14# Go's build and module caches. Both live outside clone_project_to.
15cache = [
16 { path = "/root/.cache/go-build", max_size = "2g" },
17 { path = "/go/pkg/mod", max_size = "2g" },
18]
19
20[on]
21push = ["*"]
22tag = true
23
24[variables]
25 [variables.ONLINE_TESTS]
26 default = "1"
27 description = "Set to 1 to also run the live yt-dlp tests against YouTube (needs network)"
28
29# ffmpeg/ffprobe unlock the media tests; yt-dlp is the standalone Linux build,
30# which needs no Python. podman-remote is for the image step.
31[[steps]]
32name = "setup"
33timeout = 600
34run_sh = """
35apt-get update -qq && apt-get install -y -qq --no-install-recommends ffmpeg podman-remote > /dev/null
36curl -fsSL https://github.com/yt-dlp/yt-dlp/releases/latest/download/yt-dlp_linux -o /usr/local/bin/yt-dlp
37chmod +x /usr/local/bin/yt-dlp
38yt-dlp --version
39go install mvdan.cc/gofumpt@latest
40go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest
41go install golang.org/x/vuln/cmd/govulncheck@latest
42"""
43
44# The checkout has no .git, so compare copies instead of using git diff.
45[[steps]]
46name = "tidy"
47run_sh = "cd project && cp go.mod /tmp/go.mod && cp go.sum /tmp/go.sum && go mod tidy && diff /tmp/go.mod go.mod && diff /tmp/go.sum go.sum"
48
49# gofumpt has no failure exit code, so an empty report is the pass condition.
50[[steps]]
51name = "format"
52run_sh = "cd project && test -z \"$(gofumpt -l -extra .)\" || (gofumpt -l -extra . && exit 1)"
53
54[[steps]]
55name = "lint"
56run_sh = "cd project && golangci-lint run ./..."
57
58# Advisories appear without any code change, so a hit must not block a run.
59[[steps]]
60name = "vulncheck"
61warn_on_fail = true
62run_sh = "cd project && govulncheck ./..."
63
64[[steps]]
65name = "test"
66run_sh = "cd project && go test -race -count=1 ./..."
67
68# The live tests hit YouTube, which may block CI IPs. A failure must stay
69# visible but must not fail the run.
70[[steps]]
71name = "test-online"
72run_if = 'test "$ONLINE_TESTS" = "1"'
73run_sh = "cd project && VIDARCHIVE_ONLINE_TESTS=1 go test ./internal/service -run Live -v"
74warn_on_fail = true
75
76[[steps]]
77name = "build"
78run_sh = "cd project && CGO_ENABLED=0 go build -trimpath -ldflags='-s -w' -o /ci/build/dist/vidarchive ./cmd/vidarchive"
79publish_file = ["/ci/build/dist/vidarchive"]
80
81# ── image ────────────────────────────────────────────────────────────────────
82# Packages the binary the build step made, so the image ships exactly what was
83# tested. The Containerfile's build stage is skipped via BIN_STAGE.
84# engine_socket hands this step the host engine, which is Podman on this
85# server (needs CI_ENGINE_SOCKET=1). REGISTRY_PASSWORD is a CI secret with the
86# admin password. CI_REGISTRY is "<host>/<repo>" on the built-in registry.
87# Tags: every run pushes the short sha and "edge"; a tag run also pushes the
88# tag and "latest".
89[[steps]]
90name = "image"
91engine_socket = true
92timeout = 900
93run_sh = """
94cd project
95mkdir -p ci-bin
96cp /ci/build/dist/vidarchive ci-bin/vidarchive
97
98echo "$REGISTRY_PASSWORD" | podman-remote login "${CI_REGISTRY%%/*}" -u admin --password-stdin
99
100# A remote build sends a seccomp profile path that the server opens. Ask the
101# server for its own path. An empty answer means no profile can be named, so
102# the build runs unconfined rather than failing.
103prof=$(podman-remote info --format '{{.Host.Security.SECCOMPProfilePath}}' 2>/dev/null || true)
104if [ -n "$prof" ]; then
105 seccomp="seccomp=$prof"
106else
107 seccomp="seccomp=unconfined"
108fi
109
110img="$CI_REGISTRY:$CI_COMMIT_SHORT_SHA"
111podman-remote build --security-opt "$seccomp" \
112 -f Containerfile -t "$img" --build-arg BIN_STAGE=prebuilt .
113podman-remote push "$img"
114
115if [ -n "${CI_COMMIT_TAG:-}" ]; then
116 tags="$CI_COMMIT_TAG latest"
117else
118 tags="edge"
119fi
120for t in $tags; do
121 podman-remote tag "$img" "$CI_REGISTRY:$t"
122 podman-remote push "$CI_REGISTRY:$t"
123done
124"""
125