Skip to content

Commit 96ab81f

Browse files
committed
CI: Add local developer versions of test-git-signatures and test-git-lfs. Modify test-git-lfs, test-git-signatures, and test-links to all add comments on PRs and link to the failures.
1 parent 1208243 commit 96ab81f

File tree

5 files changed

+626
-61
lines changed

5 files changed

+626
-61
lines changed

.github/workflows/test-git-lfs.yml

Lines changed: 97 additions & 57 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Test Git LFS Tracking
1+
name: Test Git LFS
22

33
on:
44
push:
@@ -8,66 +8,106 @@ on:
88
# see the "Reviewing PRs from forks" section in CONTRIBUTING.md for more details
99

1010
jobs:
11-
check-lfs:
11+
test-git-lfs:
1212
runs-on: ubuntu-latest
1313

1414
steps:
1515
- name: Checkout repository
1616
uses: actions/checkout@v4
1717

1818
- name: Check that binary files are tracked by LFS
19-
run: |
20-
echo "Checking that all binary files are tracked by Git LFS..."
21-
22-
# File extensions that should be tracked by LFS (from .gitattributes)
23-
EXTENSIONS="pptx pdf png jpg jpeg gif webp"
24-
25-
# Build find pattern
26-
FIND_PATTERN=""
27-
for ext in $EXTENSIONS; do
28-
if [ -n "$FIND_PATTERN" ]; then
29-
FIND_PATTERN="$FIND_PATTERN -o"
30-
fi
31-
FIND_PATTERN="$FIND_PATTERN -name *.$ext"
32-
done
33-
34-
# Find all matching files
35-
FILES=$(find . -type f \( $FIND_PATTERN \) ! -path "./.git/*" ! -path "./venv/*" 2>/dev/null || true)
36-
37-
if [ -z "$FILES" ]; then
38-
echo "No binary files found to check."
39-
exit 0
40-
fi
41-
42-
FAILED=0
43-
NOT_LFS_FILES=""
44-
45-
for file in $FILES; do
46-
# Check if file is an LFS pointer (starts with "version https://git-lfs.github.com/spec/v1")
47-
if head -c 50 "$file" 2>/dev/null | grep -q "version https://git-lfs.github.com/spec/v1"; then
48-
echo "✓ $file (LFS pointer)"
49-
else
50-
echo "✗ $file (NOT tracked by LFS)"
51-
NOT_LFS_FILES="$NOT_LFS_FILES\n - $file"
52-
FAILED=1
53-
fi
54-
done
55-
56-
if [ $FAILED -eq 1 ]; then
57-
echo ""
58-
echo "=========================================="
59-
echo "ERROR: The following files should be tracked by Git LFS but are not:"
60-
echo -e "$NOT_LFS_FILES"
61-
echo ""
62-
echo "To fix this, run:"
63-
echo " git rm --cached <file>"
64-
echo " git add <file>"
65-
echo " git commit -m 'Track file with Git LFS'"
66-
echo ""
67-
echo "Make sure Git LFS is installed and .gitattributes includes the file pattern."
68-
echo "=========================================="
69-
exit 1
70-
fi
71-
72-
echo ""
73-
echo "All binary files are properly tracked by Git LFS!"
19+
id: git-lfs-check
20+
run: bash brev/test-git-lfs.bash .
21+
22+
- name: Comment on PR if check failed
23+
if: failure() && steps.git-lfs-check.outputs.error_type != ''
24+
uses: actions/github-script@v7
25+
with:
26+
script: |
27+
// Find PR associated with this branch
28+
const { data: pulls } = await github.rest.pulls.list({
29+
owner: context.repo.owner,
30+
repo: context.repo.repo,
31+
head: `${context.repo.owner}:${process.env.GITHUB_REF_NAME}`,
32+
state: 'open'
33+
});
34+
35+
if (pulls.length === 0) {
36+
console.log('No open PR found for this branch, skipping comment.');
37+
return;
38+
}
39+
40+
const prNumber = pulls[0].number;
41+
const errorType = '${{ steps.git-lfs-check.outputs.error_type }}';
42+
const errorFiles = `${{ steps.git-lfs-check.outputs.error_files }}`;
43+
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
44+
45+
let commentBody;
46+
if (errorType === 'subdir_gitattributes') {
47+
commentBody = `## ❌ Git LFS Check Failed
48+
49+
**Found \`.gitattributes\` file(s) in subdirectories.** Only the top-level \`.gitattributes\` should be used for LFS configuration.
50+
51+
🔗 [View workflow run logs](${runUrl})
52+
53+
### Files to remove:
54+
${errorFiles.split('\n').map(f => f.trim()).filter(f => f).map(f => '- \`' + f + '\`').join('\n')}
55+
56+
### How to fix:
57+
\`\`\`bash
58+
git rm <path/to/.gitattributes>
59+
git commit -m "Remove subdirectory .gitattributes"
60+
\`\`\`
61+
`;
62+
} else if (errorType === 'not_lfs_tracked') {
63+
commentBody = `## ❌ Git LFS Check Failed
64+
65+
**The following files should be tracked by Git LFS but are not:**
66+
67+
🔗 [View workflow run logs](${runUrl})
68+
69+
${errorFiles}
70+
71+
### How to fix:
72+
\`\`\`bash
73+
# For each file listed above:
74+
git rm --cached <file>
75+
git add <file>
76+
git commit -m "Track file with Git LFS"
77+
\`\`\`
78+
79+
Make sure Git LFS is installed locally (\`git lfs install\`) before running these commands.
80+
`;
81+
}
82+
83+
// Check if we already commented on this PR to avoid spam
84+
const { data: comments } = await github.rest.issues.listComments({
85+
owner: context.repo.owner,
86+
repo: context.repo.repo,
87+
issue_number: prNumber
88+
});
89+
90+
const botComment = comments.find(comment =>
91+
comment.user.type === 'Bot' &&
92+
comment.body.includes('Git LFS Check Failed')
93+
);
94+
95+
if (botComment) {
96+
// Update existing comment
97+
await github.rest.issues.updateComment({
98+
owner: context.repo.owner,
99+
repo: context.repo.repo,
100+
comment_id: botComment.id,
101+
body: commentBody
102+
});
103+
console.log(`Updated existing comment on PR #${prNumber}`);
104+
} else {
105+
// Create new comment
106+
await github.rest.issues.createComment({
107+
owner: context.repo.owner,
108+
repo: context.repo.repo,
109+
issue_number: prNumber,
110+
body: commentBody
111+
});
112+
console.log(`Created comment on PR #${prNumber}`);
113+
}
Lines changed: 99 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
name: Test Commit Signatures
1+
name: Test Git Signatures
22

