Skip to content
This repository was archived by the owner on Jun 29, 2026. It is now read-only.
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
The table of contents is too big for display.
Diff view
Diff view
  •  
  •  
  •  
6 changes: 3 additions & 3 deletions .codecov.yml
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,15 @@ coverage:
- "**/test/**/*"
status:
# https://docs.codecov.com/docs/github-checks#yaml-configuration-for-github-checks-and-codecov
patch: false
patch: off
# https://docs.codecov.com/docs/commit-status
project:
default:
target: auto
threshold: 0.05%
base: auto
paths:
- "src"
- "src/main/**/*"
- "**/src/main/**"
if_ci_failed: error
informational: false
only_pulls: true
Expand Down
47 changes: 32 additions & 15 deletions .github/workflows/build-on-ubuntu.yml
Original file line number Diff line number Diff line change
@@ -1,40 +1,57 @@
name: Build under Ubuntu
name: Ubuntu CI

on: push

jobs:
build:
name: Build on Ubuntu
runs-on: ubuntu-latest
concurrency: # Avoid canceling in-progress runs for the same branch.
group: ubuntu-ci-${{ github.ref }}
cancel-in-progress: false

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
with:
submodules: 'true'

- uses: actions/setup-java@v2
- uses: actions/setup-java@v5
with:
java-version: 11
java-version: 17
distribution: zulu
cache: gradle

- uses: actions/setup-node@v2
with:
node-version: 'lts/*'
- uses: gradle/actions/setup-gradle@v6

- name: Pull config
run: git submodule update --init --recursive
# Mirrors the pagefile step in build-on-windows.yml. The Linux runner
# ships with effectively no swap, so a memory peak becomes an instant
# OOM kill; this gives the kernel somewhere to fall back to.
- name: Add swap space
uses: pierotofy/set-swap-space@v1.0
with:
swap-size-gb: 8

- name: Build project and run tests
- name: Build project, run tests
shell: bash
run: ./gradlew build --stacktrace

# `build` does not run Dokka — its tasks are gated to the publishing
# graph — so `dokkaGenerate` is appended to surface documentation
# warnings on each push, before merge, instead of only in the post-merge
# `Publish` job. `failOnWarning` is enabled in the Dokka setup.
- name: Check documentation
run: ./gradlew dokkaGenerate --stacktrace

# See: https://github.com/marketplace/actions/junit-report-action
- name: Publish Test Report
uses: mikepenz/action-junit-report@v2.8.4
uses: mikepenz/action-junit-report@v4.0.3
if: always() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
report_paths: '**/build/test-results/**/TEST-*.xml'
require_tests: true # will fail workflow if test reports not found

- name: Upload code coverage report
uses: codecov/codecov-action@v2
uses: codecov/codecov-action@v4
with:
fail_ci_if_error: false
token: ${{ secrets.CODECOV_TOKEN }}
fail_ci_if_error: true
verbose: true
30 changes: 16 additions & 14 deletions .github/workflows/build-on-windows.yml
Original file line number Diff line number Diff line change
@@ -1,35 +1,37 @@
name: Build under Windows
name: Windows CI

on: pull_request

jobs:
build:
runs-on: windows-latest
name: Build on Windows

steps:
- uses: actions/checkout@v2
- uses: actions/checkout@v6
with:
submodules: recursive
fetch-depth: 0

- uses: actions/setup-java@v2
- uses: actions/setup-java@v5
with:
java-version: 11
java-version: 17
distribution: zulu
cache: gradle

- uses: actions/setup-node@v2
with:
node-version: 'lts/*'
- uses: gradle/actions/setup-gradle@v6

- name: Pull config
run: git submodule update --init --recursive
# See: https://github.com/al-cheb/configure-pagefile-action
- name: Configure Pagefile
uses: al-cheb/configure-pagefile-action@v1.3

