Skip to content

Validate Links

Validate Links #22

name: Validate Links
on:
schedule:
# Run daily at 2:00 AM UTC
- cron: '0 2 * * *'
workflow_dispatch: # Allow manual triggering
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
permissions:
contents: read
issues: write
jobs:
validate-links:
runs-on: ubuntu-latest
steps:
- name: Checkout repository
uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install dependencies
run: make install
- name: Run link validation
id: validate
run: |
make validate-github
echo "has_broken_links=$(python -c "import json; data=json.load(open('validation_results.json')); print('true' if data['newly_broken'] else 'false')")" >> "$GITHUB_OUTPUT"
- name: Upload validation results
if: always()
uses: actions/upload-artifact@v4
with:
name: validation-results
path: |
validation_results.json
THE_RESOURCES_TABLE.csv
- name: Check for existing issue
if: steps.validate.outputs.has_broken_links == 'true'
id: check_issue
uses: actions/github-script@v7
with:
script: |
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'broken-links'
});
const today = new Date().toISOString().split('T')[0];
const existingIssue = issues.data.find(issue =>
issue.title.includes('Broken Links Report') &&
issue.title.includes(today)
);
core.setOutput('issue_number', existingIssue ? existingIssue.number : '');
- name: Create or update issue
if: steps.validate.outputs.has_broken_links == 'true'
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const results = JSON.parse(fs.readFileSync('validation_results.json', 'utf8'));
const today = new Date().toISOString().split('T')[0];
let issueBody = `## 🔗 Broken Links Report\n\n`;
issueBody += `This automated scan found **${results.newly_broken_links.length}** new broken link(s) in the repository.\n\n`;
issueBody += `### Broken Links:\n\n`;
for (const link of results.newly_broken_links) {
issueBody += `- **${link.name}**\n`;
issueBody += ` - URL: ${link.url}\n`;
}
issueBody += `### Summary\n\n`;
issueBody += `- Broken links: ${results.newly_broken_links.length}\n`;
issueBody += `- Scan completed: ${results.timestamp}\n\n`;
issueBody += `---\n`;
issueBody += `*This issue was automatically created by the [link validation workflow](${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/workflows/validate-links.yml).*`;
const existingIssueNumber = ${{ steps.check_issue.outputs.result }} || 0;
if (existingIssueNumber) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: existingIssueNumber,
body: issueBody
});
console.log(`Updated existing issue #${existingIssueNumber}`);
} else {
const issue = await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🚨 Broken Links Report - ${today}`,
body: issueBody,
labels: ['broken-links', 'automated']
});
console.log(`Created new issue #${issue.data.number}`);
}
- name: Close old broken link issues
if: steps.validate.outputs.has_broken_links == 'false'
uses: actions/github-script@v7
with:
script: |
const issues = await github.rest.issues.listForRepo({
owner: context.repo.owner,
repo: context.repo.repo,
state: 'open',
labels: 'broken-links'
});
for (const issue of issues.data) {
await github.rest.issues.update({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
state: 'closed',
state_reason: 'completed'
});
await github.rest.issues.createComment({
owner: context.repo.owner,
repo: context.repo.repo,
issue_number: issue.number,
body: '✅ All links are now working! Closing this issue.'
});
console.log(`Closed issue #${issue.number}`);
}