Skip to main content

check-links.yml

Run a lychee link check with a bundled config, a PR skip-label, and an auto-filed issue when links break on main.

Inputs

Input Type Default Description
lychee-config string '' Path in the caller repo to a lychee config; falls back to the bundled default.
lychee-args string '' Arguments and globs passed to lychee; a sensible default glob set is used when empty. Do not pass --format or --output.
fail-if-empty boolean false Fail the run when no links are found.
create-issue-on-main boolean true File an issue when links break on the main branch.
skip-label string 'links checked by hand' PR label that skips the link check.
fail-on-any-5xx boolean false Fail on every 5xx server error, not only on one on a line the PR adds.
runs-on string empty (CI_RUNNER variable, else ubuntu-latest) Runner label for the job. Use a self-hosted label only for a private repository; never for a public one, whose pull requests can run arbitrary code on it (#1167).

A 404, a network error, or a timeout fails the check wherever the link is. The one exception is a network error saying the certificate is not trusted (“SSL certificate not trusted”): it is a fault of the host’s certificate chain or the runner’s trust store, so the link is unverified, not known dead. It is reported as a warning annotation naming the URL on every line, and does not fail the run (gha#1416). A 5xx server error is usually a host that is briefly down rather than a dead link, so it fails a pull request only when the link is on a line the PR adds. Any other 5xx is reported as a warning annotation and in the job summary, and does not fail the run (gha#1052). On any other event, such as a push, a scheduled run, workflow_dispatch, merge_group, or pull_request_target, the workflow passes no diff base, so every 5xx is a warning unless fail-on-any-5xx is set. The check-links composite that this workflow calls defaults its base-ref input to all, so a direct caller of the composite still fails on every 5xx unless it passes a ref or ''.

Permissions

Grant issues: write, pull-requests: read, and contents: read (it opens an issue on main failures).

Example

# Copy to .github/workflows/check-links.yml in your repo.
# NOTE: the calling job grants issues:write so the workflow can open an issue
# when links break on main. Required because repos default to a read-only token.
name: Check Links

on:
  push:
    branches:
      - main
  pull_request:
    types: [opened, synchronize, reopened, labeled, unlabeled]
  schedule:
    # Run weekly on Mondays at 9:00 UTC to catch link rot
    - cron: "0 9 * * 1"
  workflow_dispatch:

jobs:
  check:
    permissions:
      contents: read
      issues: write
      pull-requests: read
    uses: Morrison-Lab/gha/.github/workflows/check-links.yml@v2
    # with:
    #   lychee-config: lychee.toml       # use a repo-local config instead of the bundled default
    #   fail-if-empty: true              # fail when no links are found (default: false)
    #   create-issue-on-main: true
    #   skip-label: 'links checked by hand'
    #   fail-on-any-5xx: true            # fail on every 5xx, not only on PR-added lines (@v3)
    #   runs-on: private-runner                # self-hosted runner label (private repositories only)

See examples/check-links.yml for the full caller stub.