Skip to content

Link Checker

Link Checker #15

Workflow file for this run

name: Link Checker
on:
workflow_dispatch:
push:
branches:
- master
pull_request:
branches:
- master
schedule:
- cron: "0 0 * * *"
# Original source, at commit with id 444094f5:
# https://github.com/kdeldycke/workflows/blob/main/.github/workflows/lint.yaml
jobs:
linkChecker:
name: Link Checker
runs-on: ubuntu-latest
permissions:
contents: read
issues: write # required for peter-evans/create-issue-from-file
# Manually manage the life-cycle of issues created in this job because the
# `create-issue-from-file` action blindly creates issues ad-nauseam.
# See also: https://github.com/peter-evans/create-issue-from-file/issues/298
steps:
- uses: actions/checkout@v4
- name: Restore lychee cache
uses: actions/cache@v4
with:
path: .lycheecache
key: cache-lychee-${{ github.sha }}
restore-keys: cache-lychee-
- name: Link Checker
uses: lycheeverse/lychee-action@v2.3.0
id: lychee_run
with:
args: |
--cache
--verbose
--no-progress
'.'
continue-on-error: true
- name: List open issues
if: github.ref == 'refs/heads/master'
id: open_issues
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: >
echo "issues=$(
gh issue list
--state open
--author 'github-actions[bot]'
--json number,title,createdAt
)"
| tee -a "$GITHUB_OUTPUT"
- name: Filter issues
if: github.ref == 'refs/heads/master'
id: issue_groups
shell: python
run: |
import json
import os
from operator import itemgetter
from pathlib import Path
exit_code = """${{ steps.lychee_run.outputs.exit_code }}"""
print(f"Lychee exit code: {exit_code!r}")
broken_links_found = bool(int(exit_code))
if broken_links_found:
print("Broken links found: create a new issue or update the existing one.")
else:
print("No broken link found: close all open issues.")
open_issues_raw = '${{ steps.open_issues.outputs.issues }}'
if open_issues_raw:
open_issues = json.loads(open_issues_raw)
else:
open_issues = []
issue_to_update: int | None = None
issues_to_close: set[int] = set()
for issue in sorted(open_issues, key=itemgetter("createdAt"), reverse=True):
print(f"Processing {issue!r} ...")
if issue["title"] != "Broken links":
print(f"{issue!r} is not a broken links issue, skip it.")
continue
if broken_links_found and not issue_to_update:
print(f"{issue!r} is the most recent open issue.")
issue_to_update = issue["number"]
else:
print(f"{issue!r} is an old open issue we have to close.")
issues_to_close.add(issue["number"])
output = f"broken_links_found={str(broken_links_found).lower()}\n"
output += f"issue_to_update={issue_to_update}\n"
output += f"issues_to_close={' '.join(map(str, issues_to_close))}\n"
env_file = Path(os.getenv("GITHUB_OUTPUT"))
env_file.write_text(output)
- name: Print issue groups
if: github.ref == 'refs/heads/master'
run: |
echo "Broken links found: ${{ steps.issue_groups.outputs.broken_links_found }}"
echo "Issue to update: ${{ steps.issue_groups.outputs.issue_to_update }}"
echo "Issues to close: ${{ steps.issue_groups.outputs.issues_to_close }}"
- name: Create or update issue
if: >
github.ref == 'refs/heads/master'
&& fromJSON(steps.issue_groups.outputs.broken_links_found)
uses: peter-evans/create-issue-from-file@v5.0.1
with:
title: "Broken links"
issue-number: ${{ steps.issue_groups.outputs.issue_to_update }}
content-filepath: ./lychee/out.md
labels: 📚 documentation, 🤖 automated issue
- name: Close old issues
if: >
github.ref == 'refs/heads/master'
&& steps.issue_groups.outputs.issues_to_close
env:
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
run: |
for number in ${{ steps.issue_groups.outputs.issues_to_close }}; do
if [[ "${{ steps.issue_groups.outputs.issue_to_update }}" =~ ^[0-9]+$ ]]; then
comment="Superseded by #${{ steps.issue_groups.outputs.issue_to_update }}.";
else
comment="All broken links are fixed. 👏"
fi
gh issue close "$number" --comment "$comment";
done
- name: Fail workflow on broken links
run: exit ${{ steps.lychee_run.outputs.exit_code }}