- name: Build project and run tests
shell: cmd
# For the reason on `--no-daemon` see https://github.com/actions/cache/issues/454
run: gradlew.bat build --stacktrace --no-daemon
run: gradlew.bat build --stacktrace

# See: https://github.com/marketplace/actions/junit-report-action
- name: Publish Test Report
uses: mikepenz/action-junit-report@v2.8.4
uses: mikepenz/action-junit-report@v4.0.3
if: always() # always run even if the previous step fails
with:
report_paths: '**/build/test-results/test/TEST-*.xml'
report_paths: '**/build/test-results/**/TEST-*.xml'
require_tests: true
216 changes: 216 additions & 0 deletions .github/workflows/check-links.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,216 @@
name: Check Links

# Trigger only when the docs site, the link checker config, or this workflow
# itself changes — unrelated PRs do not need to pay the build+check cost.
on:
pull_request:
paths:
- 'docs/**'
- 'site/**'
- 'lychee.toml'
- '.github/workflows/check-links.yml'
workflow_dispatch:

env:
HUGO_VERSION: 0.161.1
LYCHEE_RELEASE: "lychee-x86_64-unknown-linux-gnu.tar.gz"
LYCHEE_VERSION_TAG: "lychee-v0.24.2"
# SHA256 of the above tarball, pinned at download time. Update alongside
# LYCHEE_VERSION_TAG whenever the binary is upgraded.
LYCHEE_SHA256: "1f4e0ef7f6554a6ed33dd7ac144fb2e1bbed98598e7af973042fc5cd43951c9a"
# Force Hugo to write its module cache where the cache step actually
# restores from. Hugo's default on Linux is `~/.cache/hugo_cache`
# (or `$TMPDIR/hugo_cache_$USER`), neither of which matches the
# `path: /tmp/hugo_cache` cache step below — without this env var,
# the cache would silently never hit.
HUGO_CACHEDIR: /tmp/hugo_cache

jobs:
check-links:
runs-on: ubuntu-latest
concurrency:
group: ${{ github.workflow }}-${{ github.ref }}
cancel-in-progress: true
steps:
- name: Checkout
uses: actions/checkout@v6

# Detect the Hugo site root (`docs/` or `site/`) by looking for a Hugo
# config file. Hugo config may live directly in the site root or in a
# `config/` or `config/_default/` subdirectory (both layouts are valid).
# Outputs `present=true|false` and `work_dir` (the directory where
# `npm ci` / `hugo` commands should run — either `$dir/_preview` for
# repos that use a separate preview sub-tree, or `$dir` for repos whose
# Node/Hugo setup lives at the site root).
# When neither directory has a Hugo config, the job short-circuits to a
# success so that this shared workflow stays green on repos that do not
# host a Hugo site at all.
- name: Detect docs site
id: docs
run: |
for dir in docs site; do
for cfg in hugo.toml hugo.yaml \
config/hugo.toml config/hugo.yaml \
config/_default/hugo.toml config/_default/hugo.yaml; do
if [ -f "$dir/$cfg" ]; then
if [ -f "$dir/_preview/package-lock.json" ]; then
echo "work_dir=$dir/_preview" >> "$GITHUB_OUTPUT"
echo "present=true" >> "$GITHUB_OUTPUT"
echo "::notice::Hugo site found under $dir/ (work_dir: $dir/_preview)"
elif [ -f "$dir/package-lock.json" ]; then
echo "work_dir=$dir" >> "$GITHUB_OUTPUT"
echo "present=true" >> "$GITHUB_OUTPUT"
echo "::notice::Hugo site found under $dir/ (work_dir: $dir)"
else
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::notice::Hugo config found in $dir/ but no package-lock.json found — skipping link check."
fi
exit 0
fi
done
done
echo "present=false" >> "$GITHUB_OUTPUT"
echo "::notice::No Hugo site found under docs/ or site/ — skipping link check."

