Skip to content

Commit 0cf9877

Browse files
committed
Updated with PR comments
1 parent 7d13f82 commit 0cf9877

4 files changed

Lines changed: 150 additions & 107 deletions

File tree

Lines changed: 12 additions & 94 deletions
Original file line numberDiff line numberDiff line change
@@ -1,20 +1,21 @@
11
name: Template Compatibility Comment
22

33
# Triggered when the unprivileged "Template Compatibility Check" workflow
4-
# completes. This workflow has pull-requests: write so it can post PR comments,
5-
# but it never checks out or executes any external code — it only reads the
6-
# artifact produced by the check workflow.
4+
# completes. This workflow has pull-requests: write so it can post PR comments.
5+
# It checks out the default branch (only) to load scripts/template-compatibility-comment.js,
6+
# then reads the artifact produced by the check workflow.
77
#
88
# SECURITY: workflow_run always runs on the default branch, so this workflow
9-
# definition itself cannot be tampered with by a PR contributor. Artifact
10-
# contents are treated as untrusted strings and sanitized before use.
9+
# definition and checked-out script cannot be tampered with by a PR contributor.
10+
# Artifact contents are treated as untrusted strings and sanitized before use.
1111

1212
on:
1313
workflow_run:
1414
workflows: ["Template Compatibility Check"]
1515
types: [completed]
1616

1717
permissions:
18+
contents: read
1819
pull-requests: write
1920
actions: read # required to download artifacts from another workflow run
2021

@@ -23,6 +24,9 @@ jobs:
2324
runs-on: ubuntu-latest
2425

