name: Docs link check

on:
  schedule:
    # GitHub cron uses UTC. This runs every Sunday at 20:00 UTC+8.
    - cron: '0 12 * * 0'
  pull_request:
    branches:
      - 'main'
    paths:
      - 'docs/source/**/*.md'
  workflow_dispatch:

defaults:
  run:
    shell: bash -el {0}

permissions:
  contents: read
  pull-requests: read

concurrency:
  group: docs-linkcheck-${{ github.ref }}
  cancel-in-progress: true

jobs:
  linkcheck:
    name: Sphinx linkcheck
    runs-on: linux-aarch64-a2b3-0
    container:
      image: swr.cn-southwest-2.myhuaweicloud.com/base_image/ascend-ci/cann:9.0.0-910b-ubuntu22.04-py3.11
    timeout-minutes: 30
    steps:
      - name: Checkout vllm-project/vllm-ascend repo
        uses: actions/checkout@v6

      - name: Collect changed docs files
        id: changed-docs
        if: ${{ github.event_name == 'pull_request' }}
        uses: tj-actions/changed-files@v47
        with:
          use_rest_api: true
          files: |
            docs/source/**/*.md

      - name: Install docs dependencies
        if: ${{ github.event_name != 'pull_request' || steps.changed-docs.outputs.any_changed == 'true' }}
        run: |
          python -m pip install --upgrade pip
          pip install -r docs/requirements-docs.txt

      - name: Prepare linkcheck retry helper
        if: ${{ github.event_name != 'pull_request' || steps.changed-docs.outputs.any_changed == 'true' }}
        run: |
          cat > /tmp/retry-linkcheck-failed-links.sh <<'BASH'
          #!/usr/bin/env bash
          set -euo pipefail

          linkcheck_exit_code="${1:?missing linkcheck exit code}"
          output_json="${2:?missing linkcheck output json}"

          if [[ "${linkcheck_exit_code}" == "0" ]]; then
            exit 0
          fi

          retry_failed=0
          retried=0
          while IFS= read -r url; do
            retried=1
            echo "Retrying failed link: ${url}"
            curl_args=(-fsSL --retry 3 --retry-delay 5 --retry-all-errors -o /dev/null)
            if curl "${curl_args[@]}" "${url}"; then
              echo "Retry succeeded: ${url}"
            else
              echo "Retry failed: ${url}"
              retry_failed=1
            fi
          done < <(python -c 'import json, pathlib, sys; p = pathlib.Path(sys.argv[1]); print("\n".join(sorted({json.loads(line).get("uri", "").split("#", 1)[0] for line in p.read_text(encoding="utf-8").splitlines() if json.loads(line).get("status") in {"broken", "timeout"} and json.loads(line).get("uri")})) if p.exists() else "")' "${output_json}")

          if [[ "${retried}" == "0" ]]; then
            exit "${linkcheck_exit_code}"
          fi
          exit "${retry_failed}"
          BASH
          chmod +x /tmp/retry-linkcheck-failed-links.sh

      - name: Run full Sphinx linkcheck
        if: ${{ github.event_name != 'pull_request' }}
        run: |
          set +e
          make -C docs linkcheck SPHINXOPTS="-W --keep-going"
          LINKCHECK_EXIT_CODE=$?
          set -e

          /tmp/retry-linkcheck-failed-links.sh \
            "${LINKCHECK_EXIT_CODE}" docs/_build/linkcheck/output.json

      - name: Run Sphinx linkcheck for changed docs
        if: ${{ github.event_name == 'pull_request' && steps.changed-docs.outputs.any_changed == 'true' }}
        env:
          CHANGED_DOCS: ${{ steps.changed-docs.outputs.all_changed_files }}
        run: |
          set -euo pipefail

          # shellcheck disable=SC2153
          read -r -a changed_docs <<< "${CHANGED_DOCS}"
          echo "Changed docs files:"
          printf '  %s\n' "${changed_docs[@]}"

          set +e
          sphinx-build -b linkcheck -W --keep-going \
            docs/source docs/_build/linkcheck "${changed_docs[@]}"
          LINKCHECK_EXIT_CODE=$?
          set -e

          /tmp/retry-linkcheck-failed-links.sh \
            "${LINKCHECK_EXIT_CODE}" docs/_build/linkcheck/output.json

      - name: Upload linkcheck reports
        if: ${{ always() && github.event_name != 'pull_request' }}
        uses: actions/upload-artifact@v7
        with:
          name: sphinx-linkcheck-report
          path: |
            docs/_build/linkcheck/output.txt
            docs/_build/linkcheck/output.json
          if-no-files-found: warn
          retention-days: 5