Skip to content

doc: apply the documentation review to the security and port comments #181

doc: apply the documentation review to the security and port comments

doc: apply the documentation review to the security and port comments #181

Workflow file for this run

name: CI Pipeline
on:
workflow_dispatch:
push:
branches-ignore:
- coverity_scan
- run-fuzzer**
- debug-fuzzer-**
pull_request:
schedule:
- cron: '0 20 * * *'
#
# Cancel any in-flight or queued run of the 'CI Pipeline' workflow on the
# same branch.
#
concurrency:
group: ${{ github.workflow }}-${{ github.head_ref || github.ref_name }}
cancel-in-progress: true
jobs:
#
# An ad hoc continuous integration (CI) run needs the runners that a
# scheduled round (see `ci-scheduled.yml`) may be occupying. Every
# pipeline run therefore cancels any scheduled round in flight before
# the pipeline run's own legs queue. A pull request from a fork holds a read-only token
# whatever permissions the job requests. A fork pull request
# therefore cancels nothing, and the fork pull request's legs wait
# until the scheduled round finishes.
#
scheduled_ci_cancel:
name: Cancel scheduled CI rounds
if: github.repository == 'FreeRADIUS/freeradius-server'
runs-on: ubuntu-latest
permissions:
actions: write
steps:
- name: Cancel any scheduled round in flight
env:
GH_TOKEN: ${{ github.token }}
REPO: ${{ github.repository }}
run: |
for status in queued in_progress; do
for id in $(gh run list --repo "$REPO" --workflow ci-scheduled.yml \
--status "$status" --json databaseId --jq '.[].databaseId' 2>/dev/null); do
echo "Cancelling scheduled run $id, adhoc CI takes priority"
gh run cancel "$id" --repo "$REPO" || true
done
done
ci_prev_run:
name: "CI already run?"
runs-on: ubuntu-latest
permissions:
actions: read
contents: read
outputs:
should_skip: ${{ steps.skip_check.outputs.should_skip }}
steps:
#
# `concurrent_skipping` widens the duplicate check to runs still in
# flight. `same_content_newer` skips the current run when an older
# run of the identical tree is in flight. The fast-forward to
# `master` lands while the branch pipeline that tested the commit is
# still in flight. Only the check against runs still in flight
# can match.
# Developer branches and pull requests keep 'never'. The
# concurrency group for a developer branch or pull request cancels
# the older run, and skipping against a cancelled run would merge
# an untested tree.
#
- id: skip_check
uses: fkirc/skip-duplicate-actions@v5.3.2
with:
concurrent_skipping: ${{ github.event_name == 'push' && !startsWith(github.ref_name, 'developer/') && 'same_content_newer' || 'never' }}
#
# `common_vars` determines which registry holds the images and which
# image tag the current ref should use. `common_vars` also determines
# whether the current ref changes any file that the images include.
# See `.github/workflows/common_vars.yml`.
#
common_vars:
name: Common vars
uses: ./.github/workflows/common_vars.yml
#
# Does the registry hold the images that the current ref needs? The
# images can be absent for multiple reasons: a failed refresh, pruned
# images, a rebuilt registry.
#
# The job runs on a self-hosted runner, because GitHub runners cannot
# resolve the registry. If one image is present, we assume that all
# the images are present. If we cannot get a definitive answer, we
# skip the build.
#
images_present:
name: "Docker images present?"
needs: [common_vars]
if: ${{ github.event_name == 'push'
&& github.repository_owner == 'FreeRADIUS'
&& needs.common_vars.outputs.images_changed == 'false' }}
runs-on: ${{ needs.common_vars.outputs.selfhosted == '1' && 'self-hosted' || 'ubuntu-latest' }}
outputs:
missing: ${{ steps.probe.outputs.missing }}
steps:
- name: Install regctl
uses: regclient/actions/regctl-installer@main
- name: Look for the sentinel image
id: probe
env:
DOCKER_SENTINEL_IMAGE: ${{ needs.common_vars.outputs.image_prefix }}self-hosted:${{ needs.common_vars.outputs.image_tag }}
DOCKER_REGISTRY: ${{ needs.common_vars.outputs.docker_registry }}
DOCKER_USERNAME: ${{ secrets.DOCKER_REPO_USERNAME }}
DOCKER_PASSWORD: ${{ secrets.DOCKER_REPO_PASSWORD }}
run: |
if [ -n "$DOCKER_USERNAME" ]; then
printf '%s' "$DOCKER_PASSWORD" \
| regctl registry login "$DOCKER_REGISTRY" -u "$DOCKER_USERNAME" --pass-stdin \
|| echo "Continuing without login, regctl could not sign in to $DOCKER_REGISTRY"
fi
if regctl manifest head "$DOCKER_SENTINEL_IMAGE" >/dev/null 2>&1; then
echo "$DOCKER_SENTINEL_IMAGE present"
echo "missing=false" >> "$GITHUB_OUTPUT"
elif regctl repo ls "$DOCKER_REGISTRY" >/dev/null 2>&1; then
# The registry answered, so the registry is reachable and the
# image really is absent.
echo "$DOCKER_SENTINEL_IMAGE absent, the images need rebuilding"
echo "missing=true" >> "$GITHUB_OUTPUT"
else
echo "Assuming the images are present, $DOCKER_REGISTRY did not answer"
echo "missing=false" >> "$GITHUB_OUTPUT"
fi
#
# The refresh job skips on the vast majority of commits. When the
# refresh job runs, every leg waits for the refresh job, so the
# docker images have all necessary packages before the rest of CI
# runs.
#
# Only a push rebuilds, because only a push publishes, and a pull
# request has no tag to publish to. A branch publishes under a tag
# named after the branch (a push to `feature/dhcp-fix` publishes
# `self-hosted-ci-ubuntu24:feature-dhcp-fix`, while `master` keeps reading
# `self-hosted-ci-ubuntu24:latest`), so work in progress never
# overwrites the images that the default branch reads. A fork has no
# images at all until the fork builds images, so a fork always
# refreshes. The fork refreshes into the fork's own registry, where
# the fork can overwrite nothing but the fork's own tags.
#
#
# A skipped job in `needs` skips every dependent before GitHub reads
# the dependent's own condition, unless the condition contains
# `!cancelled()`. `images_present` skips whenever the images changed,
# and an image change is exactly when the refresh has to run. The
# condition therefore checks each prerequisite's result itself.
#
docker_ci_images_refresh:
name: Docker CI images refresh
needs: [common_vars, images_present, ci_prev_run]
if: ${{ !cancelled()
&& needs.common_vars.result == 'success'
&& needs.ci_prev_run.result == 'success'
&& needs.images_present.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true'
&& ((github.event_name == 'push'
&& (needs.common_vars.outputs.images_changed == 'true'
|| needs.images_present.outputs.missing == 'true'))
|| github.repository_owner != 'FreeRADIUS') }}
uses: ./.github/workflows/docker-ci-images-refresh.yml
secrets: inherit
#
# Only the multi-server tests, `docker-crossbuild` and `docker-service`
# read the `freeradius4-{crossbuild,profiling-deps,service}` images,
# and the 'freeradius4' images take the longest to build. The
# 'freeradius4' refresh therefore runs as a separate job, and only the
# legs that read the 'freeradius4' images wait for the refresh. The
# build legs start as soon as `docker_ci_images_refresh` publishes the
# images that the build legs read.
#
docker_freeradius_images_refresh:
name: Docker FreeRADIUS images refresh
needs: [docker_ci_images_refresh]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result == 'success' }}
uses: ./.github/workflows/docker-freeradius-images-refresh.yml
secrets: inherit
ci:
needs: [docker_ci_images_refresh, ci_prev_run]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: CI
uses: ./.github/workflows/ci.yml
secrets: inherit
ci-deb:
needs: [docker_ci_images_refresh, ci_prev_run]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: CI DEB
uses: ./.github/workflows/ci-deb.yml
secrets: inherit
ci-rpm:
needs: [docker_ci_images_refresh, ci_prev_run]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: CI RPM
uses: ./.github/workflows/ci-rpm.yml
secrets: inherit
ci-sanitizers:
needs: [docker_ci_images_refresh, ci_prev_run]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: CI-Sanitizers
uses: ./.github/workflows/ci-sanitizers.yml
secrets: inherit
#
# `ci-freebsd` and `ci-macos` run on every commit but are absent
# from the `needs` list of the `merge` job, because both legs are too unreliable. Both legs run
# on virtual machines and read no docker images, so neither leg waits
# for a refresh.
#
ci-freebsd:
needs: [ci_prev_run]
if: ${{ !cancelled()
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: CI FreeBSD
uses: ./.github/workflows/ci-freebsd.yml
secrets: inherit
ci-macos:
needs: [ci_prev_run]
if: ${{ !cancelled()
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: CI macOS
uses: ./.github/workflows/ci-macos.yml
secrets: inherit
#
# `docker_freeradius_images_refresh` skips whenever `docker_ci_images_refresh` skips or fails,
# so the legs that read the 'freeradius4' images check both results.
# The legs must not read stale images after a skipped
# `docker_freeradius_images_refresh` that follows a failed `docker_ci_images_refresh`.
#
ci-multi-server:
needs: [docker_ci_images_refresh, docker_freeradius_images_refresh, ci_prev_run]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.docker_freeradius_images_refresh.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true' }}
name: Multi-Server CI Tests
uses: ./.github/workflows/ci-multi-server-tests.yml
secrets: inherit
docker-crossbuild:
name: Docker crossbuild images
needs: [docker_ci_images_refresh, docker_freeradius_images_refresh, ci]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.docker_freeradius_images_refresh.result != 'failure'
&& needs.ci.result == 'success' && github.repository_owner == 'FreeRADIUS' }}
uses: ./.github/workflows/docker-crossbuild.yml
secrets: inherit
docker-service:
name: Docker service images
needs: [docker_ci_images_refresh, docker_freeradius_images_refresh, ci]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.docker_freeradius_images_refresh.result != 'failure'
&& needs.ci.result == 'success' && github.repository_owner == 'FreeRADIUS' }}
uses: ./.github/workflows/docker-service.yml
secrets: inherit
#
# `docs` only runs when the commit changes a file that the
# documentation build reads. `docs` is absent from the `needs` list of the `merge` job,
# because a failed documentation build should not hold up a merge.
#
docs:
name: Documentation
needs: [common_vars, docker_ci_images_refresh, ci_prev_run]
if: ${{ !cancelled() && needs.docker_ci_images_refresh.result != 'failure'
&& needs.ci_prev_run.outputs.should_skip != 'true'
&& needs.common_vars.outputs.docs_changed == 'true' }}
uses: ./.github/workflows/documentation.yml
secrets: inherit
#
# `merge` runs when every gating leg passed. The `needs` list holds only
# the gating legs. The other legs are too unreliable to block a
# merge on.
#
# By default GitHub skips a job unless every job in the job's `needs`
# succeeds. The docker refresh skips on commits that do not modify
# packaging. A skipped refresh would skip the merge step, unless the
# merge step carries an explicit condition.
#
# A skipped pipeline still merges. The duplicate check only reports
# a skip once an earlier run of the 'CI Pipeline' workflow finished
# successfully on the same tree. The merged content has therefore
# already passed. Requiring the legs to report `success` in the merge
# condition would strand a developer who amends a commit message and
# force pushes. The reworded commit carries the tree that just
# passed, so every leg skips.
#
merge:
name: Merge
needs: [ci_prev_run, ci, ci-deb, ci-rpm, ci-sanitizers]
if: ${{ !cancelled()
&& (needs.ci_prev_run.outputs.should_skip == 'true'
|| (needs.ci.result == 'success'
&& needs.ci-deb.result == 'success'
&& needs.ci-rpm.result == 'success'
&& needs.ci-sanitizers.result == 'success')) }}
uses: ./.github/workflows/merge-upstream.yml
secrets:
GH_APP_PRIVATE_KEY_CI_MERGE: ${{ secrets.GH_APP_PRIVATE_KEY_CI_MERGE }}