- name: Setup Hugo
if: steps.docs.outputs.present == 'true'
uses: peaceiris/actions-hugo@v3
with:
hugo-version: ${{ env.HUGO_VERSION }}
extended: true

# `actions/setup-node@v4` ships with built-in npm caching that hashes
# the lockfile and restores `~/.npm`. We use that instead of a
# standalone `actions/cache@v4` block so there is only one source of
# truth for the cache key (no drift between two layers).
- name: Setup Node
if: steps.docs.outputs.present == 'true'
uses: actions/setup-node@v4
with:
node-version: '26'
cache: 'npm'
cache-dependency-path: ${{ steps.docs.outputs.work_dir }}/package-lock.json

# `HUGO_CACHEDIR=/tmp/hugo_cache` (set in `env:` above) makes Hugo
# actually write to the path this step restores from. The key hashes
# both possible go.sum locations so adding/removing a Hugo module
# invalidates the cache deterministically regardless of site root.
- name: Cache Hugo Modules
if: steps.docs.outputs.present == 'true'
uses: actions/cache@v4
with:
path: /tmp/hugo_cache
key: ${{ runner.os }}-hugomod-${{ hashFiles('docs/**/go.sum', 'site/**/go.sum') }}
restore-keys: |
${{ runner.os }}-hugomod-

- name: Install Dependencies
if: steps.docs.outputs.present == 'true'
working-directory: ${{ steps.docs.outputs.work_dir }}
run: npm ci

- name: Build docs preview site
if: steps.docs.outputs.present == 'true'
working-directory: ${{ steps.docs.outputs.work_dir }}
run: hugo -e development

# Cache Lychee results to avoid hitting rate limits.
# Key on the lychee.toml hash so that exclude-list edits (e.g. removing
# an exclude pattern) invalidate the cache deterministically; otherwise
# stale `200 OK` entries for the now-checked URLs would be trusted until
# `max_cache_age` expires.
- name: Cache Lychee results
if: steps.docs.outputs.present == 'true'
uses: actions/cache@v4
with:
path: .lycheecache
key: cache-lychee-${{ runner.os }}-${{ hashFiles('lychee.toml') }}
restore-keys: |
cache-lychee-${{ runner.os }}-

# The cache key includes LYCHEE_VERSION_TAG so a version bump
# automatically pulls a fresh binary instead of reusing the old one.
# The restore-keys fallback lets a release-filename tweak (rare) reuse
# the existing cached binary for the same version-tag instead of paying
# for a fresh download.
- name: Cache Lychee executable
if: steps.docs.outputs.present == 'true'
id: cache-lychee
uses: actions/cache@v4
with:
path: lychee
key: ${{ runner.os }}-${{ env.LYCHEE_VERSION_TAG }}-${{ env.LYCHEE_RELEASE }}
restore-keys: |
${{ runner.os }}-${{ env.LYCHEE_VERSION_TAG }}-

# We use Lychee directly instead of a GitHub Action because it
# must have access to the local Hugo server, which is not visible
# from the Docker-based action.
#
# `if:` gating uses `hashFiles('lychee/lychee')` rather than
# `steps.cache-lychee.outputs.cache-hit != 'true'`. Per `actions/cache`
# docs, `cache-hit` is only `'true'` on an EXACT key match — a restore
# via `restore-keys` reports `cache-hit == 'false'`, even though the
# binary is present in the workspace. Re-downloading in that case
# would defeat the point of the fallback. `hashFiles` returns an empty
# string when the file is absent, so this guard runs the download iff
# neither the exact key nor any restore-key restored the binary.
- name: Download Lychee executable
uses: robinraju/release-downloader@v1.7
if: steps.docs.outputs.present == 'true' && hashFiles('lychee/lychee') == ''
with:
repository: "lycheeverse/lychee"
tag: ${{ env.LYCHEE_VERSION_TAG }}
fileName: ${{ env.LYCHEE_RELEASE }}

