Skip to content
Draft
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
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/pull_request.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
61 changes: 57 additions & 4 deletions .github/workflows/scripts/check-docs.sh
Original file line number Diff line number Diff line change
Expand Up @@ -17,9 +17,61 @@ log() { printf -- "** %s\n" "$*" >&2; }
error() { printf -- "** ERROR: %s\n" "$*" >&2; }
fatal() { error "$@"; exit 1; }

usage() {
cat <<EOF
Usage: $(basename "$0") [options]

Options:
--no-analyze Do not pass --analyze to 'swift package plugin generate-documentation'.
--additional-docc-arguments <args...> 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
Expand Down Expand Up @@ -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."
24 changes: 22 additions & 2 deletions .github/workflows/soundness.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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."
Expand All @@ -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."
Expand Down Expand Up @@ -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)
Expand Down Expand Up @@ -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
Expand Down
2 changes: 1 addition & 1 deletion docs/pr-dependency-workflow.md
Original file line number Diff line number Diff line change
Expand Up @@ -29,5 +29,5 @@ permissions:

jobs:
check_dependencies:
uses: swiftlang/github-workflows/.github/workflows/github_actions_dependencies.yml.yml@<to-be-updated>
uses: swiftlang/github-workflows/.github/workflows/github_actions_dependencies.yml@<to-be-updated>
```
135 changes: 135 additions & 0 deletions docs/soundness-docs-check.md
Original file line number Diff line number Diff line change
@@ -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@<to-be-updated>>
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, <version>, <arch>]`.

## 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. |
5 changes: 5 additions & 0 deletions tests/TestPackage/.spi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
version: 1
builder:
configs:
- documentation_targets:
- theDocs
1 change: 1 addition & 0 deletions tests/TestPackage/Package.swift
Original file line number Diff line number Diff line change
Expand Up @@ -18,5 +18,6 @@ let package = Package(
name: "Target1Tests",
dependencies: ["Target1"]
),
.target(name: "theDocs"),
]
)
3 changes: 3 additions & 0 deletions tests/TestPackage/Sources/theDocs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
# Document title

This is a sample document.
12 changes: 12 additions & 0 deletions tests/TestPackage/Sources/theDocs/theDocs.swift
Original file line number Diff line number Diff line change
@@ -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
//
//===----------------------------------------------------------------------===//

Loading