diff --git a/.github/workflows/api_breakage.yml b/.github/workflows/api_breakage.yml deleted file mode 100644 index 2374f4a..0000000 --- a/.github/workflows/api_breakage.yml +++ /dev/null @@ -1,36 +0,0 @@ -name: API breaking changes - -on: - workflow_call: - inputs: {} - -concurrency: - group: ${{ github.workflow }}-${{ github.ref }}-api-breakage - cancel-in-progress: true - -jobs: - - check-api-breakage: - name: Check API breakage - runs-on: ubuntu-latest - - container: - image: swift:latest - - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - fetch-depth: 0 - - - name: Install curl - run: | - apt-get update -y - apt-get install -y curl - - - name: Mark repo as safe for Git - run: git config --global --add safe.directory "$GITHUB_WORKSPACE" - - - name: Run check API breakage - run: | - curl -s https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts/check-api-breakage.sh | bash \ No newline at end of file diff --git a/.github/workflows/docc_deploy.yml b/.github/workflows/docc_deploy.yml index ab98e22..784c08b 100644 --- a/.github/workflows/docc_deploy.yml +++ b/.github/workflows/docc_deploy.yml @@ -32,8 +32,13 @@ jobs: apt-get install -y curl jq - name: Generate DocC documentation + env: + REPOSITORY_NAME: ${{ github.event.repository.name }} run: | - curl -s https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts/generate-docc.sh | bash -s -- --name ${{ github.event.repository.name }} + curl --fail --show-error --location --silent \ + https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts/generate-docc.sh \ + --output "$RUNNER_TEMP/generate-docc.sh" + bash "$RUNNER_TEMP/generate-docc.sh" --name "$REPOSITORY_NAME" - name: Upload DocC artifact uses: actions/upload-pages-artifact@v3 @@ -56,4 +61,4 @@ jobs: steps: - name: Deploy to GitHub Pages id: deploy - uses: actions/deploy-pages@v4 \ No newline at end of file + uses: actions/deploy-pages@v4 diff --git a/.github/workflows/extra_soundness.yml b/.github/workflows/extra_soundness.yml index 9317738..7283a35 100644 --- a/.github/workflows/extra_soundness.yml +++ b/.github/workflows/extra_soundness.yml @@ -23,10 +23,6 @@ on: type: string description: "List of Swift versions to test with." default: '["6.1", "6.2"]' - docc_warnings_check_enabled: - type: boolean - description: "Runs DocC analysis and fails on warnings. Defaults to false." - default: false swift_package_validation_enabled: type: boolean description: "Runs Swift package validation script (structure, settings, files). Defaults to false." @@ -82,25 +78,6 @@ jobs: run: | curl -s https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts/check-swift-package.sh | bash - docc_warnings_check: - name: DocC warnings check - if: ${{ inputs.docc_warnings_check_enabled }} - runs-on: ubuntu-latest - container: - image: swift:6.2 - steps: - - name: Checkout repository - uses: actions/checkout@v6 - with: - persist-credentials: false - - name: Install curl + jq - run: | - apt-get update -y - apt-get install -y curl jq - - name: Run DocC warnings check - run: | - curl -s https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts/check-docc-warnings.sh | bash - cache-and-test: name: Run tests with cache env: diff --git a/Makefile b/Makefile index beb0b09..af7548f 100644 --- a/Makefile +++ b/Makefile @@ -2,12 +2,6 @@ SHELL=/bin/bash baseUrl = https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts -breakage: - curl -s $(baseUrl)/check-api-breakage.sh | bash - -symlinks: - curl -s $(baseUrl)/check-broken-symlinks.sh | bash - ## params: --local: generate for local testing docc-generate: curl -s $(baseUrl)/generate-docc.sh | bash @@ -15,9 +9,6 @@ docc-generate: docc-local: curl -s $(baseUrl)/generate-docc.sh | bash -s -- --local -docc-warnings: - curl -s $(baseUrl)/check-docc-warnings.sh | bash - ## params: -n: name, -p: port run-docc: curl -s $(baseUrl)/run-docc-docker.sh | bash @@ -37,9 +28,6 @@ openapi-security: openapi-validation: curl -s $(baseUrl)/check-openapi-validation.sh | bash -language: - curl -s $(baseUrl)/check-unacceptable-language.sh | bash - contributors: curl -s $(baseUrl)/generate-contributors-list.sh | bash @@ -75,7 +63,7 @@ format-shell: lint-workflows: curl -s $(baseUrl)/run-actionlint.sh | bash -check: symlinks language deps lint docc-warnings headers +check: deps lint headers test-bats: bats --recursive tests/bats diff --git a/README.md b/README.md index 7627c5f..accd030 100644 --- a/README.md +++ b/README.md @@ -21,11 +21,10 @@ This section details the reusable workflows provided by the repository. ### 1. Extra Soundness Workflow (`extra_soundness.yml`) -This workflow provides configurable, robust checks and testing: +This workflow provides Binary Birds-specific checks and testing alongside the standard checks in [swiftlang/github-workflows](https://github.com/swiftlang/github-workflows): * **Optional Local Swift Dependency Checks**: Checks for accidental `.package(path:)` usage. * **Optional Swift Headers Check**: Validates Swift source file headers using a strict 5-line format and respects `.swiftheaderignore`. -* **Optional DocC Warnings Check**: Runs DocC analysis with `--warnings-as-errors` and fails on warnings. * **Optional Swift Test Execution**: Runs tests with optional **`.build` caching** for efficiency. * **Optional Swift Package Validation**: Validates Swift package structure, settings, and conventions to ensure consistency. * **Multi-Version Support**: Tests across multiple Swift versions, configurable via input (defaulting to `["6.0", "6.1"]`). @@ -37,7 +36,6 @@ This workflow provides configurable, robust checks and testing: | ------ | ------------ | --------- | | `local_swift_dependencies_check_enabled` | Enables local Swift dependency check | `false` | | `headers_check_enabled` | Enables Swift headers validation | `false` | -| `docc_warnings_check_enabled` | Enables DocC warnings check | `false` | | `run_tests_with_cache_enabled` | Enables the Swift test job | `false` | | `tests_cache_enabled` | Enables `.build` cache restore/save in the Swift test job | `true` | | `run_tests_swift_versions` | Swift versions to test | `["6.1","6.2"]` | @@ -52,7 +50,6 @@ jobs: with: local_swift_dependencies_check_enabled: true headers_check_enabled: true - docc_warnings_check_enabled: true run_tests_with_cache_enabled: true tests_cache_enabled: true run_tests_swift_versions: '["6.1","6.2"]' @@ -79,22 +76,6 @@ jobs: id-token: write ``` -### 3. API Breaking Changes Workflow (`api_breakage.yml`) - -This workflow checks for API breaking changes in the repository: - -* Runs API Breakage Checks: Executes the Binary Birds API breakage checking script. -* Swift-based Environment: Runs inside a swift:latest Docker container. -* Full Git History Available: Checks out the repository with complete history for accurate comparison. - -#### Example Usage (Caller Repository) - -```yaml -jobs: - check-api-breakage: - uses: BinaryBirds/github-workflows/.github/workflows/api_breakage.yml@main -``` - ----- ## Makefile Usage @@ -119,114 +100,6 @@ These combined commands run several scripts in sequence to provide a quick, cons The `Makefile` uses the variable `baseUrl` which points to the source of all scripts: `https://raw.githubusercontent.com/BinaryBirds/github-workflows/refs/heads/main/scripts` -### DocC dependency placeholder requirement - -DocC-related scripts rely on a placeholder inside `Package.swift` to safely inject the `swift-docc-plugin` dependency when needed. - -Your `Package.swift` must contain a top-level `dependencies` section (even if empty) and include the following comment: - -```swift -// [docc-plugin-placeholder] -``` - -This placeholder defines the only supported injection point. If either the dependencies section or the placeholder is missing, DocC scripts will fail intentionally. - -```swift -dependencies: [ - // [docc-plugin-placeholder] -] -``` - -The placeholder itself has no effect on builds and does not modify target-level dependencies. - -### check-api-breakage.sh - -#### Purpose - -Detects source- and binary-level API breaking changes in Swift packages to prevent unintended public API regressions. - -#### Behavior - -* Uses `swift package diagnose-api-breaking-changes` -* Pull requests: fetches `${GITHUB_BASE_REF}` into a local `pull-base-ref` and compares against that ref -* Other contexts: fetches tags and compares against the latest Git tag -* If no tags exist, exits successfully with a warning -* Fails when breaking changes are detected - -#### Parameters - -_None_ - -#### Ignore files - -_None_ - -#### Raw curl example - -```sh -curl -s $(baseUrl)/check-api-breakage.sh | bash -``` - ---- - -### check-broken-symlinks.sh - -#### Purpose - -Ensures all git-tracked symbolic links resolve to valid targets. - -#### Behavior - -* Inspects only symbolic links -* Ignores regular missing files -* Reports each broken symlink explicitly -* Exits non-zero if any broken symlink is found - -#### Parameters - -_None_ - -#### Ignore files - -_None_ - -#### Raw curl example - -```sh -curl -s $(baseUrl)/check-broken-symlinks.sh | bash -``` - ---- - -### check-docc-warnings.sh - -#### Purpose - -Runs DocC documentation analysis in strict mode, treating warnings as errors. - -#### Behavior - -* Uses `.docctargetlist` when present, otherwise auto-detects targets -* Temporarily injects `swift-docc-plugin` if missing -* Requires a clean git working tree locally -* Restores git state after execution - -#### Parameters - -_None_ - -#### Ignore files - -* `.docctargetlist` – explicitly defines documented targets - -#### Raw curl example - -```sh -curl -s $(baseUrl)/check-docc-warnings.sh | bash -``` - ---- - ### check-local-swift-dependencies.sh #### Purpose @@ -376,34 +249,6 @@ curl -s $(baseUrl)/check-swift-headers.sh | bash -s -- --fix --author "John Doe" --- -### check-unacceptable-language.sh - -#### Purpose - -Detects discouraged or outdated terminology to promote inclusive language. - -#### Behavior - -* Case-insensitive, whole-word matching -* Scans git-tracked files only -* Lines containing `ignore-unacceptable-language` are excluded from failures - -#### Parameters - -_None_ - -#### Ignore files - -* `.unacceptablelanguageignore` – excludes paths from scanning - -#### Raw curl example - -```sh -curl -s $(baseUrl)/check-unacceptable-language.sh | bash -``` - ---- - ### generate-contributors-list.sh #### Purpose @@ -445,6 +290,8 @@ Generates DocC documentation into the `docs/` directory. * Supports multiple Swift targets * Enables combined documentation automatically * Can transform output for static hosting +* Temporarily adds `swift-docc-plugin` with SwiftPM when needed; no manifest placeholder is required +* Restores the original `Package.swift` after generation #### Parameters @@ -747,7 +594,7 @@ Validates Swift package structure and configuration to enforce project-wide conv * Ensures Swift tools version is 6.1 or newer * Requires defaultSwiftSettings usage on all targets * Enforces presence of the NonisolatedNonsendingByDefault Swift 6 concurrency feature -* Validates top-level dependencies section and DocC placeholder +* Validates the package manifest and target Swift settings * Checks required directory structure (Sources/, Tests/) * Ensures required repository files exist * Verifies LICENSE contains the current year diff --git a/examples/tests.yml b/examples/tests.yml index 1702a2d..9b48e73 100644 --- a/examples/tests.yml +++ b/examples/tests.yml @@ -7,10 +7,6 @@ on: jobs: - api_breakage: - name: Check API breakage - uses: BinaryBirds/github-workflows/.github/workflows/api_breakage.yml@feature/breakage-workflow - swiftlang_checks: name: Swiftlang Checks uses: swiftlang/github-workflows/.github/workflows/soundness.yml@main @@ -20,8 +16,8 @@ jobs: broken_symlink_check_enabled : true unacceptable_language_check_enabled : true shell_check_enabled : true - docs_check_enabled : false - api_breakage_check_enabled : false + docs_check_enabled : true + api_breakage_check_enabled : true license_header_check_enabled : false yamllint_check_enabled : false python_lint_check_enabled : false @@ -41,7 +37,6 @@ jobs: uses: BinaryBirds/github-workflows/.github/workflows/extra_soundness.yml@main with: local_swift_dependencies_check_enabled : true - docc_warnings_check_enabled : true run_tests_with_cache_enabled : true tests_cache_enabled : true headers_check_enabled : true diff --git a/examples/tests_private.yml b/examples/tests_private.yml index 5c31932..2c9e964 100644 --- a/examples/tests_private.yml +++ b/examples/tests_private.yml @@ -7,10 +7,6 @@ on: jobs: - api_breakage: - name: Check API breakage - uses: BinaryBirds/github-workflows/.github/workflows/api_breakage.yml@feature/breakage-workflow - bb_checks: name: BB Checks uses: BinaryBirds/github-workflows/.github/workflows/extra_soundness.yml@main @@ -25,7 +21,7 @@ jobs: format_check_enabled : true broken_symlink_check_enabled : true unacceptable_language_check_enabled : true - api_breakage_check_enabled : false + api_breakage_check_enabled : true docs_check_enabled : false license_header_check_enabled : false shell_check_enabled : false @@ -61,4 +57,4 @@ jobs: if [[ "$OSTYPE" == "linux-gnu"* ]]; then # if OS is linux git config --global --add safe.directory $PWD fi - swift test --parallel \ No newline at end of file + swift test --parallel diff --git a/scripts/check-api-breakage.sh b/scripts/check-api-breakage.sh deleted file mode 100755 index 21bfcdc..0000000 --- a/scripts/check-api-breakage.sh +++ /dev/null @@ -1,51 +0,0 @@ -#!/usr/bin/env bash -# API Breaking Changes Check -# -# This script checks whether the current Swift package introduces -# API-breaking changes compared to a baseline. -# -# Baseline selection logic: -# - In GitHub Actions PRs: uses the PR base branch -# - Otherwise: uses the latest git tag -# -# If no tags exist at all, the check is skipped with a warning. - -set -euo pipefail - -# Logging helpers -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { - error "$@" - exit 1 -} - -# Determine baseline reference -if [ -n "${GITHUB_BASE_REF:-}" ] && [ -n "${GITHUB_REPOSITORY:-}" ] && [ -n "${GITHUB_SERVER_URL:-}" ]; then - log "Running in PR context — using base branch: ${GITHUB_BASE_REF}" - - git fetch "${GITHUB_SERVER_URL}/${GITHUB_REPOSITORY}" \ - "${GITHUB_BASE_REF}:pull-base-ref" - - BASELINE_REF="pull-base-ref" -else - log "No PR context detected — checking for existing tags" - - git fetch --tags - - if ! git tag | grep -q .; then - log "⚠️ No git tags found — skipping API breakage check" - exit 0 - fi - - BASELINE_REF=$(git describe --tags --abbrev=0) -fi - -log "Using baseline: ${BASELINE_REF}" - -# Run SwiftPM API breakage diagnosis -# -# This command exits non-zero if API-breaking changes are detected. -swift package diagnose-api-breaking-changes "$BASELINE_REF" - -log "✅ No API-breaking changes detected." diff --git a/scripts/check-broken-symlinks.sh b/scripts/check-broken-symlinks.sh deleted file mode 100755 index ee03c10..0000000 --- a/scripts/check-broken-symlinks.sh +++ /dev/null @@ -1,57 +0,0 @@ -#!/usr/bin/env bash -# Broken Symlink Check -# -# This script scans the repository for broken symbolic links. -# -# A broken symlink is a symbolic link whose target file or directory -# no longer exists. Such links can cause build failures, tooling issues, -# or confusing behavior for developers. -# -# Intended usage: -# - CI: fail if broken symlinks are present in the repository -# - Local: verify repository integrity before committing or releasing -# -# Only files tracked by git are checked to ensure deterministic behavior. - -set -euo pipefail - -# Logging helpers -# All output is written to stderr for consistent CI and local logs -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { - error "$@" - exit 1 -} - -# Resolve the repository root -# Ensures correct path resolution even if the script is run from a subdirectory -REPO_ROOT="$(git -C "$PWD" rev-parse --show-toplevel)" - -log "Checking for broken symlinks..." -NUM_BROKEN_SYMLINKS=0 - -# Iterate over all files tracked by git -# -# Using `git ls-files` ensures: -# - Only committed files are checked -# - Generated or untracked files are ignored -while read -r -d '' file; do - # Check whether the symlink target exists - # - # `test -e` follows the symlink and verifies that the target - # file or directory is present. - if ! test -e "${REPO_ROOT}/${file}"; then - log "Broken symlink: ${file}" - ((NUM_BROKEN_SYMLINKS++)) - fi -done < <(git -C "${REPO_ROOT}" ls-files -z) - -# If any broken symlinks were found, fail the script -if [ "${NUM_BROKEN_SYMLINKS}" -gt 0 ]; then - log "❌ Found ${NUM_BROKEN_SYMLINKS} broken symlinks." - exit 1 -fi - -# Success -log "✅ Found 0 broken symlinks." diff --git a/scripts/check-docc-warnings.sh b/scripts/check-docc-warnings.sh deleted file mode 100755 index 60f21d2..0000000 --- a/scripts/check-docc-warnings.sh +++ /dev/null @@ -1,243 +0,0 @@ -#!/usr/bin/env bash -# DocC Warnings / Analysis Script -# -# This script runs DocC documentation analysis for Swift package targets and -# fails if any warnings or errors are produced. -# -# Key characteristics: -# - Supports an explicit target list via .docctargetlist -# - Falls back to auto-detecting documentable targets from Package.swift -# - Ensures swift-docc-plugin is available (injects temporarily if missing) -# - Enforces a clean git working tree for local runs -# - Aggregates results across all targets -# -# Intended usage: -# - CI: validate documentation quality and fail on warnings -# - Local: run the same checks developers see in CI -# -# This script follows the same mental model as Swift's upstream `check-docs.sh`. - -set -euo pipefail - -# Logging helpers -# -# All logs go to stderr to keep stdout clean and CI-friendly. -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { - error "$@" - exit 1 -} - -# Configuration / state -TARGETS_FILE=".docctargetlist" # Optional file defining an explicit list of DocC targets -TARGETS="" # Raw newline-separated target names (internal representation) -TARGET_LIST=() # Parsed target names as a Bash array for iteration - -# Swift package manifest to mutate -PACKAGE_FILE="Package.swift" -# Required injection anchor inside Package.dependencies -INJECT_MARKER='// [docc-plugin-placeholder]' -# Dependency line injected immediately after the marker -DOCC_DEP=' .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.4.0"),' -DOCC_PLUGIN_INJECTED=false - -# Git safety (local runs only) -# -# To avoid accidentally discarding local changes, this script refuses to run -# if the working tree is dirty when NOT running in GitHub Actions. -ensure_clean_git() { - if [ -n "${GITHUB_ACTIONS:-}" ]; then - return 0 - fi - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - if ! git diff --quiet || ! git diff --cached --quiet; then - fatal "Working tree has uncommitted changes — please commit or stash before running DocC analysis" - fi - fi -} - -# Reset git state after analysis (local only) -# -# Local runs restore the repository to a clean state after completion. -# This is skipped in CI environments. -reset_git_after_analysis() { - if [ -n "${GITHUB_ACTIONS:-}" ]; then - return 0 - fi - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - log "Resetting local git working tree" - git reset --hard - fi -} - -restore_injected_package_manifest() { - if [ "${DOCC_PLUGIN_INJECTED}" != "true" ]; then - return 0 - fi - - rm -f "$PACKAGE_FILE.tmp" - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - log "Restoring ${PACKAGE_FILE} after failed DocC analysis" - git checkout -- "$PACKAGE_FILE" || true - fi -} - -cleanup_on_exit() { - local rc=$? - if [ "$rc" -ne 0 ]; then - restore_injected_package_manifest - fi -} -trap cleanup_on_exit EXIT - -# Ensures that swift-docc-plugin is available to SwiftPM. -# -# This function injects the dependency using a fixed marker -# to avoid touching target-level dependencies or relying on -# fragile text matching. -ensure_docc_plugin() { - [ -f "$PACKAGE_FILE" ] || fatal "Package.swift not found" - - # Already present → no-op - if grep -q 'github.com/apple/swift-docc-plugin' "$PACKAGE_FILE"; then - log "swift-docc-plugin already present — skipping injection" - return 0 - fi - - # Marker must exist as a standalone line (whitespace allowed) - if ! awk -v marker="$INJECT_MARKER" ' - { - line = $0 - sub(/^[[:space:]]+/, "", line) - sub(/[[:space:]]+$/, "", line) - if (line == marker) { - found = 1 - exit 0 - } - } - END { exit found ? 0 : 1 } - ' "$PACKAGE_FILE"; then - fatal "Injection marker '${INJECT_MARKER}' not found as a standalone line in Package.swift" - fi - - log "Injecting swift-docc-plugin at ${INJECT_MARKER}" - - # Inject exactly once at the marker - awk -v marker="$INJECT_MARKER" -v dep="$DOCC_DEP" ' - BEGIN { injected = 0 } - { - print - if (!injected) { - line = $0 - sub(/^[[:space:]]+/, "", line) - sub(/[[:space:]]+$/, "", line) - if (line == marker) { - print dep - injected = 1 - } - } - } - END { - if (!injected) exit 42 - } - ' "$PACKAGE_FILE" >"$PACKAGE_FILE.tmp" - - mv "$PACKAGE_FILE.tmp" "$PACKAGE_FILE" - DOCC_PLUGIN_INJECTED=true - - # Validate manifest after mutation - swift package dump-package >/dev/null || - fatal "Package.swift became invalid after injecting swift-docc-plugin" -} - -# Pre-flight checks -ensure_clean_git -ensure_docc_plugin - -# Load targets from .docctargetlist -# -# If the file exists, it is treated as the authoritative list of DocC targets. -# Empty lines are ignored. -load_from_config() { - TARGETS=$(grep -v '^\s*$' "$TARGETS_FILE" || true) - if [ -z "$TARGETS" ]; then - fatal "$TARGETS_FILE exists but contains no valid targets." - fi -} - -# Auto-detect documentable Swift targets -# -# Falls back to SwiftPM package introspection when no explicit target list -# is provided. -auto_detect_targets() { - if ! command -v jq >/dev/null 2>&1; then - fatal "jq is required. Install with: brew install jq" - fi - - TARGETS=$(swift package dump-package | - jq -r '.targets[] - | select(.type == "regular" or .type == "executable") - | .name') - - if [ -z "$TARGETS" ]; then - fatal "No documentable targets found in Package.swift" - fi -} - -# Target selection -log "Detecting Swift targets for DocC analysis…" - -if [ -f "$TARGETS_FILE" ]; then - log "Using targets from $TARGETS_FILE" - load_from_config -else - log "Auto-detecting Swift targets" - auto_detect_targets -fi - -# Convert newline-separated target list into an array -while IFS= read -r TARGET; do - TARGET_LIST+=("$TARGET") -done <<<"$TARGETS" - -TARGET_COUNT="${#TARGET_LIST[@]}" -log "Targets detected: $TARGET_COUNT" - -# Run DocC analysis -# -# Each target is analyzed independently. -# Failures are collected and reported at the end instead of exiting early. -echo -TOTAL_RC=0 - -for TARGET in "${TARGET_LIST[@]}"; do - set +e - swift package \ - plugin \ - generate-documentation \ - --target "$TARGET" \ - --analyze \ - --warnings-as-errors - RC=$? - set -e - - if [ "$RC" -ne 0 ]; then - error "DocC analysis failed for target '$TARGET'" - TOTAL_RC=$RC - fi -done - -# Final result -if [ "$TOTAL_RC" -ne 0 ]; then - log "❌ One or more DocC analysis runs failed" - reset_git_after_analysis - exit "$TOTAL_RC" -fi - -log "✅ All targets passed DocC analysis without warnings." - -reset_git_after_analysis diff --git a/scripts/check-swift-package.sh b/scripts/check-swift-package.sh index 274d85a..b5c5cda 100755 --- a/scripts/check-swift-package.sh +++ b/scripts/check-swift-package.sh @@ -89,13 +89,6 @@ if [ -n "$PACKAGE_JSON" ]; then fi fi -# docc placeholder -if grep -q '// *\[docc-plugin-placeholder\]' Package.swift 2>/dev/null; then - log "✅ docc plugin placeholder present" -else - error "❌ docc plugin placeholder missing" -fi - # swiftSettings: defaultSwiftSettings on all targets if [ -n "$PACKAGE_JSON" ]; then TARGETS="$(echo "$PACKAGE_JSON" | jq -r '.targets[].name')" diff --git a/scripts/check-unacceptable-language.sh b/scripts/check-unacceptable-language.sh deleted file mode 100755 index 3cedd15..0000000 --- a/scripts/check-unacceptable-language.sh +++ /dev/null @@ -1,66 +0,0 @@ -#!/usr/bin/env bash -# Unacceptable Language Check -# -# This script scans the repository for the usage of unacceptable or -# discouraged terminology (e.g. blacklist/whitelist, master/slave, etc.). -# -# The goal is to: -# - Enforce inclusive and respectful language -# - Prevent accidental introduction of discouraged terms -# -# The script supports an optional ignore file: -# .unacceptablelanguageignore -# -# Lines matching the ignore file are excluded from the scan. -# The script fails if any unacceptable language is found. - -set -euo pipefail - -# Logging helpers -# All output is written to stderr for consistent CI logs -log() { printf -- "** %s\n" "$*" >&2; } -error() { printf -- "** ERROR: %s\n" "$*" >&2; } -fatal() { - error "$@" - exit 1 -} - -# List of unacceptable or discouraged words -# The list is converted into a single regex later -UNACCEPTABLE_WORD_LIST="blacklist whitelist slave master sane sanity insane insanity kill killed killing hang hung hanged hanging" - -# Will contain matches if any unacceptable language is found -PATHS_WITH_UNACCEPTABLE_LANGUAGE="" -UNACCEPTABLE_PATTERN="${UNACCEPTABLE_WORD_LIST// /|}" - -# Build git grep pathspec args. Start with repository root selector. -set -- . - -# If an ignore file exists, use it to exclude paths from the search -# -# Each line in .unacceptablelanguageignore is treated as a git pathspec -# exclusion and passed to `git grep`. -if [[ -f .unacceptablelanguageignore ]]; then - log "Found unacceptablelanguageignore file..." - log "Checking for unacceptable language..." - while IFS= read -r path; do - [ -z "${path}" ] && continue - set -- "$@" ":(exclude)${path}" - done <.unacceptablelanguageignore -else - log "Checking for unacceptable language..." -fi - -PATHS_WITH_UNACCEPTABLE_LANGUAGE="$( - git grep -i -I -w -H -n --column -E "${UNACCEPTABLE_PATTERN}" -- "$@" | - grep -v "ignore-unacceptable-language" || - true -)" - -# If any matches were found, fail the script and print the affected files -if [ -n "${PATHS_WITH_UNACCEPTABLE_LANGUAGE}" ]; then - fatal "❌ Found unacceptable language in files: ${PATHS_WITH_UNACCEPTABLE_LANGUAGE}." -fi - -# Success -log "✅ Found no unacceptable language." diff --git a/scripts/generate-docc.sh b/scripts/generate-docc.sh index 1edf8ff..df28435 100755 --- a/scripts/generate-docc.sh +++ b/scripts/generate-docc.sh @@ -6,7 +6,7 @@ # Features: # - Supports explicit target selection via .docctargetlist # - Falls back to auto-detecting documentable Swift targets -# - Injects swift-docc-plugin temporarily if missing +# - Adds swift-docc-plugin temporarily if missing # - Supports local preview mode and GitHub Pages static hosting # - Enforces a clean git working tree for local runs # @@ -34,13 +34,10 @@ COMBINED_FLAG="" # Experimental combined documentation flag LOCAL_MODE=false # Local preview vs static hosting mode REPO_NAME="" # Hosting base path (for GitHub Pages) -# Swift package manifest to mutate +# Swift package manifest to mutate temporarily PACKAGE_FILE="Package.swift" -# Required injection anchor inside dependencies -INJECT_MARKER='// [docc-plugin-placeholder]' -# Dependency line injected after the marker -DOCC_DEP=' .package(url: "https://github.com/apple/swift-docc-plugin", from: "1.4.0"),' DOCC_PLUGIN_INJECTED=false +PACKAGE_BACKUP="" # Argument parsing # @@ -67,9 +64,6 @@ while [[ $# -gt 0 ]]; do done # Git safety (local runs only) -# -# Prevents accidental loss of local changes when the script temporarily -# modifies Package.swift. ensure_clean_git() { if [ -n "${GITHUB_ACTIONS:-}" ]; then return 0 @@ -84,9 +78,9 @@ ensure_clean_git() { # Ensures that swift-docc-plugin is available to SwiftPM. # -# This function injects the dependency using a fixed marker -# to avoid touching target-level dependencies or relying on -# fragile text matching. +# SwiftPM edits the manifest structurally, so callers do not need to maintain +# a placeholder comment in Package.swift. The original manifest is restored by +# the EXIT trap after documentation generation, whether it succeeds or fails. ensure_docc_plugin() { [ -f "$PACKAGE_FILE" ] || fatal "Package.swift not found" @@ -96,64 +90,18 @@ ensure_docc_plugin() { return 0 fi - # Marker must exist as a standalone line (whitespace allowed) - if ! awk -v marker="$INJECT_MARKER" ' - { - line = $0 - sub(/^[[:space:]]+/, "", line) - sub(/[[:space:]]+$/, "", line) - if (line == marker) { - found = 1 - exit 0 - } - } - END { exit found ? 0 : 1 } - ' "$PACKAGE_FILE"; then - fatal "Injection marker '${INJECT_MARKER}' not found as a standalone line in Package.swift" - fi - - log "Injecting swift-docc-plugin at ${INJECT_MARKER}" - - # Inject exactly once at the marker - awk -v marker="$INJECT_MARKER" -v dep="$DOCC_DEP" ' - BEGIN { injected = 0 } - { - print - if (!injected) { - line = $0 - sub(/^[[:space:]]+/, "", line) - sub(/[[:space:]]+$/, "", line) - if (line == marker) { - print dep - injected = 1 - } - } - } - END { - if (!injected) exit 42 - } - ' "$PACKAGE_FILE" >"$PACKAGE_FILE.tmp" - - mv "$PACKAGE_FILE.tmp" "$PACKAGE_FILE" + PACKAGE_BACKUP="$(mktemp "${PACKAGE_FILE}.backup.XXXXXX")" + cp -p "$PACKAGE_FILE" "$PACKAGE_BACKUP" DOCC_PLUGIN_INJECTED=true + log "Temporarily adding swift-docc-plugin with SwiftPM" + swift package add-dependency \ + https://github.com/apple/swift-docc-plugin \ + --from 1.4.0 || fatal "Failed to add swift-docc-plugin dependency" + # Validate manifest after mutation swift package dump-package >/dev/null || - fatal "Package.swift became invalid after injecting swift-docc-plugin" -} - -# Restore git state after documentation generation (local only) -# -# Ensures the repository is returned to a clean state after execution. -reset_git_after_docs() { - if [ -n "${GITHUB_ACTIONS:-}" ]; then - return 0 - fi - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - log "Resetting local git working tree" - git reset --hard - fi + fatal "Package.swift became invalid after adding swift-docc-plugin" } # shellcheck disable=SC2317,SC2329 @@ -162,20 +110,18 @@ restore_injected_package_manifest() { return 0 fi - rm -f "$PACKAGE_FILE.tmp" - - if git rev-parse --is-inside-work-tree >/dev/null 2>&1; then - log "Restoring ${PACKAGE_FILE} after failed documentation generation" - git checkout -- "$PACKAGE_FILE" || true - fi + log "Restoring ${PACKAGE_FILE} after DocC generation" + cp -p "$PACKAGE_BACKUP" "$PACKAGE_FILE" + rm -f "$PACKAGE_BACKUP" + PACKAGE_BACKUP="" + DOCC_PLUGIN_INJECTED=false } # shellcheck disable=SC2317,SC2329 cleanup_on_exit() { local rc=$? - if [ "$rc" -ne 0 ]; then - restore_injected_package_manifest - fi + restore_injected_package_manifest + return "$rc" } trap cleanup_on_exit EXIT @@ -381,5 +327,4 @@ if ! $LOCAL_MODE && [ "$DOCS_EXIT_CODE" -eq 0 ]; then fi # Cleanup and exit -reset_git_after_docs exit $DOCS_EXIT_CODE diff --git a/tests/bats/check-broken-symlinks.bats b/tests/bats/check-broken-symlinks.bats deleted file mode 100755 index 1c20a11..0000000 --- a/tests/bats/check-broken-symlinks.bats +++ /dev/null @@ -1,64 +0,0 @@ -#!/usr/bin/env bats - -load 'helpers/test_helper.bash' - -@test "check-broken-symlinks passes when all symlinks are valid" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-broken-symlinks.sh" - - cat >"$repo/target.txt" <<'TXT' -hello -TXT - ln -s target.txt "$repo/link.txt" - commit_all "$repo" - - run bash -c "cd '$repo' && bash scripts/check-broken-symlinks.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found 0 broken symlinks"* ]] -} - -@test "check-broken-symlinks fails when symlink target is missing" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-broken-symlinks.sh" - - ln -s missing.txt "$repo/broken.txt" - commit_all "$repo" - - run bash -c "cd '$repo' && bash scripts/check-broken-symlinks.sh" - - [ "$status" -eq 1 ] - [[ "$output" == *"Broken symlink: broken.txt"* ]] -} - -@test "check-broken-symlinks ignores regular files even if they mention missing paths" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-broken-symlinks.sh" - - cat >"$repo/notes.txt" <<'TXT' -This mentions a missing file path: ./does-not-exist.txt -TXT - commit_all "$repo" - - run bash -c "cd '$repo' && bash scripts/check-broken-symlinks.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found 0 broken symlinks"* ]] -} - -@test "check-broken-symlinks checks only tracked symlinks" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-broken-symlinks.sh" - - cat >"$repo/tracked.txt" <<'TXT' -ok -TXT - git -C "$repo" add tracked.txt - git -C "$repo" commit -q -m "baseline" - ln -s missing.txt "$repo/untracked-broken.txt" - - run bash -c "cd '$repo' && bash scripts/check-broken-symlinks.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found 0 broken symlinks"* ]] -} diff --git a/tests/bats/check-swift-package.bats b/tests/bats/check-swift-package.bats index 029a9e6..0f0cfa2 100755 --- a/tests/bats/check-swift-package.bats +++ b/tests/bats/check-swift-package.bats @@ -40,7 +40,6 @@ let package = Package( .library(name: "Demo", targets: ["Demo"]) ], dependencies: [ - // [docc-plugin-placeholder] ], targets: [ .target( @@ -131,20 +130,17 @@ EOF [[ "$output" == *"swift-tools-version too old"* ]] } -@test "check-swift-package fails when docc placeholder is missing" { +@test "check-swift-package passes when docc placeholder is missing" { repo="$(make_temp_repo)" copy_script_into_repo "$repo" "check-swift-package.sh" setup_valid_swift_package_layout "$repo" write_valid_package_swift "$repo" install_swift_dump_stub_success "$repo" - sed -i.bak '/docc-plugin-placeholder/d' "$repo/Package.swift" - rm -f "$repo/Package.swift.bak" - run bash -c "cd '$repo' && sh scripts/check-swift-package.sh" - [ "$status" -eq 1 ] - [[ "$output" == *"docc plugin placeholder missing"* ]] + [ "$status" -eq 0 ] + [[ "$output" == *"Swift Package validation passed"* ]] } @test "check-swift-package fails when a target misses swiftSettings: defaultSwiftSettings" { @@ -165,7 +161,6 @@ let package = Package( .library(name: "Demo", targets: ["Demo"]) ], dependencies: [ - // [docc-plugin-placeholder] ], targets: [ .target( diff --git a/tests/bats/check-unacceptable-language.bats b/tests/bats/check-unacceptable-language.bats deleted file mode 100755 index 5d74e4c..0000000 --- a/tests/bats/check-unacceptable-language.bats +++ /dev/null @@ -1,109 +0,0 @@ -#!/usr/bin/env bats - -load 'helpers/test_helper.bash' - -@test "check-unacceptable-language passes when no banned terms exist" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-unacceptable-language.sh" - - cat >"$repo/good.txt" <<'TXT' -This file uses inclusive and neutral wording. -TXT - commit_all "$repo" - - cat >"$repo/.unacceptablelanguageignore" <<'TXT' -scripts/check-unacceptable-language.sh -TXT - commit_all "$repo" "add ignore file" - - run bash -c "cd '$repo' && bash scripts/check-unacceptable-language.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found no unacceptable language"* ]] -} - -@test "check-unacceptable-language fails when banned terms exist" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-unacceptable-language.sh" - - cat >"$repo/bad.txt" <<'TXT' -This line contains blacklist and should fail. -TXT - commit_all "$repo" - - run bash -c "cd '$repo' && bash scripts/check-unacceptable-language.sh" - - [ "$status" -eq 1 ] - [[ "$output" == *"Found unacceptable language"* ]] - [[ "$output" == *"bad.txt"* ]] -} - -@test "check-unacceptable-language respects .unacceptablelanguageignore" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-unacceptable-language.sh" - - cat >"$repo/bad.txt" <<'TXT' -This line contains blacklist. -TXT - cat >"$repo/.unacceptablelanguageignore" <<'TXT' -bad.txt -scripts/check-unacceptable-language.sh -TXT - commit_all "$repo" - - run bash -c "cd '$repo' && bash scripts/check-unacceptable-language.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found no unacceptable language"* ]] -} - -@test "check-unacceptable-language ignores lines with ignore-unacceptable-language marker" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-unacceptable-language.sh" - - cat >"$repo/allowed.txt" <<'TXT' -blacklist // ignore-unacceptable-language -TXT - cat >"$repo/.unacceptablelanguageignore" <<'TXT' -scripts/check-unacceptable-language.sh -TXT - commit_all "$repo" - - run bash -c "cd '$repo' && bash scripts/check-unacceptable-language.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found no unacceptable language"* ]] -} - -@test "check-unacceptable-language handles an empty ignore file" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-unacceptable-language.sh" - - cat >"$repo/good.txt" <<'TXT' -Inclusive wording only. -TXT - : >"$repo/.unacceptablelanguageignore" - git -C "$repo" add good.txt .unacceptablelanguageignore - git -C "$repo" commit -q -m "add fixtures" - - run bash -c "cd '$repo' && bash scripts/check-unacceptable-language.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found no unacceptable language"* ]] -} - -@test "check-unacceptable-language matches whole words only" { - repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-unacceptable-language.sh" - - cat >"$repo/words.txt" <<'TXT' -mastery and blacklisting should not match whole-word checks. -TXT - git -C "$repo" add words.txt - git -C "$repo" commit -q -m "add words fixture" - - run bash -c "cd '$repo' && bash scripts/check-unacceptable-language.sh" - - [ "$status" -eq 0 ] - [[ "$output" == *"Found no unacceptable language"* ]] -} diff --git a/tests/bats/docc-scripts.bats b/tests/bats/docc-scripts.bats index 5df3a3f..c7cc757 100644 --- a/tests/bats/docc-scripts.bats +++ b/tests/bats/docc-scripts.bats @@ -2,9 +2,9 @@ load 'helpers/test_helper.bash' -@test "check-docc-warnings restores Package.swift when analysis fails after plugin injection" { +@test "generate-docc restores Package.swift after failure without a placeholder" { repo="$(make_temp_repo)" - copy_script_into_repo "$repo" "check-docc-warnings.sh" + copy_script_into_repo "$repo" "generate-docc.sh" cat >"$repo/Package.swift" <<'SWIFT' // swift-tools-version: 6.1 @@ -13,7 +13,6 @@ import PackageDescription let package = Package( name: "DocCTest", dependencies: [ - // [docc-plugin-placeholder] ], targets: [ .target(name: "DocCTest") @@ -33,7 +32,11 @@ if [[ "$*" == "package dump-package" ]]; then JSON exit 0 fi -if [[ "$*" == *"plugin generate-documentation"* ]]; then +if [[ "$*" == "package add-dependency "* ]]; then + printf '\n// temporary DocC dependency\n' >> Package.swift + exit 0 +fi +if [[ "$*" == *"generate-documentation"* ]]; then exit 1 fi exit 0 @@ -41,14 +44,14 @@ STUB chmod +x "$repo/fakebin/swift" export PATH="$repo/fakebin:$PATH" - run bash -c "cd '$repo' && bash scripts/check-docc-warnings.sh" + run bash -c "cd '$repo' && bash scripts/generate-docc.sh" [ "$status" -eq 1 ] - run bash -c "grep -q 'swift-docc-plugin' '$repo/Package.swift'" + run bash -c "grep -q 'temporary DocC dependency' '$repo/Package.swift'" [ "$status" -eq 1 ] } -@test "generate-docc restores Package.swift when generation fails after plugin injection" { +@test "generate-docc deploy generation works without a placeholder and restores Package.swift" { repo="$(make_temp_repo)" copy_script_into_repo "$repo" "generate-docc.sh" @@ -58,9 +61,7 @@ import PackageDescription let package = Package( name: "DocCTest", - dependencies: [ - // [docc-plugin-placeholder] - ], + dependencies: [], targets: [ .target(name: "DocCTest") ] @@ -73,6 +74,10 @@ SWIFT cat >"$repo/fakebin/swift" <<'STUB' #!/usr/bin/env bash set -euo pipefail +if [[ "$*" == "package add-dependency "* ]]; then + printf '\n// temporary DocC dependency\n' >> Package.swift + exit 0 +fi if [[ "$*" == "package dump-package" ]]; then cat <<'JSON' {"targets":[{"type":"regular","name":"DocCTest"}]} @@ -80,16 +85,26 @@ JSON exit 0 fi if [[ "$*" == *"generate-documentation"* ]]; then + while [[ $# -gt 0 ]]; do + if [[ "$1" == "--output-path" ]]; then + mkdir -p "$2/documentation/DocCTest" + printf '\n' >"$2/documentation/DocCTest/index.html" + exit 0 + fi + shift + done exit 1 fi -exit 0 +exit 1 STUB chmod +x "$repo/fakebin/swift" export PATH="$repo/fakebin:$PATH" - run bash -c "cd '$repo' && bash scripts/generate-docc.sh" + run bash -c "cd '$repo' && bash scripts/generate-docc.sh --name DocCTest" - [ "$status" -eq 1 ] - run bash -c "grep -q 'swift-docc-plugin' '$repo/Package.swift'" + [ "$status" -eq 0 ] + [ -f "$repo/docs/index.html" ] + [ -f "$repo/docs/documentation/index.html" ] + run bash -c "grep -q 'temporary DocC dependency' '$repo/Package.swift'" [ "$status" -eq 1 ] }