1+ name : Check for Dead Links
2+
3+ on :
4+ workflow_run :
5+ workflows : ["Deploy to GitHub Pages"]
6+ types :
7+ - completed
8+ workflow_dispatch :
9+
10+ permissions :
11+ issues : write
12+ contents : read
13+
14+ jobs :
15+ check-links :
16+ runs-on : ubuntu-latest
17+ if : ${{ github.event.workflow_run.conclusion == 'success' }}
18+
19+ steps :
20+ - name : Checkout
21+ uses : actions/checkout@v4
22+
23+ - name : Wait for deployment to be ready
24+ run : |
25+ echo "Waiting 30 seconds for deployment to be fully available..."
26+ sleep 30
27+
28+ - name : Install lychee
29+ run : |
30+ wget https://github.com/lycheeverse/lychee/releases/latest/download/lychee-x86_64-unknown-linux-gnu.tar.gz
31+ tar -xzf lychee-x86_64-unknown-linux-gnu.tar.gz
32+ sudo mv lychee /usr/local/bin/
33+
34+ - name : Check for dead links
35+ id : lychee
36+ run : |
37+ # Run lychee and capture output
38+ lychee --format json --output results.json https://happy.engineering/ || true
39+
40+ # Check if any links failed
41+ if [ -f "results.json" ]; then
42+ FAILED_COUNT=$(jq '[.[] | select(.status == "Failed")] | length' results.json)
43+ echo "failed_count=$FAILED_COUNT" >> $GITHUB_OUTPUT
44+
45+ if [ "$FAILED_COUNT" -gt 0 ]; then
46+ echo "has_failures=true" >> $GITHUB_OUTPUT
47+ # Format the failed links for the issue body
48+ echo "FAILED_LINKS<<EOF" >> $GITHUB_OUTPUT
49+ jq -r '.[] | select(.status == "Failed") | "- **\(.url)** - \(.chain[0].status // "Unknown error")"' results.json >> $GITHUB_OUTPUT
50+ echo "EOF" >> $GITHUB_OUTPUT
51+ else
52+ echo "has_failures=false" >> $GITHUB_OUTPUT
53+ fi
54+ else
55+ echo "has_failures=false" >> $GITHUB_OUTPUT
56+ echo "failed_count=0" >> $GITHUB_OUTPUT
57+ fi
58+
59+ - name : Check for existing issue
60+ id : existing-issue
61+ if : steps.lychee.outputs.has_failures == 'true'
62+ uses : actions/github-script@v7
63+ with :
64+ script : |
65+ const issues = await github.rest.issues.listForRepo({
66+ owner: context.repo.owner,
67+ repo: context.repo.repo,
68+ state: 'open',
69+ labels: 'broken-links'
70+ });
71+
72+ const existingIssue = issues.data.find(issue =>
73+ issue.title.includes('Dead Links Found')
74+ );
75+
76+ return existingIssue ? existingIssue.number : null;
77+
78+ - name : Create or update issue
79+ if : steps.lychee.outputs.has_failures == 'true'
80+ uses : actions/github-script@v7
81+ with :
82+ script : |
83+ const failedLinks = `${{ steps.lychee.outputs.FAILED_LINKS }}`;
84+ const failedCount = ${{ steps.lychee.outputs.failed_count }};
85+ const existingIssueNumber = ${{ steps.existing-issue.outputs.result }};
86+
87+ const issueBody = `## 🔗 Dead Links Detected
88+
89+ The post-deployment link check found **${failedCount}** broken link${failedCount > 1 ? 's' : ''}:
90+
91+ ${failedLinks}
92+
93+ **Deployment:** ${{ github.event.workflow_run.html_url }}
94+ **Site:** https://happy.engineering/
95+ **Checked on:** ${new Date().toISOString()}
96+
97+ Please fix these broken links to ensure a good user experience.
98+
99+ ---
100+ *This issue was automatically created by the post-deployment link checker.*`;
101+
102+ if (existingIssueNumber) {
103+ // Update existing issue
104+ await github.rest.issues.createComment({
105+ owner: context.repo.owner,
106+ repo: context.repo.repo,
107+ issue_number: existingIssueNumber,
108+ body: `## 🔄 New Dead Links Found\n\n${issueBody}`
109+ });
110+
111+ console.log(`Updated existing issue #${existingIssueNumber}`);
112+ } else {
113+ // Create new issue
114+ const issue = await github.rest.issues.create({
115+ owner: context.repo.owner,
116+ repo: context.repo.repo,
117+ title: `🔗 Dead Links Found on https://happy.engineering (${failedCount} link${failedCount > 1 ? 's' : ''})`,
118+ body: issueBody,
119+ labels: ['bug', 'broken-links'],
120+ assignees: [context.repo.owner]
121+ });
122+
123+ console.log(`Created new issue #${issue.data.number}`);
124+ }
125+
126+ - name : Success message
127+ if : steps.lychee.outputs.has_failures == 'false'
128+ run : |
129+ echo "✅ No dead links found on https://happy.engineering/"
0 commit comments