mirror of
https://github.com/FreshRSS/FreshRSS.git
synced 2026-10-28 05:58:40 -04:00
Add a link checker workflow using lychee (#9209)
* Add a link checker workflow using lychee Depends on #9207 (merged) https://github.com/lycheeverse/lychee-action * Adjust cron to run once every month instead * Apply suggestions * Pass zizmor's pedantic audit * Set `failIfEmpty: false` for lychee action * Add offline Lychee check to catch broken links after renames * Add workaround for `--files-from` option not respecting `extensions` * Escape `?` correctly in `.lycheeignore` * Don't accept timeouts, retry instead * Bump link checker workflow to `ubuntu-26.04` * Always scope to host correctly in `.lycheeignore` * Set `hidden = true` in config to ensure that dotfiles are scanned * Exclude issue template from the scan * with open --------- Co-authored-by: Alexandre Alapetite <alexandre@alapetite.fr>
This commit is contained in:
1 parent
22c3128b23
commit
456b9fbff5
3 files changed
+211
No files matched your search
@@ -0,0 +1,113 @@
|
||||
name: Link Checker
|
||||
|
||||
on:
|
||||
push:
|
||||
branches: [ edge ]
|
||||
pull_request:
|
||||
branches: [ edge ]
|
||||
schedule:
|
||||
- cron: '30 12 1 * *'
|
||||
workflow_dispatch:
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
concurrency:
|
||||
group: link-checker-${{ github.ref }}
|
||||
cancel-in-progress: false
|
||||
|
||||
jobs:
|
||||
linkChecker:
|
||||
name: linkChecker
|
||||
runs-on: ubuntu-26.04
|
||||
steps:
|
||||
- uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
|
||||
with:
|
||||
persist-credentials: false
|
||||
fetch-depth: 0
|
||||
|
||||
# Check local links across the repository first to catch broken links in
|
||||
# files that are not part of the PR's changed files.
|
||||
- name: Check local links (offline run)
|
||||
if: github.event_name == 'pull_request'
|
||||
uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0
|
||||
with:
|
||||
fail: true
|
||||
failIfEmpty: false
|
||||
args: >
|
||||
--offline
|
||||
.
|
||||
|
||||
- name: Get changed files
|
||||
if: github.event_name == 'pull_request'
|
||||
env:
|
||||
BASE_SHA: ${{ github.event.pull_request.base.sha }}
|
||||
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
|
||||
run: |
|
||||
git diff --name-only --diff-filter=ACMR \
|
||||
"$BASE_SHA" \
|
||||
"$HEAD_SHA" \
|
||||
> changed-files.txt
|
||||
|
||||
# Lychee does not apply `extensions` from the config to files passed via `--files-from`.
|
||||
# Filter the changed files so only allowed file types are checked.
|
||||
python3 <<'PY'
|
||||
import pathlib
|
||||
import tomllib
|
||||
|
||||
with open("lychee.toml", "rb") as fp:
|
||||
config = tomllib.load(fp)
|
||||
extensions = {e.lstrip(".").lower() for e in config["extensions"]}
|
||||
|
||||
files = pathlib.Path("changed-files.txt").read_text().splitlines()
|
||||
files = [
|
||||
f for f in files
|
||||
if (
|
||||
pathlib.PurePath(f).suffix.lstrip(".").lower() in extensions
|
||||
or (
|
||||
not pathlib.PurePath(f).suffix
|
||||
and pathlib.PurePath(f).name.startswith(".")
|
||||
and pathlib.PurePath(f).name.lstrip(".").lower() in extensions
|
||||
)
|
||||
or (
|
||||
not pathlib.PurePath(f).suffix
|
||||
and not pathlib.PurePath(f).name.startswith(".")
|
||||
and config.get("default_extension") in extensions
|
||||
)
|
||||
)
|
||||
]
|
||||
|
||||
pathlib.Path("changed-files.txt").write_text(
|
||||
"\n".join(files) + ("\n" if files else "")
|
||||
)
|
||||
PY
|
||||
|
||||
- name: Restore lychee cache
|
||||
id: restore-cache
|
||||
uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: cache-lychee-${{ github.sha }}
|
||||
restore-keys: cache-lychee-
|
||||
|
||||
- name: Run lychee
|
||||
uses: lycheeverse/lychee-action@e7477775783ea5526144ba13e8db5eec57747ce8 # v2.9.0
|
||||
with:
|
||||
# PAT with read-only permissions used to avoid GitHub rate limits (allows 5,000 requests per hour)
|
||||
# Won't be available to forks in pull requests due to restrictions, so falls back to default
|
||||
# GITHUB_TOKEN with 1,000 requests per hour instead.
|
||||
token: ${{ secrets.LYCHEE_GITHUB_TOKEN || secrets.GITHUB_TOKEN }}
|
||||
|
||||
fail: true
|
||||
failIfEmpty: false # Avoid failing in PRs with no links in changed files (`true` is the default)
|
||||
|
||||
# Config is loaded from lychee.toml
|
||||
args: >
|
||||
${{ github.event_name == 'pull_request' && '--files-from changed-files.txt' || '.' }}
|
||||
|
||||
- name: Save lychee cache
|
||||
uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0
|
||||
if: always()
|
||||
with:
|
||||
path: .lycheecache
|
||||
key: ${{ steps.restore-cache.outputs.cache-primary-key }}
|
||||
@@ -0,0 +1,54 @@
|
||||
# URLs used as examples
|
||||
^https://codeload\.github\.com/FreshRSS/FreshRSS/zip/x\.y\.z
|
||||
^https://[^/]+\.domain/
|
||||
^http://wiremock:8080/
|
||||
^http://freshrss/
|
||||
|
||||
# URLs used in source code / tests
|
||||
^http://www\.blogger\.com/feeds/1/posts
|
||||
^https://www\.blogger\.com/feeds/1/posts
|
||||
^http://a\.bc/
|
||||
^https://web\.archive\.org/save/~LINK~
|
||||
^https://www\.facebook\.com/sharer\.php\?u=~LINK~&t=~TITLE~
|
||||
^https://archive\.ph/submit/\?url=~LINK~
|
||||
^https://archive\.is/submit/\?url=~LINK~
|
||||
^https://www\.reddit\.com/submit\?url=~LINK~
|
||||
^https://publish\.buffer\.com/compose\?url=~LINK~&text=~TITLE
|
||||
^https://www\.journalduhacker\.net/stories/new\?url=~LINK~&title=~TITLE~
|
||||
^https://www\.linkedin\.com/shareArticle\?url=~LINK~&title=~TITLE~&source=FreshRSS
|
||||
^https://www\.xing\.com/spi/shares/new\?url=~LINK
|
||||
|
||||
# URLs that likely exist, but will be published in the future
|
||||
^https://github\.com/FreshRSS/FreshRSS/security/advisories/.*
|
||||
|
||||
# Namespaces
|
||||
^http://purl\.org/dc/elements/1\.1/
|
||||
^http://search\.yahoo\.com/mrss/
|
||||
^http://www\.w3\.org/2005/Atom
|
||||
^https://freshrss\.org/opml
|
||||
|
||||
# Old references (HTTP)
|
||||
^http://freshrss\.org/
|
||||
|
||||
# Wrongly returned status codes
|
||||
^https://www\.pikapods\.com/pods\?run=freshrss
|
||||
|
||||
# Unreachable due to bot protection
|
||||
^https://stackoverflow\.com/
|
||||
^https://serverfault\.com/
|
||||
^https://docs\.slackware\.com/
|
||||
^https://bugs\.mysql\.com/
|
||||
^https://dev\.mysql\.com/
|
||||
^https://alternativeto\.net/
|
||||
^https://www\.patreon\.com/
|
||||
^https://www\.reddit\.com/
|
||||
|
||||
# Works normally in the browser, but not in lychee
|
||||
^https://www\.vsta\.org/andy/
|
||||
^https://gnusocial\.rocks/
|
||||
^https://friendi\.ca/
|
||||
|
||||
# TODO: Dead?
|
||||
^https://template\.run\.claw\.cloud/\?openapp=system-fastdeploy%3FtemplateName%3Dfreshrss
|
||||
^https://amaury\.carrade\.eu/
|
||||
^https://www\.pimsnel\.com/
|
||||
+44
@@ -0,0 +1,44 @@
|
||||
# https://lychee.cli.rs/guides/config/
|
||||
|
||||
verbose = "warn"
|
||||
no_progress = true
|
||||
cache = true
|
||||
max_cache_age = "1d"
|
||||
|
||||
accept = "200..=204, 302, 307, 429"
|
||||
cache_exclude_status = "500..=599, 403, 429"
|
||||
|
||||
require_https = true
|
||||
exclude_all_private = true
|
||||
|
||||
max_concurrency = 50
|
||||
accept_timeouts = false # Retry instead
|
||||
timeout = 15
|
||||
max_retries = 3
|
||||
retry_wait_time = 1
|
||||
|
||||
extensions = [
|
||||
"md", "html", "css", "js", "php", "phtml", "sh", "json", "jsonc", "xml", "yml", "yaml", "toml", "neon", "dist", "conf", "txt",
|
||||
|
||||
# Dotfiles
|
||||
"htaccess", "editorconfig", "gitattributes", "gitignore", "jshintignore", "jshintrc", "stylelintignore", "dockerignore"
|
||||
]
|
||||
|
||||
# For extensionless files
|
||||
default_extension = "txt"
|
||||
|
||||
include_verbatim = true
|
||||
hidden = true
|
||||
|
||||
exclude_path = [
|
||||
# Files
|
||||
'(^|/)index\.html$',
|
||||
'(^|/)\.github/ISSUE_TEMPLATE/feed-report-form\.yml$',
|
||||
|
||||
# Directories
|
||||
'(^|/)data/(cache|users|favicons)($|/)',
|
||||
'(^|/)lib/(phpmailer|simplepie|phpgt|marienfressinaud)($|/)',
|
||||
'(^|/)docs/(_includes|_layouts|i18n)($|/)',
|
||||
'(^|/)p/scripts/vendor($|/)'
|
||||
]
|
||||
|
||||
Reference in new issue
Block a user