# CI Design

The GitLab pipeline is defined in [`deploy/.gitlab-ci.yml`](../deploy/.gitlab-ci.yml).
This document records its structure and the repository settings that affect its
behavior. Contributor-facing build and test commands are documented in the
[README](../README.md#building).

## Pipeline structure

The pipeline has four stages:

1.  `preflight` verifies that each merge-request commit contains the current
   target-branch commit. Rebasing onto the target branch and merging the target
   branch into the source branch both satisfy this requirement. Stale branches
   fail before restoring build caches or starting compilation.
2.  `verify` formats, lints, builds, and tests the Rust, Kotlin, shell, JSON, and
   systemd sources. A non-blocking dependency job reports newer stable Cargo,
   Kotlin, Gradle, plugin, and library releases when changes are under review.
   A separate Fedora job validates the systemd units, resolves the RPM's static
   BuildRequires, generates its dynamic BuildRequires, and uses DNF to verify
   that the current Fedora repositories can resolve both sets. The verified
   update checker becomes a short-lived pipeline artifact for the `publish`
   stage.
3.  `publish` uploads the verified update-checker executable from the default
   branch to GitLab's generic package registry.
4.  `versions` runs the published update checker and reconciles the COPR project
   on a schedule or by manual request. Keeping external state separate from
   ordinary verification prevents merge-request pipelines from depending on it.

Merge-request and default-branch pipelines run verification. Ordinary
feature-branch pushes and tags do not create pipelines; changes are validated
once they enter a merge request. Scheduled pipelines run only the version check
and COPR configuration reconciliation.

The `upstream-dependency-report` job runs for merge requests and default-branch
pushes. It reports available upstream releases and registry errors in its log
without changing the pipeline status. Runtime Rust candidates are eligible only
when every supported Fedora and EPEL target packages them, as described in
[Dependency Maintenance](dependency-management.md).

## Caching

Rust compilation uses `sccache`. Cargo registry data is cached separately from
compiler outputs so dependency-report jobs can reuse package metadata without
downloading the compiler cache. Cargo's `target` directory is not cached because
its complete build trees are substantially larger than the reusable sccache
entries they bypass. The Gradle distribution, dependency cache, and Kotlin build
outputs are also cached. The cache definitions use fixed default-branch keys and
set `unprotect: true`, so protected and unprotected pipelines address the same
caches. Default-branch pipelines update the shared caches; merge-request
pipelines use them read-only.
Cached build data is an optimization rather than a build input: Cargo, Gradle,
and sccache validate cached outputs against the source, dependency, compiler,
and build-option inputs before reuse.

Full RPM builds are delegated to Copr, which provides the authoritative
Mock-based Fedora and EPEL build environments. GitLab performs only a lightweight
Fedora dependency-resolution check: Tito runs RPM through
`%generate_buildrequires` after DNF resolves the static BuildRequires. DNF then
resolves the resulting build-requirements RPM without compiling or packaging
the project. This catches static dependencies and pinned Cargo dependencies that
are absent from the current Fedora repositories while leaving the complete
package build to Copr. The repository's GitLab webhook sends release events to
the Copr package webhook; tags without a release and ordinary branch pushes do
not trigger Copr.

## Merge-request pipelines

The project uses standard GitLab merge-request pipelines, which are available
on GitLab Free. A merge request must contain the current target-branch commit
before its build and test jobs run. Each pipeline verifies the merge-request
commit and reads the shared default-branch build caches. The default-branch
pipeline verifies the repository state produced by each completed merge and
refreshes those caches.

## Version updates

The update checker owns changes derived from upstream Minecraft, Fabric, Geyser,
and MCXboxBroadcast releases. Its tests verify generated release metadata,
including byte-for-byte synchronization of the checked-in README, RPM spec, and
Tito metadata with their templates. The normal CI `cargo test` invocation runs
this generated-file verification on every merge request and default-branch
build.

[`upstream-versions.json`](../upstream-versions.json) is the source of truth for
the version, download URL, and SHA-256 checksum of every upstream server
artifact. Its schema is checked by CI. The Rust Bedrock downloader and Kotlin
wrapper jars embed the same file at build time, so installed packages do not
depend on a repository file at runtime.

`cargo run -p update-checker -- update` refreshes the version locks and generated
files in the current working tree. It does not configure Git, create a commit,
push a branch, or publish a release. The scheduled job invokes
`update-checker release`, which performs those publication operations after
generating the same files.

The scheduled job uses the executable published from the current default branch,
ensuring version-update behavior has already passed the normal verification
pipeline.

`GITLAB_API_TOKEN` must be a protected, masked GitLab CI/CD variable with access
to push the generated commit and create the corresponding release. The update
checker reads the token directly from its environment, and its command line does
not contain the token.

After committing a version update, the updater creates the GitLab Release and its
version tag. The release webhook then asks Copr to mirror that published release
into the Fedora and EPEL repositories. COPR completion does not gate or alter the
GitLab Release.

## COPR configuration

The `configure-copr` job makes the repository the source of truth for the COPR
project's active build targets and description. It uses Fedora's maintained
distribution aliases to select the latest stable Fedora and latest EPEL major,
then enables x86_64 and AArch64 for both. The job derives both the COPR project
description and its installation instructions directly from the shared
Handlebars subsections under `deploy/readme/`; the top-level
[`deploy/README.md.hbs`](../deploy/README.md.hbs) assembles the complete README.
The subsection templates are therefore the single source of truth for content
shared with COPR.

Documentation changes must update the templates and regenerate their checked-in
README output, or update both source and output in the same commit. The updater
regenerates it during automated version changes, and its
generated-file test makes CI fail whenever a checked-in output differs from its
template.

The job runs after default-branch pushes and as part of scheduled pipelines, so
distribution lifecycle changes are applied even when the repository has no new
commit. `COPR_CONFIG` must be a protected GitLab file variable containing the
standard `copr-cli` configuration. COPR credentials and webhook URLs are not
stored in the repository, and the job never prints the configuration file.
