cargo fmt --all over the workspace, with the rustfmt of the CI image (1.98.1); 1.99's output is the same. Formatting only: 167 files, no code changes, plus the AGPL notice on the two upstream files this touched first. Vendored crates are outside the workspace and stay as they are. The tree had drifted, so formatting any one changed file reformatted dozens of untouched ones. CI now runs cargo fmt --all --check before the build on both forges.
150 lines
7.6 KiB
YAML
150 lines
7.6 KiB
YAML
# CI on the self-hosted Gitea, ported from .gitlab-ci.yml during the move off
|
|
# GitLab (2026-09-22). Gitea reads .gitea/workflows and ignores .github/ once
|
|
# this directory exists; .github/workflows stays as it was for GitHub.
|
|
#
|
|
# Every job runs in an image pinned by digest (tag in the trailing comment),
|
|
# and the only action used is coffey-labs/actions/checkout pinned by SHA. The
|
|
# instance resolves short `uses:` against itself, never GitHub, so nothing
|
|
# unreviewed can be pulled in.
|
|
#
|
|
# BUILD_ON: when the Actions variable BUILD_ON is 'github' (org or repo),
|
|
# fork-checks and build skip here and the `github` job below waits for the
|
|
# same work done by .github/workflows/ci.yml on the GitHub mirror, passing or
|
|
# failing with it -- so this run still carries the answer pull requests and
|
|
# merges look at. Unset, everything builds here as before. If GitHub is
|
|
# unavailable, unset BUILD_ON and nothing else has to change.
|
|
name: ci
|
|
|
|
on:
|
|
push:
|
|
branches: [main]
|
|
pull_request:
|
|
|
|
concurrency:
|
|
group: ${{ github.workflow }}-${{ github.ref }}
|
|
cancel-in-progress: true
|
|
|
|
jobs:
|
|
# What an upstream merge can bring in or leave behind without a conflict:
|
|
# the upstream name in a new string literal, and a changed upstream file
|
|
# without the AGPL 5(a) notice. Seconds, and needs no toolchain. The notice
|
|
# check diffs against the upstream snapshot branch, hence the full fetch.
|
|
fork-checks:
|
|
if: ${{ vars.BUILD_ON != 'github' }}
|
|
runs-on: light
|
|
container:
|
|
image: python:3.13-slim@sha256:8d9d0b8bcf6506481eae4907c18f5e3e7902e629f5f6d684f9e7c32e85e3ddf0 # 3.13-slim
|
|
steps:
|
|
- uses: coffey-labs/actions/checkout@fab0c4d45e0162963965f1555df27b7bed5e20ec
|
|
with:
|
|
fetch-depth: 0
|
|
- run: python3 tools/fork/name-check.py
|
|
- if: always()
|
|
run: python3 tools/fork/notice-check.py
|
|
# Cargo can patch a dependency to a directory in this repository, and
|
|
# the image builds from a context .dockerignore prunes to almost
|
|
# nothing. CI never sees the difference; a release does.
|
|
- if: always()
|
|
run: python3 tools/fork/context-check.py
|
|
# The personal-data catalog must classify every object and field the
|
|
# schema has, and name nothing that is gone.
|
|
- if: always()
|
|
run: python3 tools/fork/privacy-check.py
|
|
# The admin reads each expression field's allowed values and variables
|
|
# from the schema; they're generated from the registry and must match it.
|
|
- if: always()
|
|
run: python3 tools/fork/expr-schema.py --check
|
|
- if: always()
|
|
run: python3 -m unittest discover -s tools/fork/tests
|
|
|
|
build:
|
|
if: ${{ vars.BUILD_ON != 'github' }}
|
|
# Either runner (host1 or host2): the build needs no docker socket.
|
|
runs-on: light
|
|
container:
|
|
image: rust:1-bookworm@sha256:93ce27a88655056a51dbdd8f5f2d7ddc071c7b0070fb288a37b5a285fc83971e # 1-bookworm
|
|
# A named volume per host that outlives the job: Cargo's registry/git
|
|
# cache and the target dir. Without it every run recompiled RocksDB and
|
|
# the rest of the dependency tree from scratch. Each runner allows this
|
|
# one volume in its valid_volumes; each host keeps its own copy.
|
|
volumes:
|
|
- inbuxa-server-cargo:/cache
|
|
env:
|
|
CARGO_HOME: /cache/cargo-home
|
|
CARGO_TARGET_DIR: /cache/target
|
|
# Dependencies are reused whole; incremental data for the workspace
|
|
# crates would only bloat a shared target dir.
|
|
CARGO_INCREMENTAL: "0"
|
|
steps:
|
|
- uses: coffey-labs/actions/checkout@fab0c4d45e0162963965f1555df27b7bed5e20ec
|
|
# Cargo sizes its parallelism from the host's core count, not the job's
|
|
# CPU cap (2 on host2, 4 on host1); a C++ build of RocksDB at 8-way
|
|
# parallelism inside 6 GB gets OOM-killed. Match jobs to the cap.
|
|
- run: |
|
|
jobs=$(awk '$1 != "max" { printf "%d", $1 / $2 }' /sys/fs/cgroup/cpu.max 2>/dev/null)
|
|
echo "CARGO_BUILD_JOBS=${jobs:-$(nproc)}" >> "$GITHUB_ENV"
|
|
echo "cargo jobs: ${jobs:-$(nproc)}; cache: $(du -sh /cache 2>/dev/null | cut -f1)"
|
|
# One formatting for the whole tree, so a change's diff is only its own
|
|
# lines. Before the build: it fails in seconds.
|
|
- run: rustup component add rustfmt && cargo fmt --all --check
|
|
- run: apt-get update -qq && apt-get install -y -qq --no-install-recommends clang >/dev/null
|
|
- run: cargo build -p inbuxa --locked
|
|
# --no-run: the workflow compiled every test target without running them,
|
|
# which catches a test that no longer builds without paying for the suite.
|
|
- run: cargo test --workspace --locked --no-run
|
|
# The release profile, on main only. It is the profile the image is
|
|
# built with, and it fails in ways the dev profile does not: v2026.9.24
|
|
# was tagged on a commit whose CI was green and whose release build
|
|
# could not compile the scim crate at all. A few minutes per merge is
|
|
# cheaper than finding that out from a tag, which throws away a
|
|
# multi-architecture build and leaves a version half-cut.
|
|
#
|
|
# Pull requests stay on the dev profile, where the wait is worth less.
|
|
- if: github.event_name == 'push'
|
|
run: cargo build -p inbuxa --locked --release
|
|
# Keep the cache from growing without bound: past 60 GB the target dir
|
|
# is dropped and the next build starts cold. The download cache stays.
|
|
# Two builds (dev + test profiles) already fill ~22 GB, so the limit
|
|
# has to sit well above that or it would wipe a warm cache every run.
|
|
- if: always()
|
|
run: |
|
|
used=$(du -s --block-size=1G /cache/target 2>/dev/null | cut -f1)
|
|
echo "target dir: ${used:-0} GB"
|
|
if [ "${used:-0}" -gt 60 ]; then rm -rf /cache/target && echo "over 60 GB: target dir cleared"; fi
|
|
|
|
# BUILD_ON=github: the GitHub mirror builds this commit and posts the result
|
|
# back as the commit status "github/ci (branch)". This waits for that status
|
|
# and takes its answer. The mirror pushes on every commit, so a missing
|
|
# status means GitHub has not got the push or is not running: after the
|
|
# timeout this fails, which is the cue to unset BUILD_ON.
|
|
github:
|
|
if: ${{ vars.BUILD_ON == 'github' }}
|
|
# Its own runner label with plenty of slots: this job only polls, but holds a slot
|
|
# for as long as the GitHub build takes, and must not starve the build runners.
|
|
runs-on: wait
|
|
timeout-minutes: 150
|
|
container:
|
|
image: python:3.13-slim@sha256:8d9d0b8bcf6506481eae4907c18f5e3e7902e629f5f6d684f9e7c32e85e3ddf0 # 3.13-slim
|
|
steps:
|
|
- env:
|
|
TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
|
SHA: ${{ github.event.pull_request.head.sha || github.sha }}
|
|
CONTEXT: github/ci (branch)
|
|
run: |
|
|
python3 - <<'EOF'
|
|
import json, os, time, urllib.request
|
|
url = (f"{os.environ['CI_SERVER_INTERNAL']}/api/v1/repos/{os.environ['GITHUB_REPOSITORY']}"
|
|
f"/commits/{os.environ['SHA']}/statuses?limit=50")
|
|
req = urllib.request.Request(url, headers={"Authorization": f"token {os.environ['TOKEN']}"})
|
|
ctx, last = os.environ["CONTEXT"], None
|
|
print(f"waiting for '{ctx}' on {os.environ['SHA']}", flush=True)
|
|
while True:
|
|
mine = [s for s in json.load(urllib.request.urlopen(req)) if s["context"] == ctx]
|
|
state = max(mine, key=lambda s: s["id"]) if mine else None
|
|
if state and state["status"] != last:
|
|
last = state["status"]; print(f"{ctx}: {last} {state.get('target_url', '')}", flush=True)
|
|
if last == "success": raise SystemExit(0)
|
|
if last in ("failure", "error"): raise SystemExit(1)
|
|
time.sleep(20)
|
|
EOF
|