2526
steps:
27+
- name: Checkout repository
28+
uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
29+
2630
- name: Download results artifact
2731
uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0
2832
with:
@@ -35,92 +39,6 @@ jobs:
3539
uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1
3640
with:
3741
script: |
38-
const fs = require('fs');
39-
40-
const read = (filename) => {
41-
try { return fs.readFileSync(`/tmp/compat-results/${filename}`, 'utf8').trim(); }
42-
catch { return ''; }
43-
};
44-
45-
// Validate PR number — must be a positive integer.
46-
const prNumber = parseInt(read('pr-number.txt'), 10);
47-
if (!Number.isInteger(prNumber) || prNumber <= 0) {
48-
console.log('Invalid or missing PR number in artifact; skipping comment.');
49-
return;
50-
}
51-
52-
const exitCode = read('exit-code.txt');
53-
const fullOutput = read('output.txt');
54-
// Sanitize values read from the artifact before embedding in markdown
55-
// to prevent injection (e.g. a malicious branch name or script output
56-
// containing markdown syntax that escapes a code fence).
57-
const templatesRef = read('templates-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, '');
58-
const headRef = read('head-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, '');
59-
60-
const marker = '<!-- template-compat-comment -->';
61-
62-
const { data: comments } = await github.rest.issues.listComments({
63-
owner: context.repo.owner,
64-
repo: context.repo.repo,
65-
issue_number: prNumber,
66-
});
67-
const existing = comments.find(c => c.body.includes(marker));
68-
69-
if (exitCode === '0') {
70-
// Templates pass — remove any stale failure comment.
71-
if (existing) {
72-
await github.rest.issues.deleteComment({
73-
owner: context.repo.owner,
74-
repo: context.repo.repo,
75-
comment_id: existing.id,
76-
});
77-
}
78-
return;
79-
}
80-
81-
// Extract just the "Results" and "Failure Details" sections from output.
82-
const resultsMatch = fullOutput.match(/={8,}\nResults:.*\n={8,}[\s\S]*/);
83-
const failureSummary = resultsMatch ? resultsMatch[0].trim() : fullOutput.trim();
84-
85-
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.payload.workflow_run.id}`;
86-
const refNote = templatesRef === 'main'
87-
? 'tested against `cre-templates:main`'
88-
: `tested against \`cre-templates:${templatesRef}\` (compat branch)`;
89-
90-
const body = [
91-
'## ⚠️ Template Compatibility Failures',
92-
'',
93-
`This PR breaks one or more templates in [cre-templates](https://github.com/smartcontractkit/cre-templates) (${refNote}).`,
94-
'',
95-
'```',
96-
failureSummary,
97-
'```',
98-
'',
99-
`[View full output →](${runUrl})`,
100-
'',
101-
'<details>',
102-
'<summary>What should I do?</summary>',
103-
'',
104-
'- **Accidental break:** Fix the SDK change so existing templates continue to compile.',
105-
`- **Intentional breaking change:** Create a branch in \`cre-templates\` named \`compat/${headRef}\` with the template fixes applied. This job will automatically retest against that branch.`,
106-
'',
107-
'</details>',
108-
].join('\n');
109-
110-
const commentBody = `${marker}\n${body}`;
111-
112-
if (existing) {
113-
await github.rest.issues.updateComment({
114-
owner: context.repo.owner,
115-
repo: context.repo.repo,
116-
comment_id: existing.id,
117-
body: commentBody,
118-
});
119-
} else {
120-
await github.rest.issues.createComment({
121-
owner: context.repo.owner,
122-
repo: context.repo.repo,
123-
issue_number: prNumber,
124-
body: commentBody,
125-
});
126-
}
42+
const path = require('path');
43+
const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.js'));
44+
await run({ github, context });

.github/workflows/template-compatibility.yml

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,10 @@ on:
44
pull_request:
55
types: [opened, synchronize, reopened]
66

7+
concurrency:
8+
group: template-compat-${{ github.head_ref || github.ref }}
9+
cancel-in-progress: true
10+
711
# This job is informational — it is intentionally NOT a required status check
812
# so that breaking SDK changes (major version bumps) can still be merged.
913
# When templates break, a comment is posted on the PR with details.
@@ -17,6 +21,7 @@ permissions:
1721
jobs:
1822
template-compatibility:
1923
runs-on: ubuntu-latest
24+
timeout-minutes: 20
2025

2126
defaults:
2227
run:
Lines changed: 100 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,100 @@
1+
/**
2+
* Used by .github/workflows/template-compatibility-comment.yml.
3+
* Reads /tmp/compat-results from the prior workflow's artifact and posts or
4+
* removes a PR comment via the GitHub API.
5+
*/
6+
module.exports = async ({ github, context }) => {
7+
const fs = require('fs');
8+
9+
const read = (filename) => {
10+
try {
11+
return fs.readFileSync(`/tmp/compat-results/${filename}`, 'utf8').trim();
12+
} catch {
13+
return '';
14+
}
15+
};
16+
17+
// Validate PR number — must be a positive integer.
18+
const prNumber = parseInt(read('pr-number.txt'), 10);
19+
if (!Number.isInteger(prNumber) || prNumber <= 0) {
20+
console.log('Invalid or missing PR number in artifact; skipping comment.');
21+
return;
22+
}
23+
24+
const exitCode = read('exit-code.txt');
25+
const fullOutput = read('output.txt');
26+
// Sanitize values read from the artifact before embedding in markdown
27+
// to prevent injection (e.g. a malicious branch name or script output
28+
// containing markdown syntax that escapes a code fence).
29+
const templatesRef = read('templates-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, '');
30+
const headRef = read('head-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, '');
31+
32+
const marker = '<!-- template-compat-comment -->';
33+
34+
const { data: comments } = await github.rest.issues.listComments({
35+
owner: context.repo.owner,
36+
repo: context.repo.repo,
37+
issue_number: prNumber,
38+
});
39+
const existing = comments.find((c) => c.body.includes(marker));
40+
41+
if (exitCode === '0') {
42+
// Templates pass — remove any stale failure comment.
43+
if (existing) {
44+
await github.rest.issues.deleteComment({
45+
owner: context.repo.owner,
46+
repo: context.repo.repo,
47+
comment_id: existing.id,
48+
});
49+
}
50+
return;
51+
}
52+
53+
// Extract just the "Results" and "Failure Details" sections from output.
54+
const resultsMatch = fullOutput.match(/={8,}\nResults:.*\n={8,}[\s\S]*/);
55+
const failureSummary = resultsMatch ? resultsMatch[0].trim() : fullOutput.trim();
56+
57+
const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.payload.workflow_run.id}`;
58+
const refNote =
59+
templatesRef === 'main'
60+
? 'tested against `cre-templates:main`'
61+
: `tested against \`cre-templates:${templatesRef}\` (compat branch)`;
62+
63+
const body = [
64+
'## ⚠️ Template Compatibility Failures',
65+
'',
66+
`This PR breaks one or more templates in [cre-templates](https://github.com/smartcontractkit/cre-templates) (${refNote}).`,
67+
'',
68+
'```',
69+
failureSummary,
70+
'```',
71+
'',
72+
`[View full output →](${runUrl})`,
73+
'',
74+
'<details>',
75+
'<summary>What should I do?</summary>',
76+
'',
77+
'- **Accidental break:** Fix the SDK change so existing templates continue to compile.',
78+
`- **Intentional breaking change:** Create a branch in \`cre-templates\` named \`compat/${headRef}\` with the template fixes applied. This job will automatically retest against that branch.`,
79+
'',
80+
'</details>',
81+
].join('\n');
82+
83+
const commentBody = `${marker}\n${body}`;
84+
85+
if (existing) {
86+
await github.rest.issues.updateComment({
87+
owner: context.repo.owner,
88+
repo: context.repo.repo,
89+
comment_id: existing.id,
90+
body: commentBody,
91+
});
92+
} else {
93+
await github.rest.issues.createComment({
94+
owner: context.repo.owner,
95+
repo: context.repo.repo,
96+
issue_number: prNumber,
97+
body: commentBody,
98+
});
99+
}
100+
};

scripts/test-templates.sh

Lines changed: 33 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -140,24 +140,44 @@ info ""
140140
info "Packing SDK..."
141141

142142
cd packages/cre-sdk-javy-plugin
143-
JAVY_TARBALL=$(npm pack --quiet 2>/dev/null)
144-
if [ -z "$JAVY_TARBALL" ]; then
143+
_pack_log=$(mktemp)
144+
if ! bun pm pack --quiet >"$_pack_log" 2>&1; then
145145
info "❌ Failed to pack Javy plugin."
146+
info "$(cat "$_pack_log")"
147+
rm -f "$_pack_log"
146148
exit 1
147149
fi
150+
JAVY_TARBALL=$(grep -oE '[^[:space:]]+\.tgz' "$_pack_log" | tail -n1 | tr -d '\r\n')
151+
if [ -z "$JAVY_TARBALL" ] || [ ! -f "$(pwd)/$JAVY_TARBALL" ]; then
152+
info "❌ Failed to pack Javy plugin (no .tgz produced or name not found)."
153+
info "$(cat "$_pack_log")"
154+
rm -f "$_pack_log"
155+
exit 1
156+
fi
157+
rm -f "$_pack_log"
148158
JAVY_TARBALL_PATH="$(pwd)/$JAVY_TARBALL"
149159
SDK_TARBALLS+=("$JAVY_TARBALL_PATH")
150160
vlog " Javy plugin: $JAVY_TARBALL_PATH"
151161

152162
cd ../cre-sdk
153163
cp package.json package.json.bak
154-
npm pkg set dependencies."@chainlink/cre-sdk-javy-plugin"="file:$JAVY_TARBALL_PATH"
164+
bun pm pkg set "dependencies.@chainlink/cre-sdk-javy-plugin=file:$JAVY_TARBALL_PATH"
155165

156-
TARBALL=$(npm pack --quiet 2>/dev/null)
157-
if [ -z "$TARBALL" ]; then
166+
_pack_log=$(mktemp)
167+
if ! bun pm pack --quiet >"$_pack_log" 2>&1; then
158168
info "❌ Failed to pack SDK."
169+
info "$(cat "$_pack_log")"
170+
rm -f "$_pack_log"
171+
exit 1
172+
fi
173+
TARBALL=$(grep -oE '[^[:space:]]+\.tgz' "$_pack_log" | tail -n1 | tr -d '\r\n')
174+
if [ -z "$TARBALL" ] || [ ! -f "$(pwd)/$TARBALL" ]; then
175+
info "❌ Failed to pack SDK (no .tgz produced or name not found)."
176+
info "$(cat "$_pack_log")"
177+
rm -f "$_pack_log"
159178
exit 1
160179
fi
180+
rm -f "$_pack_log"
161181
TARBALL_PATH="$(pwd)/$TARBALL"
162182
SDK_TARBALLS+=("$TARBALL_PATH")
163183
vlog " SDK: $TARBALL_PATH"
@@ -173,7 +193,7 @@ info ""
173193

174194
if [ ! -d "$TEMPLATES_DIR" ]; then
175195
info "❌ Templates directory not found: $TEMPLATES_DIR"
176-
info "Override with: TEMPLATES_DIR=/path/to/cre-templates ./scripts/test-templates.sh"
196+
info "Override with: TEMPLATES_DIR=/path/to/cre-templates bun run test:templates"
177197
exit 1
178198
fi
179199

@@ -226,17 +246,17 @@ for pkg in "${ALL_PKGS[@]}"; do
226246
# Install dependencies
227247
vlog " Installing dependencies..."
228248
_out=""
229-
if ! run_captured _out npm install --no-audit --fund=false; then
249+
if ! run_captured _out bun install; then
230250
info "$PREFIX $DISPLAY_NAME"
231251
FAILED_TEMPLATES+=("$DISPLAY_NAME")
232-
FAILURE_STEPS+=("npm install")
252+
FAILURE_STEPS+=("bun install")
233253
FAILURE_OUTPUTS+=("$_out")
234254
continue
235255
fi
236256

237257
# Inject local SDK tarball
238258
vlog " Installing local SDK..."
239-
if ! run_captured _out npm install --no-save --no-audit --fund=false "$TARBALL_PATH"; then
259+
if ! run_captured _out bun install --no-save "@chainlink/cre-sdk@file:$TARBALL_PATH"; then
240260
info "$PREFIX $DISPLAY_NAME"
241261
FAILED_TEMPLATES+=("$DISPLAY_NAME")
242262
FAILURE_STEPS+=("sdk install")
@@ -249,7 +269,7 @@ for pkg in "${ALL_PKGS[@]}"; do
249269
# Typecheck
250270
if grep -q '"typecheck"' package.json; then
251271
vlog " Running typecheck..."
252-
if ! run_captured _out npm run typecheck --silent; then
272+
if ! run_captured _out bun run typecheck; then
253273
info "$PREFIX $DISPLAY_NAME"
254274
FAILED_TEMPLATES+=("$DISPLAY_NAME")
255275
FAILURE_STEPS+=("typecheck")
@@ -268,7 +288,7 @@ for pkg in "${ALL_PKGS[@]}"; do
268288
if [ -f "main.ts" ]; then
269289
GENERATED_FILES+=("$WORKFLOW_DIR/main.js" "$WORKFLOW_DIR/main.wasm")
270290
vlog " Running cre-compile..."
271-
if ! run_captured _out npx --no cre-compile main.ts; then
291+
if ! run_captured _out bunx cre-compile main.ts; then
272292
info "$PREFIX $DISPLAY_NAME"
273293
FAILED_TEMPLATES+=("$DISPLAY_NAME")
274294
FAILURE_STEPS+=("cre-compile")
@@ -312,8 +332,8 @@ if [ $FAIL_COUNT -gt 0 ]; then
312332
info ""
313333
info "${FAILED_TEMPLATES[$i]}${FAILURE_STEPS[$i]}"
314334
info "--------"
315-
# Print the captured output, stripping npm warn noise to highlight real errors
316-
echo "${FAILURE_OUTPUTS[$i]}" | grep -v "^npm warn" | grep -v "^$" || true
335+
# Print the captured output, stripping common PM noise to highlight real errors
336+
echo "${FAILURE_OUTPUTS[$i]}" | grep -v "^npm warn" | grep -v "^bun install v" | grep -v "^$" || true
317337
done
318338

319339
exit 1

0 commit comments

Comments
 (0)