Skip to content

Commit 8a27173

Browse files
committed
ci: run link check on a weekday schedule with Slack failure alerts
Mirrors crawlee-python's scheduled-tests workflow: runs weekdays at 01:00 UTC (same as crawlee) and pings the team Slack channel via SLACK_WEBHOOK_URL when a scheduled run fails (skipped on manual dispatch). Renames lychee.yml to on_schedule_lychee.yaml to match the on_schedule_* convention.
1 parent a36095d commit 8a27173

2 files changed

Lines changed: 148 additions & 71 deletions

File tree

.github/workflows/lychee.yml

Lines changed: 0 additions & 71 deletions
This file was deleted.
Lines changed: 148 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,148 @@
1+
name: Scheduled link check
2+
3+
on:
4+
# Runs when manually triggered from the GitHub UI.
5+
workflow_dispatch:
6+
7+
# Runs on weekdays at 01:00 UTC.
8+
schedule:
9+
- cron: '0 1 * * 1-5'
10+
11+
concurrency:
12+
group: scheduled-link-check
13+
cancel-in-progress: false
14+
15+
permissions:
16+
contents: read
17+
18+
env:
19+
NODE_VERSION: 22
20+
PYTHON_VERSION: 3.14
21+
22+
jobs:
23+
link_check:
24+
name: Link check
25+
runs-on: ubuntu-latest
26+
27+
steps:
28+
- name: Checkout repository
29+
uses: actions/checkout@v6
30+
31+
- name: Set up Node
32+
uses: actions/setup-node@v6
33+
with:
34+
node-version: ${{ env.NODE_VERSION }}
35+
36+
- name: Set up Python
37+
uses: actions/setup-python@v6
38+
with:
39+
python-version: ${{ env.PYTHON_VERSION }}
40+
41+
- name: Set up uv package manager
42+
uses: astral-sh/setup-uv@v8.2.0
43+
with:
44+
python-version: ${{ env.PYTHON_VERSION }}
45+
46+
- name: Install Python dependencies
47+
run: uv run poe install-dev
48+
49+
- name: Install pnpm and website dependencies
50+
uses: apify/actions/pnpm-install@v1.2.0
51+
with:
52+
working-directory: website
53+
54+
- name: Build docs
55+
run: uv run poe build-docs
56+
env:
57+
APIFY_SIGNING_TOKEN: ${{ secrets.APIFY_SIGNING_TOKEN }}
58+
SEGMENT_TOKEN: ${{ secrets.SEGMENT_TOKEN }}
59+
60+
- name: Run Lychee link checker
61+
id: lychee
62+
uses: lycheeverse/lychee-action@v2.8.0
63+
env:
64+
GITHUB_TOKEN: ${{ secrets.APIFY_SERVICE_ACCOUNT_GITHUB_TOKEN }}
65+
with:
66+
fail: true
67+
args: >
68+
--base https://docs.apify.com
69+
--max-retries 6
70+
--verbose
71+
--no-progress
72+
--timeout '60'
73+
--accept '100..=103,200..=299,429'
74+
--max-redirects 5
75+
--format markdown
76+
'./website/build/**/*.html'
77+
78+
# Send a Slack notification to the team alerting channel when the scheduled link check fails.
79+
# Skipped on workflow_dispatch (manual runs) so that ad-hoc triggers don't spam the channel.
80+
notify_on_failure:
81+
name: Notify Slack on failure
82+
needs: link_check
83+
if: failure() && github.event_name == 'schedule'
84+
runs-on: ubuntu-latest
85+
permissions:
86+
contents: read
87+
actions: read
88+
89+
steps:
90+
- name: Build Slack payload
91+
env:
92+
GH_TOKEN: ${{ secrets.GITHUB_TOKEN }}
93+
REPO: ${{ github.repository }}
94+
RUN_ID: ${{ github.run_id }}
95+
RUN_ATTEMPT: ${{ github.run_attempt }}
96+
WORKFLOW_URL: ${{ github.server_url }}/${{ github.repository }}/actions/runs/${{ github.run_id }}
97+
HEADING: ':red_circle: Scheduled link check failed'
98+
run: |
99+
# Retry the API call to tolerate transient 5xx from GitHub.
100+
max_attempts=5
101+
fetched=0
102+
for attempt in $(seq 1 "${max_attempts}"); do
103+
if failed_jobs=$(gh api \
104+
"repos/${REPO}/actions/runs/${RUN_ID}/attempts/${RUN_ATTEMPT}/jobs?per_page=100" \
105+
--jq '[.jobs[] | select(.conclusion == "failure") | "• \(.name)"] | join("\n")'); then
106+
fetched=1
107+
break
108+
fi
109+
if [[ "${attempt}" -lt "${max_attempts}" ]]; then
110+
sleep "$((attempt * 5))"
111+
fi
112+
done
113+
if [[ "${fetched}" -eq 0 ]]; then
114+
echo "Failed to fetch job list after ${max_attempts} attempts; sending notification without it." >&2
115+
failed_jobs="(unable to fetch job list — see workflow run)"
116+
fi
117+
jq -n \
118+
--arg repo "${REPO}" \
119+
--arg url "${WORKFLOW_URL}" \
120+
--arg heading "${HEADING}" \
121+
--arg failed "${failed_jobs}" \
122+
'{
123+
text: "\($heading) in \($repo)",
124+
blocks: [
125+
{
126+
type: "header",
127+
text: { type: "plain_text", text: $heading, emoji: true }
128+
},
129+
{
130+
type: "section",
131+
fields: [
132+
{ type: "mrkdwn", text: "*Repository:*\n\($repo)" },
133+
{ type: "mrkdwn", text: "*Workflow run:*\n<\($url)|View on GitHub>" }
134+
]
135+
},
136+
{
137+
type: "section",
138+
text: { type: "mrkdwn", text: "*Failed jobs:*\n\($failed)" }
139+
}
140+
]
141+
}' > slack-payload.json
142+
143+
- name: Send Slack notification
144+
uses: slackapi/slack-github-action@v3.0.3
145+
with:
146+
webhook: ${{ secrets.SLACK_WEBHOOK_URL }}
147+
webhook-type: incoming-webhook
148+
payload-file-path: slack-payload.json

0 commit comments

Comments
 (0)