33
on:
44
push:
@@ -8,7 +8,7 @@ on:
88
# see the "Reviewing PRs from forks" section in CONTRIBUTING.md for more details
99

1010
jobs:
11-
test-signatures:
11+
test-git-signatures:
1212
runs-on: ubuntu-latest
1313

1414
steps:
@@ -17,7 +17,13 @@ jobs:
1717
with:
1818
fetch-depth: 0 # Need full history to compare with main
1919

20-
- name: Check commit signatures
20+
- name: Check commit signatures (local verification)
21+
id: git-signatures-check
22+
run: bash brev/test-git-signatures.bash origin/main
23+
24+
- name: Check commit signatures (GitHub API verification)
25+
id: github-api-check
26+
if: always() && !cancelled()
2127
uses: actions/github-script@v7
2228
with:
2329
script: |
@@ -50,7 +56,7 @@ jobs:
5056
return;
5157
}
5258
53-
console.log(`Checking ${commitShas.length} commit(s) for signatures...`);
59+
console.log(`Checking ${commitShas.length} commit(s) via GitHub API...`);
5460
5561
// Check each commit's verification status via GitHub API
5662
const unsignedCommits = [];
@@ -86,6 +92,10 @@ jobs:
8692
}
8793
8894
if (unsignedCommits.length > 0) {
95+
// Set outputs for PR comment
96+
core.setOutput('unsigned_count', unsignedCommits.length);
97+
core.setOutput('unsigned_commits', unsignedCommits.map(c => `- \`${c.sha}\`: ${c.message} (${c.reason})`).join('\n'));
98+
8999
console.log('\n❌ Found unsigned commits:');
90100
for (const commit of unsignedCommits) {
91101
console.log(` ${commit.sha}: ${commit.message} (reason: ${commit.reason})`);
@@ -97,3 +107,88 @@ jobs:
97107
console.log(`\n✅ All ${commitShas.length} commit(s) are properly signed.`);
98108
}
99109
110+
- name: Comment on PR if check failed
111+
if: failure() && steps.github-api-check.outputs.unsigned_count != ''
112+
uses: actions/github-script@v7
113+
with:
114+
script: |
115+
// Find PR associated with this branch
116+
const { data: pulls } = await github.rest.pulls.list({
117+
owner: context.repo.owner,
118+
repo: context.repo.repo,
119+
head: `${context.repo.owner}:${process.env.GITHUB_REF_NAME}`,
120+
state: 'open'
121+
});
122+
123+
if (pulls.length === 0) {
124+
console.log('No open PR found for this branch, skipping comment.');
125+
return;
126+
}
127+
128+
const prNumber = pulls[0].number;
129+
const unsignedCount = '${{ steps.github-api-check.outputs.unsigned_count }}';
130+
const unsignedCommits = `${{ steps.github-api-check.outputs.unsigned_commits }}`;
131+
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
132+
133+
const commentBody = `## ❌ Commit Signature Check Failed
134+
135+
**Found ${unsignedCount} unsigned commit(s):**
136+
137+
🔗 [View workflow run logs](${runUrl})
138+
139+
${unsignedCommits}
140+
141+
### All commits must be signed
142+
143+
#### How to fix:
144+
145+
1. **Configure commit signing** (if not already done):
146+
\`\`\`bash
147+
# For GPG signing
148+
git config --global commit.gpgsign true
149+
150+
# Or for SSH signing (Git 2.34+)
151+
git config --global gpg.format ssh
152+
git config --global user.signingkey ~/.ssh/id_ed25519.pub
153+
\`\`\`
154+
155+
2. **Re-sign your commits:**
156+
\`\`\`bash
157+
git rebase -i origin/main --exec "git commit --amend --no-edit -S"
158+
git push --force-with-lease
159+
\`\`\`
160+
161+
📚 [GitHub documentation on signing commits](https://docs.github.com/en/authentication/managing-commit-signature-verification)
162+
`;
163+
164+
// Check if we already commented on this PR to avoid spam
165+
const { data: comments } = await github.rest.issues.listComments({
166+
owner: context.repo.owner,
167+
repo: context.repo.repo,
168+
issue_number: prNumber
169+
});
170+
171+
const botComment = comments.find(comment =>
172+
comment.user.type === 'Bot' &&
173+
comment.body.includes('Commit Signature Check Failed')
174+
);
175+
176+
if (botComment) {
177+
// Update existing comment
178+
await github.rest.issues.updateComment({
179+
owner: context.repo.owner,
180+
repo: context.repo.repo,
181+
comment_id: botComment.id,
182+
body: commentBody
183+
});
184+
console.log(`Updated existing comment on PR #${prNumber}`);
185+
} else {
186+
// Create new comment
187+
await github.rest.issues.createComment({
188+
owner: context.repo.owner,
189+
repo: context.repo.repo,
190+
issue_number: prNumber,
191+
body: commentBody
192+
});
193+
console.log(`Created comment on PR #${prNumber}`);
194+
}

.github/workflows/test-links.yml

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -34,7 +34,83 @@ jobs:
3434
done
3535
3636
- name: Check links with Lychee
37+
id: lychee-check
3738
uses: lycheeverse/lychee-action@v2
3839
with:
3940
args: --verbose --no-progress --config brev/lychee.toml --exclude-file brev/.lycheeignore '.'
4041
fail: true
42+
43+
- name: Comment on PR if check failed
44+
if: failure() && steps.lychee-check.outcome == 'failure'
45+
uses: actions/github-script@v7
46+
with:
47+
script: |
48+
// Find PR associated with this branch
49+
const { data: pulls } = await github.rest.pulls.list({
50+
owner: context.repo.owner,
51+
repo: context.repo.repo,
52+
head: `${context.repo.owner}:${process.env.GITHUB_REF_NAME}`,
53+
state: 'open'
54+
});
55+
56+
if (pulls.length === 0) {
57+
console.log('No open PR found for this branch, skipping comment.');
58+
return;
59+
}
60+
61+
const prNumber = pulls[0].number;
62+
const runUrl = `${process.env.GITHUB_SERVER_URL}/${process.env.GITHUB_REPOSITORY}/actions/runs/${process.env.GITHUB_RUN_ID}`;
63+
64+
const commentBody = `## ❌ Link Check Failed
65+
66+
**Broken links were detected in this PR.**
67+
68+
Please check the [workflow run logs](${runUrl}) for details on which links are broken.
69+
70+
### Common fixes:
71+
72+
1. **Typo in URL** - Check for spelling mistakes in the link
73+
2. **Outdated link** - The page may have moved or been deleted
74+
3. **Relative path issue** - Ensure relative links use the correct path
75+
4. **External site down** - If the external site is temporarily down, you can add it to \`brev/.lycheeignore\`
76+
77+
### To test links locally:
78+
79+
\`\`\`bash
80+
./brev/test-links.bash .
81+
\`\`\`
82+
83+
📚 [Lychee documentation](https://github.com/lycheeverse/lychee)
84+
`;
85+
86+
// Check if we already commented on this PR to avoid spam
87+
const { data: comments } = await github.rest.issues.listComments({
88+
owner: context.repo.owner,
89+
repo: context.repo.repo,
90+
issue_number: prNumber
91+
});
92+
93+
const botComment = comments.find(comment =>
94+
comment.user.type === 'Bot' &&
95+
comment.body.includes('Link Check Failed')
96+
);
97+
98+
if (botComment) {
99+
// Update existing comment
100+
await github.rest.issues.updateComment({
101+
owner: context.repo.owner,
102+
repo: context.repo.repo,
103+
comment_id: botComment.id,
104+
body: commentBody
105+
});
106+
console.log(`Updated existing comment on PR #${prNumber}`);
107+
} else {
108+
// Create new comment
109+
await github.rest.issues.createComment({
110+
owner: context.repo.owner,
111+
repo: context.repo.repo,
112+
issue_number: prNumber,
113+
body: commentBody
114+
});
115+
console.log(`Created comment on PR #${prNumber}`);
116+
}

0 commit comments

Comments
 (0)