From f31e5a2e9ff91fa3fed309783d226a622c48b19a Mon Sep 17 00:00:00 2001 From: Bassam Khouri Date: Tue, 9 Jun 2026 15:48:39 -0400 Subject: [PATCH] Docs: provide option to skip analysis in doc-check Some projects may not want to run the `--analyze` option on the DocC generation until they are ready. Provide an workflow input option to skip the analyze. While at it, update the `check-docs.sh` script to accept arugments instead of relying on environment variables being set. This allows more easily run the script at-desk. Fixes #2181 --- .github/workflows/pull_request.yml | 1 + .github/workflows/scripts/check-docs.sh | 61 +++++++- .github/workflows/soundness.yml | 24 +++- docs/pr-dependency-workflow.md | 2 +- docs/soundness-docs-check.md | 135 ++++++++++++++++++ tests/TestPackage/.spi.yml | 5 + tests/TestPackage/Package.swift | 1 + tests/TestPackage/Sources/theDocs/README.md | 3 + .../TestPackage/Sources/theDocs/theDocs.swift | 12 ++ 9 files changed, 237 insertions(+), 7 deletions(-) create mode 100644 docs/soundness-docs-check.md create mode 100644 tests/TestPackage/.spi.yml create mode 100644 tests/TestPackage/Sources/theDocs/README.md create mode 100644 tests/TestPackage/Sources/theDocs/theDocs.swift diff --git a/.github/workflows/pull_request.yml b/.github/workflows/pull_request.yml index b451cfa9..0fc1937c 100644 --- a/.github/workflows/pull_request.yml +++ b/.github/workflows/pull_request.yml @@ -95,4 +95,5 @@ jobs: uses: ./.github/workflows/soundness.yml with: api_breakage_check_enabled: false + docs_check_enabled: false license_header_check_project_name: "Swift.org" diff --git a/.github/workflows/scripts/check-docs.sh b/.github/workflows/scripts/check-docs.sh index d0becf00..a5d95ee7 100755 --- a/.github/workflows/scripts/check-docs.sh +++ b/.github/workflows/scripts/check-docs.sh @@ -17,9 +17,61 @@ log() { printf -- "** %s\n" "$*" >&2; } error() { printf -- "** ERROR: %s\n" "$*" >&2; } fatal() { error "$@"; exit 1; } +usage() { + cat < Extra arguments forwarded to 'swift package plugin generate-documentation'. + Consumes all remaining tokens until the next known option + (--no-analyze, -h, --help) or end of arguments. + -h, --help Show this help message. +EOF +} + +is_known_option() { + case "$1" in + --no-analyze|--additional-docc-arguments|-h|--help) + return 0 + ;; + *) + return 1 + ;; + esac +} + +analyze_flag="--analyze" +additional_docc_arguments="" +while [[ $# -gt 0 ]]; do + case "$1" in + --no-analyze) + analyze_flag="" + shift + ;; + --additional-docc-arguments) + shift + collected=() + while [[ $# -gt 0 ]] && ! is_known_option "$1"; do + collected+=("$1") + shift + done + additional_docc_arguments="${collected[*]}" + ;; + -h|--help) + usage + exit 0 + ;; + *) + error "Unknown argument: $1" + usage >&2 + exit 2 + ;; + esac +done + if [ ! -f .spi.yml ]; then - log "No '.spi.yml' found, no documentation targets to check." - exit 0 + fatal "No '.spi.yml' found. Failing as there is no documentation targets to check. Learn more at https://github.com/swiftlang/github-workflows/blob/main/docs/soundness-docs-check.md" fi if ! command -v yq &> /dev/null; then @@ -55,8 +107,9 @@ fi log "Checking documentation targets..." for target in $(yq -r '.builder.configs[].documentation_targets[]' .spi.yml); do log "Checking target $target..." - # shellcheck disable=SC2086 # We explicitly want to explode "$ADDITIONAL_DOCC_ARGUMENTS" into multiple arguments. - swift package plugin generate-documentation --target "$target" --warnings-as-errors --analyze $ADDITIONAL_DOCC_ARGUMENTS + target_docc_arguments=$(yq ".builder.configs[] | select(.documentation_targets[] == \"${target}\") | .custom_documentation_parameters[]" .spi.yml) + # shellcheck disable=SC2086 # We explicitly want to explode "$analyze_flag", "$additional_docc_arguments" and "$target_docc_arguments" into multiple arguments. + swift package plugin generate-documentation --target "$target" --warnings-as-errors $analyze_flag $additional_docc_arguments $target_docc_arguments done log "✅ Found no documentation issues." diff --git a/.github/workflows/soundness.yml b/.github/workflows/soundness.yml index 134748e7..9c848894 100644 --- a/.github/workflows/soundness.yml +++ b/.github/workflows/soundness.yml @@ -31,6 +31,10 @@ on: type: string description: "Additional arguments that should be passed to docc" default: "" + docs_check_analyze: + type: boolean + description: "Boolean to pass --analyze to the docs check. Defaults to true." + default: true docs_check_macos_enabled: type: boolean description: "Boolean to enable the macOS docs check job. Defaults to false." @@ -51,6 +55,10 @@ on: type: string description: "Additional arguments that should be passed to docc for the macOS docs check job." default: "" + docs_check_macos_analyze: + type: boolean + description: "Boolean to pass --analyze to the macOS docs check. Defaults to true." + default: true unacceptable_language_check_enabled: type: boolean description: "Boolean to enable the acceptable language check job. Defaults to true." @@ -187,8 +195,14 @@ jobs: - name: Run documentation check env: ADDITIONAL_DOCC_ARGUMENTS: ${{ inputs.docs_check_additional_arguments }} + DOCC_ANALYZE: ${{ inputs.docs_check_analyze }} SCRIPT_ROOT: ${{ steps.script_path.outputs.root }} - run: ${SCRIPT_ROOT}/.github/workflows/scripts/check-docs.sh + run: | + analyze_arg="" + if [[ "${DOCC_ANALYZE}" != "true" ]]; then + analyze_arg="--no-analyze" + fi + "${SCRIPT_ROOT}/.github/workflows/scripts/check-docs.sh" ${analyze_arg} --additional-docc-arguments ${ADDITIONAL_DOCC_ARGUMENTS} docs-check-macos: name: Documentation check (macOS) @@ -226,8 +240,14 @@ jobs: - name: Run documentation check env: ADDITIONAL_DOCC_ARGUMENTS: ${{ inputs.docs_check_macos_additional_arguments }} + DOCC_ANALYZE: ${{ inputs.docs_check_macos_analyze }} SCRIPT_ROOT: ${{ steps.script_path.outputs.root }} - run: ${SCRIPT_ROOT}/.github/workflows/scripts/check-docs.sh + run: | + analyze_arg="" + if [[ "${DOCC_ANALYZE}" != "true" ]]; then + analyze_arg="--no-analyze" + fi + "${SCRIPT_ROOT}/.github/workflows/scripts/check-docs.sh" ${analyze_arg} --additional-docc-arguments ${ADDITIONAL_DOCC_ARGUMENTS} unacceptable-language-check: name: Unacceptable language check diff --git a/docs/pr-dependency-workflow.md b/docs/pr-dependency-workflow.md index 3e0eb07c..f7992683 100644 --- a/docs/pr-dependency-workflow.md +++ b/docs/pr-dependency-workflow.md @@ -29,5 +29,5 @@ permissions: jobs: check_dependencies: - uses: swiftlang/github-workflows/.github/workflows/github_actions_dependencies.yml.yml@ + uses: swiftlang/github-workflows/.github/workflows/github_actions_dependencies.yml@ ``` diff --git a/docs/soundness-docs-check.md b/docs/soundness-docs-check.md new file mode 100644 index 00000000..c87cf449 --- /dev/null +++ b/docs/soundness-docs-check.md @@ -0,0 +1,135 @@ +# Documentation Check + +The Soundness workflow can verify that your Swift package's [DocC](https://www.swift.org/documentation/docc/) documentation builds without warnings. Two jobs are available: + +- **`docs-check`** — runs on Linux. Enabled by default. +- **`docs-check-macos`** — runs on a self-hosted macOS runner. Opt-in. + +Running both lets you catch documentation issues that only surface on one toolchain. + +Documentation warnings (and, by default, DocC analyzer findings) cause the job to fail. + +## Requirements + +For either job to run, your repository needs: + +1. A `Package.swift` (or any `Package*.swift`) at the repository root. +2. A `.spi.yml` at the repository root listing the targets to document. Read the [official documentation](https://swiftpackageindex.com/SwiftPackageIndex/SPIManifest/1.12.0/documentation/spimanifest/commonusecases) on how to generate the `.spi.yml`. For example: + + ```yaml + version: 1 + builder: + configs: + - documentation_targets: [MyLibrary, MyOtherLibrary] + custom_documentation_parameters: + - --include-extended-types + ``` + + Targets listed under `documentation_targets` must match real SwiftPM target names. Any `custom_documentation_parameters` are forwarded to DocC for that group of targets. + +You do not need to add `swift-docc-plugin` to your package — CI provides it for you. + +## Enabling the check + +Add (or extend) a workflow file under `.github/workflows/` in your repository: + +```yaml +name: Pull request + +on: + pull_request: + branches: [main] + +jobs: + soundness: + name: Soundness + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@> + with: + docs_check_enabled: true +``` + +This enables the Linux documentation check along with the other soundness checks. The macOS variant remains off unless you opt in. + +## Configuration + +### Linux job (`docs-check`) + +| Input | Type | Default | Description | +|---|---|---|---| +| `docs_check_enabled` | boolean | `true` | Enable or disable the job. | +| `docs_check_container_image` | string | `swift:6.2-noble` | Docker image used to run the check. | +| `docs_check_additional_arguments` | string | `""` | Extra arguments to pass to DocC. | +| `docs_check_analyze` | boolean | `true` | Set to `false` to skip DocC's analyzer pass. | +| `linux_pre_build_command` | string | `""` | Shell command to run before the check (e.g., installing system dependencies). | + +### macOS job (`docs-check-macos`) + +| Input | Type | Default | Description | +|---|---|---|---| +| `docs_check_macos_enabled` | boolean | `false` | Enable or disable the job. | +| `docs_check_macos_version` | string | `tahoe` | macOS version label of the runner to target. | +| `docs_check_macos_arch` | string | `ARM64` | Architecture label of the runner to target. | +| `docs_check_macos_xcode_version` | string | `26.0` | Xcode version to use. | +| `docs_check_macos_additional_arguments` | string | `""` | Extra arguments to pass to DocC. | +| `docs_check_macos_analyze` | boolean | `true` | Set to `false` to skip DocC's analyzer pass. | + +The macOS job requires a self-hosted runner registered with the label set `[self-hosted, macos, , ]`. + +## Common scenarios + +### Enable the macOS check + +```yaml +jobs: + soundness: + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + docs_check_macos_enabled: true + docs_check_macos_version: "tahoe" + docs_check_macos_arch: "ARM64" + docs_check_macos_xcode_version: "26.0" +``` + +### Pin a different Swift toolchain or pass extra DocC flags + +```yaml +jobs: + soundness: + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + docs_check_container_image: "swift:nightly-noble" + docs_check_additional_arguments: "--include-extended-types" + linux_pre_build_command: "apt-get update && apt-get install -y libxml2-dev" +``` + +### Skip the DocC analyzer + +```yaml +jobs: + soundness: + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + docs_check_analyze: false +``` + +### Disable the check + +If your package has no documentation targets, disable the job rather than letting it fail on a missing `.spi.yml`: + +```yaml +jobs: + soundness: + uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main + with: + docs_check_enabled: false +``` + +## Troubleshooting + +| Symptom | Likely cause | +|---|---| +| `No '.spi.yml' found.` | Add a `.spi.yml` at the repo root, or disable the job. | +| `Package.swift not found.` | The check expects a SwiftPM package at the repo root. | +| Warnings cause the job to fail. | Intentional. Resolve the DocC warnings, or pass DocC flags via `.spi.yml`'s `custom_documentation_parameters` to suppress them. | +| macOS job stays queued. | No self-hosted runner matches the requested labels. Verify the `version` and `arch` inputs against your runner inventory. | +| macOS job cannot find Xcode. | The requested Xcode version is not installed on the runner. | diff --git a/tests/TestPackage/.spi.yml b/tests/TestPackage/.spi.yml new file mode 100644 index 00000000..ae183cfb --- /dev/null +++ b/tests/TestPackage/.spi.yml @@ -0,0 +1,5 @@ +version: 1 +builder: + configs: + - documentation_targets: + - theDocs diff --git a/tests/TestPackage/Package.swift b/tests/TestPackage/Package.swift index 2212f8a6..079bbb33 100644 --- a/tests/TestPackage/Package.swift +++ b/tests/TestPackage/Package.swift @@ -18,5 +18,6 @@ let package = Package( name: "Target1Tests", dependencies: ["Target1"] ), + .target(name: "theDocs"), ] ) diff --git a/tests/TestPackage/Sources/theDocs/README.md b/tests/TestPackage/Sources/theDocs/README.md new file mode 100644 index 00000000..e76baea0 --- /dev/null +++ b/tests/TestPackage/Sources/theDocs/README.md @@ -0,0 +1,3 @@ +# Document title + +This is a sample document. diff --git a/tests/TestPackage/Sources/theDocs/theDocs.swift b/tests/TestPackage/Sources/theDocs/theDocs.swift new file mode 100644 index 00000000..8ad45948 --- /dev/null +++ b/tests/TestPackage/Sources/theDocs/theDocs.swift @@ -0,0 +1,12 @@ +//===----------------------------------------------------------------------===// +// +// This source file is part of the Swift.org open source project +// +// Copyright (c) 2026 Apple Inc. and the Swift project authors +// Licensed under Apache License v2.0 with Runtime Library Exception +// +// See https://swift.org/LICENSE.txt for license information +// See https://swift.org/CONTRIBUTORS.txt for the list of Swift project authors +// +//===----------------------------------------------------------------------===// +