- name: Verify Lychee checksum
if: steps.docs.outputs.present == 'true' && hashFiles('lychee/lychee') == ''
run: |
echo "${{ env.LYCHEE_SHA256 }} ${{ env.LYCHEE_RELEASE }}" | sha256sum --check --strict

# The v0.24.2 tarball contains a top-level directory
# (e.g. `lychee-x86_64-unknown-linux-gnu/lychee`), so `--strip-components=1`
# flattens it to `lychee/lychee` — matching what the companion
# `check-links` skill does locally and what the next step expects.
- name: Extract Lychee executable
if: steps.docs.outputs.present == 'true' && hashFiles('lychee/lychee') == ''
run: |
mkdir -p lychee &&
tar -xzf ${{ env.LYCHEE_RELEASE }} --strip-components=1 -C lychee

# 1. In the generated HTML, some inner links will have absolute URLs and
# the link checker will attempt to fetch them. That's why we need
# a server. Sadly, link checkers have no settings to address this.
# 2. Output redirection is necessary for nohup in GitHub Actions.
# 3. Sleep + `curl` readiness check make sure the server is actually
# serving HTTP before the next step runs Lychee. Without the curl
# probe a silent startup failure (port already bound, missing
# Hugo module, build error surfacing after `nohup` returns 0)
# would manifest 60 s later as "every URL unreachable" Lychee
# errors instead of pointing at the real cause. Mirrors the
# `pgrep -F` guard in the companion `check-links` skill.
# 4. `--port 1313` is set explicitly (not relying on Hugo's default) so
# the coupling with `--base-url http://localhost:1313/` in the next
# Lychee step is visible — change one, change the other.
- name: Start Hugo server
if: steps.docs.outputs.present == 'true'
working-directory: ${{ steps.docs.outputs.work_dir }}
run: |
nohup hugo server \
--environment development \
--port 1313 \
> nohup.out 2> nohup.err < /dev/null &
sleep 5
if ! curl -sf http://localhost:1313/ > /dev/null; then
echo "ERROR: Hugo server did not respond on port 1313." >&2
echo "--- stdout ---" >&2; cat nohup.out >&2 || true
echo "--- stderr ---" >&2; cat nohup.err >&2 || true
exit 1
fi

- name: Check links
if: steps.docs.outputs.present == 'true'
run: |
./lychee/lychee --config lychee.toml --timeout 60 \
--base-url http://localhost:1313/ \
'${{ steps.docs.outputs.work_dir }}/public/**/*.html'
35 changes: 35 additions & 0 deletions .github/workflows/ensure-reports-updated.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
# Ensures that the license report files were modified in this PR.
#
# The check runs only for pull requests targeting a default (`master`/`main`) or
# a release-line (e.g. `2.x-jdk8-master`) branch. The report files embed the project
# version, so they are refreshed by the branches which bump it. Pull requests
# targeting auxiliary branches are not checked.
#
# The base branch is checked inside the job rather than via the `branches` filter:
# a workflow skipped by branch filtering leaves its check in the `Pending` state,
# blocking PRs which require it, while a job skipped via `if` reports `skipped`,
# which satisfies required status checks.

name: License Reports

on:
pull_request:

jobs:
check:
name: Ensure license reports are updated
runs-on: ubuntu-latest
# Default and release-line branches, e.g. `master`, `main`, `2.x-jdk8-master`.
if: endsWith(github.base_ref, 'master') || endsWith(github.base_ref, 'main')

steps:
- uses: actions/checkout@v6
with:
# Configure the checkout of all branches so that it is possible to run the comparison.
fetch-depth: 0
# Check out the `config` submodule to fetch the required script file.
submodules: true

- name: Check that dependency report files are modified
shell: bash
run: chmod +x ./config/scripts/ensure-reports-updated.sh && ./config/scripts/ensure-reports-updated.sh
Loading
Loading