Skip to content

Doxygen second pass: coverage, improvements, fixes, etc. #10

Doxygen second pass: coverage, improvements, fixes, etc.

Doxygen second pass: coverage, improvements, fixes, etc. #10

Workflow file for this run

name: Generate docs
permissions:
contents: read
# Builds the Doxygen docs and uploads them as a workflow artifact (without
# publishing). Two roles:
#
# 1. Dry-run on every PR that touches C++ code (or other Doxygen inputs) so
# docs breakage is caught before merge. The artifact is attached to the
# workflow run so reviewers can download a preview.
# 2. Reusable workflow consumed by publish-docs.yml: builds the docs, prints
# the resolved version, and uploads the docs/doxygen/html/ directory as an
# artifact for a downstream publish job to consume. Avoids double-building
# the docs.
#
# Artifact layout: actions/upload-artifact@v4+ uses the longest common path of
# all uploaded files as the artifact root. With `path: docs/doxygen/html/`, the
# artifact contains the *contents* of that directory at the artifact root
# (index.html, favicon.ico, …) -- NOT nested under docs/doxygen/html/. The
# `html/` prefix that publish-docs.yml relies on comes from the *download*
# step's `path: html` parameter, not from the artifact itself. The
# "Re-download artifact" + "Inspect downloaded artifact" steps below prove
# this end-to-end on every run by re-downloading with `path: html` and
# asserting `html/index.html` is present.
on:
pull_request:
branches: ["main"]
paths:
- include/**
- src/**
- examples/**
- docs/**
- README.md
- scripts/generate-docs.sh
- .github/workflows/generate-docs.yml
- .github/workflows/publish-docs.yml
workflow_call:
inputs:
version:
description: 'Documentation version (e.g. v0.1.0)'
required: false
type: string
upload_artifact:
description: 'Upload the built docs/doxygen/html/ tree as a workflow artifact'
required: false
type: boolean
default: true
artifact_name:
description: 'Name to use for the uploaded docs artifact'
required: false
type: string
default: livekit-cpp-docs
artifact_retention_days:
description: 'Retention (days) for the uploaded docs artifact. Default tuned for PR-preview use.'
required: false
type: number
default: 7
outputs:
project_number:
description: 'Doxygen PROJECT_NUMBER used for the build (e.g., v1.2.3)'
value: ${{ jobs.validate.outputs.project_number }}
artifact_name:
description: 'Name of the uploaded docs artifact (empty if upload was skipped via upload_artifact: false)'
value: ${{ jobs.validate.outputs.artifact_name }}
jobs:
validate:
name: Generate and verify docs
runs-on: ubuntu-latest
outputs:
project_number: ${{ steps.build_docs.outputs.project_number }}
artifact_name: ${{ steps.artifact_meta.outputs.name }}
steps:
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
with:
# fetch-depth: 0 is required so `git describe --tags` in
# scripts/generate-docs.sh resolves the right version from the git history.
fetch-depth: 0
# Doxygen is available on ubuntu-latest, but we install explicitly for stability
- name: Install Doxygen
run: |
sudo apt-get update
sudo apt-get install -y doxygen graphviz
- name: Generate docs (Doxygen)
id: build_docs
shell: bash
env:
INPUT_VERSION: ${{ inputs.version || '' }}
run: |
set -euo pipefail
args=()
if [[ -n "$INPUT_VERSION" ]]; then
args+=(--version "$INPUT_VERSION")
elif [[ "${{ github.ref_type }}" == "tag" ]]; then
args+=(--version "${{ github.ref_name }}")
fi
./scripts/generate-docs.sh "${args[@]}"
- name: Print docs version
shell: bash
run: |
set -euo pipefail
PROJECT_NUMBER="${{ steps.build_docs.outputs.project_number }}"
if [[ -z "$PROJECT_NUMBER" ]]; then
echo "ERROR: build_docs step did not emit a project_number output."
exit 1
fi
echo "Docs version: ${PROJECT_NUMBER}"
{
echo "Version: \`${PROJECT_NUMBER}\`"
echo ""
echo "> Note: On a non-tag/release run, the version will resolve to:"
echo " \`<closest tag>-<commits since tag>-<commit sha>\`"
} >>"$GITHUB_STEP_SUMMARY"
- name: Verify docs were generated
shell: bash
run: |
set -euo pipefail
if [[ ! -f docs/doxygen/html/index.html ]]; then
echo "ERROR: Expected docs at docs/doxygen/html/index.html but file not found."
exit 1
fi
# `inputs.*` is only populated on `workflow_call`. On a direct
# `pull_request` trigger every `inputs.*` lookup is the empty string, so
# we OR-fold to the documented defaults to keep PR runs behaving the same
# as the workflow_call default of `upload_artifact: true`.
- name: Resolve artifact metadata
id: artifact_meta
if: inputs.upload_artifact || github.event_name == 'pull_request'
shell: bash
env:
INPUT_NAME: ${{ inputs.artifact_name || 'livekit-cpp-docs' }}
INPUT_RETENTION: ${{ inputs.artifact_retention_days || '7' }}
run: |
set -euo pipefail
if [[ -z "$INPUT_NAME" ]]; then
echo "ERROR: artifact name resolved to empty."
exit 1
fi
echo "name=${INPUT_NAME}" >>"$GITHUB_OUTPUT"
echo "retention=${INPUT_RETENTION}" >>"$GITHUB_OUTPUT"
- name: Upload docs artifact
if: steps.artifact_meta.outputs.name != ''
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.artifact_meta.outputs.name }}
path: docs/doxygen/html/
retention-days: ${{ steps.artifact_meta.outputs.retention }}
if-no-files-found: error
# Empirical proof of the publish-docs.yml contract: re-download the
# just-uploaded artifact using the *same* `path: html` value that
# publish-docs.yml uses, and assert html/index.html exists. The artifact
# itself is flat (its root contains index.html, favicon.ico, …); the
# `html/` prefix comes purely from the download step's `path:`. If
# actions/upload-artifact ever changes its "longest common path" root
# behavior, this assertion fails loudly.
- name: Re-download artifact (mirrors publish-docs.yml exactly)
if: steps.artifact_meta.outputs.name != ''
uses: actions/download-artifact@37930b1c2abaa49bbe596cd826c3c89aef350131 # v7.0.0
with:
name: ${{ steps.artifact_meta.outputs.name }}
path: html
- name: Inspect downloaded artifact
if: steps.artifact_meta.outputs.name != ''
shell: bash
run: |
set -euo pipefail
NAME="${{ steps.artifact_meta.outputs.name }}"
ROOT="html"
echo "Artifact '${NAME}' extracted into '${ROOT}/'. Top-level contents:"
# `sed -n '1,Np'` reads the whole pipe before truncating its output,
# so the producer (ls) never hits SIGPIPE under `pipefail`.
ls -la "$ROOT" | sed -n '1,40p'
TOTAL=$(find "$ROOT" -type f | wc -l | tr -d ' ')
echo "Total files: ${TOTAL}"
if [[ ! -f "${ROOT}/index.html" ]]; then
echo "ERROR: ${ROOT}/index.html is missing -- artifact layout regressed."
echo " publish-docs.yml downloads with the same 'path: html' and"
echo " expects html/index.html."
exit 1
fi
SAMPLE=$( (cd "$ROOT" && ls -1) | sed -n '1,20p' )
{
echo "### Docs artifact: \`${NAME}\`"
echo ""
echo "Uploaded ${TOTAL} files. The artifact's *contents* live at"
echo "the artifact root (there is no \`html/\` prefix inside the"
echo "artifact itself). The \`html/\` directory below comes from"
echo "this step's \`path: html\`, which mirrors \`publish-docs.yml\`:"
echo ""
echo '```'
echo "${SAMPLE}"
echo '```'
echo ""
echo "Layout confirmed: \`html/index.html\` is present, so the"
echo "\`aws s3 cp html/ s3://… --recursive\` step in"
echo "\`publish-docs.yml\` will publish \`index.html\`, \`favicon.ico\`,"
echo "etc. at the bucket root."
} >>"$GITHUB_STEP_SUMMARY"