From 9bbbc7c0539d4596e02a6eaa6b560fd8138ea749 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Thu, 30 Apr 2026 09:13:20 -0400 Subject: [PATCH 1/8] Added script to test against templates and github action to run that test --- .../template-compatibility-comment.yml | 130 +++++++ .github/workflows/template-compatibility.yml | 153 +++++++++ package.json | 1 + scripts/test-templates.sh | 324 ++++++++++++++++++ 4 files changed, 608 insertions(+) create mode 100644 .github/workflows/template-compatibility-comment.yml create mode 100644 .github/workflows/template-compatibility.yml create mode 100755 scripts/test-templates.sh diff --git a/.github/workflows/template-compatibility-comment.yml b/.github/workflows/template-compatibility-comment.yml new file mode 100644 index 00000000..75d5daa3 --- /dev/null +++ b/.github/workflows/template-compatibility-comment.yml @@ -0,0 +1,130 @@ +name: Template Compatibility Comment + +# Triggered when the unprivileged "Template Compatibility Check" workflow +# completes. This workflow has pull-requests: write so it can post PR comments, +# but it never checks out or executes any external code — it only reads the +# artifact produced by the check workflow. +# +# SECURITY: workflow_run always runs on the default branch, so this workflow +# definition itself cannot be tampered with by a PR contributor. Artifact +# contents are treated as untrusted strings and sanitized before use. + +on: + workflow_run: + workflows: ["Template Compatibility Check"] + types: [completed] + workflow_dispatch: # temporary — remove before merging + inputs: + run_id: + required: true + +permissions: + pull-requests: write + actions: read # required to download artifacts from another workflow run + +jobs: + post-comment: + runs-on: ubuntu-latest + + steps: + - name: Download results artifact + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: template-compat-results + path: /tmp/compat-results + run-id: ${{ github.event.workflow_run.id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Post or remove PR comment + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + with: + script: | + const fs = require('fs'); + + const read = (filename) => { + try { return fs.readFileSync(`/tmp/compat-results/${filename}`, 'utf8').trim(); } + catch { return ''; } + }; + + // Validate PR number — must be a positive integer. + const prNumber = parseInt(read('pr-number.txt'), 10); + if (!Number.isInteger(prNumber) || prNumber <= 0) { + console.log('Invalid or missing PR number in artifact; skipping comment.'); + return; + } + + const exitCode = read('exit-code.txt'); + const fullOutput = read('output.txt'); + // Sanitize values read from the artifact before embedding in markdown + // to prevent injection (e.g. a malicious branch name or script output + // containing markdown syntax that escapes a code fence). + const templatesRef = read('templates-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, ''); + const headRef = read('head-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, ''); + + const marker = ''; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + const existing = comments.find(c => c.body.includes(marker)); + + if (exitCode === '0') { + // Templates pass — remove any stale failure comment. + if (existing) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + }); + } + return; + } + + // Extract just the "Results" and "Failure Details" sections from output. + const resultsMatch = fullOutput.match(/={8,}\nResults:.*\n={8,}[\s\S]*/); + const failureSummary = resultsMatch ? resultsMatch[0].trim() : fullOutput.trim(); + + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.payload.workflow_run.id}`; + const refNote = templatesRef === 'main' + ? 'tested against `cre-templates:main`' + : `tested against \`cre-templates:${templatesRef}\` (compat branch)`; + + const body = [ + '## ⚠️ Template Compatibility Failures', + '', + `This PR breaks one or more templates in [cre-templates](https://github.com/smartcontractkit/cre-templates) (${refNote}).`, + '', + '```', + failureSummary, + '```', + '', + `[View full output →](${runUrl})`, + '', + '
', + 'What should I do?', + '', + '- **Accidental break:** Fix the SDK change so existing templates continue to compile.', + `- **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.`, + '', + '
', + ].join('\n'); + + const commentBody = `${marker}\n${body}`; + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: commentBody, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody, + }); + } diff --git a/.github/workflows/template-compatibility.yml b/.github/workflows/template-compatibility.yml new file mode 100644 index 00000000..0e5be1a2 --- /dev/null +++ b/.github/workflows/template-compatibility.yml @@ -0,0 +1,153 @@ +name: Template Compatibility Check + +on: + pull_request: + types: [opened, synchronize, reopened] + +# This job is informational — it is intentionally NOT a required status check +# so that breaking SDK changes (major version bumps) can still be merged. +# When templates break, a comment is posted on the PR with details. +# For intentional breaking changes, create a matching branch in cre-templates +# named compat/ with the template fixes applied. +# The job will automatically detect and test against that branch. + +permissions: + contents: read + +jobs: + template-compatibility: + runs-on: ubuntu-latest + + defaults: + run: + shell: bash {0} + + env: + TEMPLATES_REPO: smartcontractkit/cre-templates + + steps: + - name: Checkout SDK + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + submodules: recursive + + - name: Setup Rust (1.85.0) with wasm target + uses: actions-rust-lang/setup-rust-toolchain@150fca883cd4034361b621bd4e6a9d34e5143606 # v1.15.4 + with: + toolchain: 1.85.0 + target: wasm32-wasip1 + override: true + + - name: Setup Bun + uses: oven-sh/setup-bun@0c5077e51419868618aeaa5fe8019c62421857d6 # v2.2.0 + with: + bun-version: 1.3.12 + + - name: Cache Bun dependencies + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: ~/.bun/install/cache + key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} + + - name: Cache cargo + Javy + uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + with: + path: | + ~/.cargo/registry + ~/.cargo/git + ~/.cache/javy + key: ${{ runner.os }}-cre-plugin-v8.1.0-${{ hashFiles('packages/cre-sdk-javy-plugin/src/javy_chainlink_sdk/Cargo.lock', 'packages/cre-sdk-javy-plugin/src/cre_generated_host.Cargo.lock') }} + + - name: Install SDK dependencies + run: bun install --frozen-lockfile + + # Detect whether a matching compat branch exists in cre-templates. + # If it does, we test against it (coordinated breaking change). + # If not, we fall back to main. + - name: Detect cre-templates ref to test against + id: detect-ref + env: + GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} + HEAD_REF: ${{ github.head_ref }} + run: | + SAFE_HEAD_REF="${HEAD_REF//[^a-zA-Z0-9._\/-]/}" + COMPAT_BRANCH="compat/$SAFE_HEAD_REF" + + if gh api "repos/$TEMPLATES_REPO/branches/$COMPAT_BRANCH" &>/dev/null; then + echo "ref=$COMPAT_BRANCH" >> "$GITHUB_OUTPUT" + echo "Using compat branch: $COMPAT_BRANCH" + else + echo "ref=main" >> "$GITHUB_OUTPUT" + echo "No compat branch found, using: main" + fi + + - name: Checkout cre-templates (${{ steps.detect-ref.outputs.ref }}) + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + with: + repository: ${{ env.TEMPLATES_REPO }} + ref: ${{ steps.detect-ref.outputs.ref }} + path: cre-templates + token: ${{ secrets.GITHUB_TOKEN }} + + - name: Setup Node (for npm) + uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + with: + node-version: '22' + + - name: Run template compatibility check + id: template-check + env: + TEMPLATES_DIR: cre-templates + run: | + set +e + OUTPUT=$(./scripts/test-templates.sh 2>&1) + EXIT_CODE=$? + set -e + + echo "$OUTPUT" > /tmp/template-check-output.txt + + # Surface it in the action log regardless + echo "$OUTPUT" + + echo "exit_code=$EXIT_CODE" >> "$GITHUB_OUTPUT" + + # Bundle everything the comment workflow needs into an artifact. + # The comment workflow runs with pull-requests: write but must never + # execute external code — it only reads these files. + - name: Save results for comment workflow + if: always() + env: + PR_NUMBER: ${{ github.event.pull_request.number }} + TEMPLATES_REF: ${{ steps.detect-ref.outputs.ref }} + HEAD_REF: ${{ github.head_ref }} + EXIT_CODE: ${{ steps.template-check.outputs.exit_code }} + run: | + mkdir -p /tmp/compat-results + printf '%s' "$PR_NUMBER" > /tmp/compat-results/pr-number.txt + printf '%s' "$TEMPLATES_REF" > /tmp/compat-results/templates-ref.txt + printf '%s' "$HEAD_REF" > /tmp/compat-results/head-ref.txt + printf '%s' "$EXIT_CODE" > /tmp/compat-results/exit-code.txt + if [ -f /tmp/template-check-output.txt ]; then + cp /tmp/template-check-output.txt /tmp/compat-results/output.txt + fi + + - name: Upload results artifact + if: always() + uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + with: + name: template-compat-results + path: /tmp/compat-results/ + retention-days: 7 + + # Always exit 0 — this job is informational, not a merge gate + - name: Report result (non-blocking) + if: always() + run: | + EXIT_CODE="${{ steps.template-check.outputs.exit_code }}" + if [ "$EXIT_CODE" = "0" ]; then + echo "✅ All templates are compatible with this SDK change." + else + echo "⚠️ Some templates failed — see PR comment for details." + echo "This check is informational and does not block merging." + fi + exit 0 diff --git a/package.json b/package.json index aee36150..87a28c9b 100644 --- a/package.json +++ b/package.json @@ -14,6 +14,7 @@ "check:ci": "turbo run check:ci", "format": "turbo run format", "full-checks": "./scripts/full-checks.sh", + "test:templates": "./scripts/test-templates.sh", "lint": "turbo run lint", "typecheck": "turbo run typecheck" }, diff --git a/scripts/test-templates.sh b/scripts/test-templates.sh new file mode 100755 index 00000000..48613165 --- /dev/null +++ b/scripts/test-templates.sh @@ -0,0 +1,324 @@ +#!/bin/bash +# test-templates.sh +# Builds the local SDK, packs it into a tarball, and runs it against all +# TypeScript templates in the cre-templates repository. +# The cre-templates directory is fully restored on exit, even if the script crashes. +# +# Usage: +# bun run test:templates [--verbose|-v] +# +# Environment variables: +# TEMPLATES_DIR Path to the cre-templates repo (default: ../cre-templates) +# VERBOSE Set to 1 to enable verbose output (same as --verbose) + +# -------------------------------------------------------------------------- +# Flags + +VERBOSE=false +for arg in "$@"; do + case "$arg" in + -v|--verbose) VERBOSE=true ;; + esac +done +[ "${VERBOSE:-0}" = "1" ] && VERBOSE=true + +# -------------------------------------------------------------------------- +# Logging helpers + +# Always printed +info() { echo "$@"; } +# Only printed in verbose mode +vlog() { $VERBOSE && echo "$@" || true; } + +# Run a command, streaming output in verbose mode or capturing it silently. +# Usage: run_captured [args...] +# Sets $output_var to the combined stdout+stderr of the command. +# Returns the command's exit code. +run_captured() { + local _outvar="$1"; shift + local _tmpfile; _tmpfile=$(mktemp) + if $VERBOSE; then + "$@" 2>&1 | tee "$_tmpfile"; local _rc="${PIPESTATUS[0]}" + else + "$@" > "$_tmpfile" 2>&1; local _rc=$? + fi + # shellcheck disable=SC2086 + printf -v "$_outvar" '%s' "$(cat "$_tmpfile")" + rm -f "$_tmpfile" + return "$_rc" +} + +# -------------------------------------------------------------------------- +# Cleanup tracking + +LOCKFILE_BACKUPS=() # "backup_path:original_path" pairs +GENERATED_FILES=() # files to delete on exit +SDK_TARBALLS=() # tarball files to delete on exit +TEMP_FILES=() # misc temp files + +MONOREPO_ROOT="" # set after cd below + +cleanup() { + info "" + info "Cleaning up..." + + local sdk_pkg_bak="$MONOREPO_ROOT/packages/cre-sdk/package.json.bak" + if [ -f "$sdk_pkg_bak" ]; then + mv "$sdk_pkg_bak" "$MONOREPO_ROOT/packages/cre-sdk/package.json" + vlog " Restored: packages/cre-sdk/package.json" + fi + + for entry in "${LOCKFILE_BACKUPS[@]+"${LOCKFILE_BACKUPS[@]}"}"; do + local backup="${entry%%:*}" + local original="${entry##*:}" + if [ -f "$backup" ]; then + mv "$backup" "$original" + vlog " Restored: $original" + fi + done + + for f in "${GENERATED_FILES[@]+"${GENERATED_FILES[@]}"}"; do + if [ -f "$f" ]; then + rm "$f" + vlog " Removed: $f" + fi + done + + for f in "${SDK_TARBALLS[@]+"${SDK_TARBALLS[@]}"}"; do + if [ -f "$f" ]; then + rm "$f" + vlog " Removed: $f" + fi + done + + for f in "${TEMP_FILES[@]+"${TEMP_FILES[@]}"}"; do + rm -f "$f" + done +} + +trap cleanup EXIT INT TERM + +# -------------------------------------------------------------------------- +# Setup + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +MONOREPO_ROOT="$(dirname "$SCRIPT_DIR")" +cd "$MONOREPO_ROOT" + +TEMPLATES_DIR="${TEMPLATES_DIR:-../cre-templates}" + +# -------------------------------------------------------------------------- +# 1. Build + +info "Building SDK..." + +# Back up the compiled wasm artifact before the build overwrites it. +# Using a file backup (not git restore) so we restore whatever state the +# developer had — including uncommitted changes — not just the last commit. +# IMPORTANT: the backup must live outside the dist/ directory because the +# build's clean step runs `rm -rf dist`, which would delete an in-place backup. +WASM_FILE="$MONOREPO_ROOT/packages/cre-sdk-javy-plugin/dist/javy-chainlink-sdk.plugin.wasm" +if [ -f "$WASM_FILE" ]; then + WASM_BACKUP=$(mktemp) + cp "$WASM_FILE" "$WASM_BACKUP" + LOCKFILE_BACKUPS+=("$WASM_BACKUP:$WASM_FILE") +fi + +_build_out="" +if ! run_captured _build_out bun run build; then + info "❌ SDK build failed." + info "" + info "$_build_out" + exit 1 +fi +info "✅ SDK built." +info "" + +# -------------------------------------------------------------------------- +# 2. Pack + +info "Packing SDK..." + +cd packages/cre-sdk-javy-plugin +JAVY_TARBALL=$(npm pack --quiet 2>/dev/null) +if [ -z "$JAVY_TARBALL" ]; then + info "❌ Failed to pack Javy plugin." + exit 1 +fi +JAVY_TARBALL_PATH="$(pwd)/$JAVY_TARBALL" +SDK_TARBALLS+=("$JAVY_TARBALL_PATH") +vlog " Javy plugin: $JAVY_TARBALL_PATH" + +cd ../cre-sdk +cp package.json package.json.bak +npm pkg set dependencies."@chainlink/cre-sdk-javy-plugin"="file:$JAVY_TARBALL_PATH" + +TARBALL=$(npm pack --quiet 2>/dev/null) +if [ -z "$TARBALL" ]; then + info "❌ Failed to pack SDK." + exit 1 +fi +TARBALL_PATH="$(pwd)/$TARBALL" +SDK_TARBALLS+=("$TARBALL_PATH") +vlog " SDK: $TARBALL_PATH" + +mv package.json.bak package.json +cd "$MONOREPO_ROOT" + +info "✅ SDK packed." +info "" + +# -------------------------------------------------------------------------- +# 3. Discover templates + +if [ ! -d "$TEMPLATES_DIR" ]; then + info "❌ Templates directory not found: $TEMPLATES_DIR" + info "Override with: TEMPLATES_DIR=/path/to/cre-templates ./scripts/test-templates.sh" + exit 1 +fi + +TEMPLATES_ABS_PATH="$(cd "$TEMPLATES_DIR" && pwd)" + +# Collect all package.json files that depend on @chainlink/cre-sdk +ALL_PKGS=() +while IFS= read -r pkg; do + grep -q '"@chainlink/cre-sdk"' "$pkg" && ALL_PKGS+=("$pkg") +done < <(/usr/bin/find "$TEMPLATES_ABS_PATH" -name "package.json" -not -path "*/node_modules/*") + +TOTAL=${#ALL_PKGS[@]} +info "Found $TOTAL TypeScript templates to test." +info "" + +# -------------------------------------------------------------------------- +# 4. Test each template + +FAILED_TEMPLATES=() +PASSED_TEMPLATES=() +# Parallel arrays: failure reason and captured output for each failed template +FAILURE_STEPS=() +FAILURE_OUTPUTS=() + +IDX=0 +for pkg in "${ALL_PKGS[@]}"; do + IDX=$((IDX + 1)) + WORKFLOW_DIR=$(dirname "$pkg") + WORKFLOW_DIR_NAME=$(basename "$WORKFLOW_DIR") + PROJECT_NAME=$(basename "$(dirname "$WORKFLOW_DIR")") + DISPLAY_NAME="$PROJECT_NAME/$WORKFLOW_DIR_NAME" + PREFIX="[$IDX/$TOTAL]" + + vlog "--------------------------------------------------------" + vlog "$PREFIX $DISPLAY_NAME" + vlog "--------------------------------------------------------" + + cd "$WORKFLOW_DIR" + + # Track lock files for restoration + for lockfile in package-lock.json bun.lock; do + if [ -f "$lockfile" ]; then + cp "$lockfile" "$lockfile.bak" + LOCKFILE_BACKUPS+=("$WORKFLOW_DIR/$lockfile.bak:$WORKFLOW_DIR/$lockfile") + else + GENERATED_FILES+=("$WORKFLOW_DIR/$lockfile") + fi + done + + # Install dependencies + vlog " Installing dependencies..." + _out="" + if ! run_captured _out npm install --no-audit --fund=false; then + info " ❌ $PREFIX $DISPLAY_NAME" + FAILED_TEMPLATES+=("$DISPLAY_NAME") + FAILURE_STEPS+=("npm install") + FAILURE_OUTPUTS+=("$_out") + continue + fi + + # Inject local SDK tarball + vlog " Installing local SDK..." + if ! run_captured _out npm install --no-save --no-audit --fund=false "$TARBALL_PATH"; then + info " ❌ $PREFIX $DISPLAY_NAME" + FAILED_TEMPLATES+=("$DISPLAY_NAME") + FAILURE_STEPS+=("sdk install") + FAILURE_OUTPUTS+=("$_out") + continue + fi + + FAILED_THIS=false + + # Typecheck + if grep -q '"typecheck"' package.json; then + vlog " Running typecheck..." + if ! run_captured _out npm run typecheck --silent; then + info " ❌ $PREFIX $DISPLAY_NAME" + FAILED_TEMPLATES+=("$DISPLAY_NAME") + FAILURE_STEPS+=("typecheck") + FAILURE_OUTPUTS+=("$_out") + FAILED_THIS=true + else + vlog " ✅ typecheck passed" + fi + else + vlog " ⚠️ No typecheck script, skipping" + fi + + if $FAILED_THIS; then continue; fi + + # Compile to WASM + if [ -f "main.ts" ]; then + GENERATED_FILES+=("$WORKFLOW_DIR/main.js" "$WORKFLOW_DIR/main.wasm") + vlog " Running cre-compile..." + if ! run_captured _out npx --no cre-compile main.ts; then + info " ❌ $PREFIX $DISPLAY_NAME" + FAILED_TEMPLATES+=("$DISPLAY_NAME") + FAILURE_STEPS+=("cre-compile") + FAILURE_OUTPUTS+=("$_out") + continue + fi + vlog " ✅ compile passed" + info " ✅ $PREFIX $DISPLAY_NAME" + PASSED_TEMPLATES+=("$DISPLAY_NAME") + else + vlog " ⚠️ No main.ts, skipping compile" + info " ✅ $PREFIX $DISPLAY_NAME (typecheck only)" + PASSED_TEMPLATES+=("$DISPLAY_NAME (typecheck only)") + fi +done + +# -------------------------------------------------------------------------- +# 5. Summary + +PASS_COUNT=${#PASSED_TEMPLATES[@]} +FAIL_COUNT=${#FAILED_TEMPLATES[@]} + +info "" +info "========================================================" +info "Results: $PASS_COUNT passed, $FAIL_COUNT failed" +info "========================================================" + +if [ $FAIL_COUNT -gt 0 ]; then + info "" + info "Failed templates:" + for t in "${FAILED_TEMPLATES[@]}"; do + info " ❌ $t" + done + + info "" + info "========================================================" + info "Failure Details" + info "========================================================" + + for i in "${!FAILED_TEMPLATES[@]}"; do + info "" + info "❌ ${FAILED_TEMPLATES[$i]} — ${FAILURE_STEPS[$i]}" + info "--------" + # Print the captured output, stripping npm warn noise to highlight real errors + echo "${FAILURE_OUTPUTS[$i]}" | grep -v "^npm warn" | grep -v "^$" || true + done + + exit 1 +else + info "" + info "All templates passed!" + exit 0 +fi From 7d13f82a842117c66ebe636c2ef36dfc4d6bcc69 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Thu, 30 Apr 2026 09:15:26 -0400 Subject: [PATCH 2/8] Removed debugging workflow_dispatch --- .github/workflows/template-compatibility-comment.yml | 4 ---- 1 file changed, 4 deletions(-) diff --git a/.github/workflows/template-compatibility-comment.yml b/.github/workflows/template-compatibility-comment.yml index 75d5daa3..ef3ceedb 100644 --- a/.github/workflows/template-compatibility-comment.yml +++ b/.github/workflows/template-compatibility-comment.yml @@ -13,10 +13,6 @@ on: workflow_run: workflows: ["Template Compatibility Check"] types: [completed] - workflow_dispatch: # temporary — remove before merging - inputs: - run_id: - required: true permissions: pull-requests: write From 0cf98775832e1e6d4d89933df35d13f0ab4f7099 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 4 May 2026 10:33:20 -0400 Subject: [PATCH 3/8] Updated with PR comments --- .../template-compatibility-comment.yml | 106 ++---------------- .github/workflows/template-compatibility.yml | 5 + scripts/template-compatibility-comment.js | 100 +++++++++++++++++ scripts/test-templates.sh | 46 +++++--- 4 files changed, 150 insertions(+), 107 deletions(-) create mode 100644 scripts/template-compatibility-comment.js diff --git a/.github/workflows/template-compatibility-comment.yml b/.github/workflows/template-compatibility-comment.yml index ef3ceedb..2ba864d8 100644 --- a/.github/workflows/template-compatibility-comment.yml +++ b/.github/workflows/template-compatibility-comment.yml @@ -1,13 +1,13 @@ name: Template Compatibility Comment # Triggered when the unprivileged "Template Compatibility Check" workflow -# completes. This workflow has pull-requests: write so it can post PR comments, -# but it never checks out or executes any external code — it only reads the -# artifact produced by the check workflow. +# completes. This workflow has pull-requests: write so it can post PR comments. +# It checks out the default branch (only) to load scripts/template-compatibility-comment.js, +# then reads the artifact produced by the check workflow. # # SECURITY: workflow_run always runs on the default branch, so this workflow -# definition itself cannot be tampered with by a PR contributor. Artifact -# contents are treated as untrusted strings and sanitized before use. +# definition and checked-out script cannot be tampered with by a PR contributor. +# Artifact contents are treated as untrusted strings and sanitized before use. on: workflow_run: @@ -15,6 +15,7 @@ on: types: [completed] permissions: + contents: read pull-requests: write actions: read # required to download artifacts from another workflow run @@ -23,6 +24,9 @@ jobs: runs-on: ubuntu-latest steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + - name: Download results artifact uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 with: @@ -35,92 +39,6 @@ jobs: uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 with: script: | - const fs = require('fs'); - - const read = (filename) => { - try { return fs.readFileSync(`/tmp/compat-results/${filename}`, 'utf8').trim(); } - catch { return ''; } - }; - - // Validate PR number — must be a positive integer. - const prNumber = parseInt(read('pr-number.txt'), 10); - if (!Number.isInteger(prNumber) || prNumber <= 0) { - console.log('Invalid or missing PR number in artifact; skipping comment.'); - return; - } - - const exitCode = read('exit-code.txt'); - const fullOutput = read('output.txt'); - // Sanitize values read from the artifact before embedding in markdown - // to prevent injection (e.g. a malicious branch name or script output - // containing markdown syntax that escapes a code fence). - const templatesRef = read('templates-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, ''); - const headRef = read('head-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, ''); - - const marker = ''; - - const { data: comments } = await github.rest.issues.listComments({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - }); - const existing = comments.find(c => c.body.includes(marker)); - - if (exitCode === '0') { - // Templates pass — remove any stale failure comment. - if (existing) { - await github.rest.issues.deleteComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - }); - } - return; - } - - // Extract just the "Results" and "Failure Details" sections from output. - const resultsMatch = fullOutput.match(/={8,}\nResults:.*\n={8,}[\s\S]*/); - const failureSummary = resultsMatch ? resultsMatch[0].trim() : fullOutput.trim(); - - const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.payload.workflow_run.id}`; - const refNote = templatesRef === 'main' - ? 'tested against `cre-templates:main`' - : `tested against \`cre-templates:${templatesRef}\` (compat branch)`; - - const body = [ - '## ⚠️ Template Compatibility Failures', - '', - `This PR breaks one or more templates in [cre-templates](https://github.com/smartcontractkit/cre-templates) (${refNote}).`, - '', - '```', - failureSummary, - '```', - '', - `[View full output →](${runUrl})`, - '', - '
', - 'What should I do?', - '', - '- **Accidental break:** Fix the SDK change so existing templates continue to compile.', - `- **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.`, - '', - '
', - ].join('\n'); - - const commentBody = `${marker}\n${body}`; - - if (existing) { - await github.rest.issues.updateComment({ - owner: context.repo.owner, - repo: context.repo.repo, - comment_id: existing.id, - body: commentBody, - }); - } else { - await github.rest.issues.createComment({ - owner: context.repo.owner, - repo: context.repo.repo, - issue_number: prNumber, - body: commentBody, - }); - } + const path = require('path'); + const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.js')); + await run({ github, context }); diff --git a/.github/workflows/template-compatibility.yml b/.github/workflows/template-compatibility.yml index 0e5be1a2..ea60b7a3 100644 --- a/.github/workflows/template-compatibility.yml +++ b/.github/workflows/template-compatibility.yml @@ -4,6 +4,10 @@ on: pull_request: types: [opened, synchronize, reopened] +concurrency: + group: template-compat-${{ github.head_ref || github.ref }} + cancel-in-progress: true + # This job is informational — it is intentionally NOT a required status check # so that breaking SDK changes (major version bumps) can still be merged. # When templates break, a comment is posted on the PR with details. @@ -17,6 +21,7 @@ permissions: jobs: template-compatibility: runs-on: ubuntu-latest + timeout-minutes: 20 defaults: run: diff --git a/scripts/template-compatibility-comment.js b/scripts/template-compatibility-comment.js new file mode 100644 index 00000000..428c72db --- /dev/null +++ b/scripts/template-compatibility-comment.js @@ -0,0 +1,100 @@ +/** + * Used by .github/workflows/template-compatibility-comment.yml. + * Reads /tmp/compat-results from the prior workflow's artifact and posts or + * removes a PR comment via the GitHub API. + */ +module.exports = async ({ github, context }) => { + const fs = require('fs'); + + const read = (filename) => { + try { + return fs.readFileSync(`/tmp/compat-results/${filename}`, 'utf8').trim(); + } catch { + return ''; + } + }; + + // Validate PR number — must be a positive integer. + const prNumber = parseInt(read('pr-number.txt'), 10); + if (!Number.isInteger(prNumber) || prNumber <= 0) { + console.log('Invalid or missing PR number in artifact; skipping comment.'); + return; + } + + const exitCode = read('exit-code.txt'); + const fullOutput = read('output.txt'); + // Sanitize values read from the artifact before embedding in markdown + // to prevent injection (e.g. a malicious branch name or script output + // containing markdown syntax that escapes a code fence). + const templatesRef = read('templates-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, ''); + const headRef = read('head-ref.txt').replace(/[^a-zA-Z0-9._\/-]/g, ''); + + const marker = ''; + + const { data: comments } = await github.rest.issues.listComments({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + }); + const existing = comments.find((c) => c.body.includes(marker)); + + if (exitCode === '0') { + // Templates pass — remove any stale failure comment. + if (existing) { + await github.rest.issues.deleteComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + }); + } + return; + } + + // Extract just the "Results" and "Failure Details" sections from output. + const resultsMatch = fullOutput.match(/={8,}\nResults:.*\n={8,}[\s\S]*/); + const failureSummary = resultsMatch ? resultsMatch[0].trim() : fullOutput.trim(); + + const runUrl = `${context.serverUrl}/${context.repo.owner}/${context.repo.repo}/actions/runs/${context.payload.workflow_run.id}`; + const refNote = + templatesRef === 'main' + ? 'tested against `cre-templates:main`' + : `tested against \`cre-templates:${templatesRef}\` (compat branch)`; + + const body = [ + '## ⚠️ Template Compatibility Failures', + '', + `This PR breaks one or more templates in [cre-templates](https://github.com/smartcontractkit/cre-templates) (${refNote}).`, + '', + '```', + failureSummary, + '```', + '', + `[View full output →](${runUrl})`, + '', + '
', + 'What should I do?', + '', + '- **Accidental break:** Fix the SDK change so existing templates continue to compile.', + `- **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.`, + '', + '
', + ].join('\n'); + + const commentBody = `${marker}\n${body}`; + + if (existing) { + await github.rest.issues.updateComment({ + owner: context.repo.owner, + repo: context.repo.repo, + comment_id: existing.id, + body: commentBody, + }); + } else { + await github.rest.issues.createComment({ + owner: context.repo.owner, + repo: context.repo.repo, + issue_number: prNumber, + body: commentBody, + }); + } +}; diff --git a/scripts/test-templates.sh b/scripts/test-templates.sh index 48613165..26f91fc3 100755 --- a/scripts/test-templates.sh +++ b/scripts/test-templates.sh @@ -140,24 +140,44 @@ info "" info "Packing SDK..." cd packages/cre-sdk-javy-plugin -JAVY_TARBALL=$(npm pack --quiet 2>/dev/null) -if [ -z "$JAVY_TARBALL" ]; then +_pack_log=$(mktemp) +if ! bun pm pack --quiet >"$_pack_log" 2>&1; then info "❌ Failed to pack Javy plugin." + info "$(cat "$_pack_log")" + rm -f "$_pack_log" exit 1 fi +JAVY_TARBALL=$(grep -oE '[^[:space:]]+\.tgz' "$_pack_log" | tail -n1 | tr -d '\r\n') +if [ -z "$JAVY_TARBALL" ] || [ ! -f "$(pwd)/$JAVY_TARBALL" ]; then + info "❌ Failed to pack Javy plugin (no .tgz produced or name not found)." + info "$(cat "$_pack_log")" + rm -f "$_pack_log" + exit 1 +fi +rm -f "$_pack_log" JAVY_TARBALL_PATH="$(pwd)/$JAVY_TARBALL" SDK_TARBALLS+=("$JAVY_TARBALL_PATH") vlog " Javy plugin: $JAVY_TARBALL_PATH" cd ../cre-sdk cp package.json package.json.bak -npm pkg set dependencies."@chainlink/cre-sdk-javy-plugin"="file:$JAVY_TARBALL_PATH" +bun pm pkg set "dependencies.@chainlink/cre-sdk-javy-plugin=file:$JAVY_TARBALL_PATH" -TARBALL=$(npm pack --quiet 2>/dev/null) -if [ -z "$TARBALL" ]; then +_pack_log=$(mktemp) +if ! bun pm pack --quiet >"$_pack_log" 2>&1; then info "❌ Failed to pack SDK." + info "$(cat "$_pack_log")" + rm -f "$_pack_log" + exit 1 +fi +TARBALL=$(grep -oE '[^[:space:]]+\.tgz' "$_pack_log" | tail -n1 | tr -d '\r\n') +if [ -z "$TARBALL" ] || [ ! -f "$(pwd)/$TARBALL" ]; then + info "❌ Failed to pack SDK (no .tgz produced or name not found)." + info "$(cat "$_pack_log")" + rm -f "$_pack_log" exit 1 fi +rm -f "$_pack_log" TARBALL_PATH="$(pwd)/$TARBALL" SDK_TARBALLS+=("$TARBALL_PATH") vlog " SDK: $TARBALL_PATH" @@ -173,7 +193,7 @@ info "" if [ ! -d "$TEMPLATES_DIR" ]; then info "❌ Templates directory not found: $TEMPLATES_DIR" - info "Override with: TEMPLATES_DIR=/path/to/cre-templates ./scripts/test-templates.sh" + info "Override with: TEMPLATES_DIR=/path/to/cre-templates bun run test:templates" exit 1 fi @@ -226,17 +246,17 @@ for pkg in "${ALL_PKGS[@]}"; do # Install dependencies vlog " Installing dependencies..." _out="" - if ! run_captured _out npm install --no-audit --fund=false; then + if ! run_captured _out bun install; then info " ❌ $PREFIX $DISPLAY_NAME" FAILED_TEMPLATES+=("$DISPLAY_NAME") - FAILURE_STEPS+=("npm install") + FAILURE_STEPS+=("bun install") FAILURE_OUTPUTS+=("$_out") continue fi # Inject local SDK tarball vlog " Installing local SDK..." - if ! run_captured _out npm install --no-save --no-audit --fund=false "$TARBALL_PATH"; then + if ! run_captured _out bun install --no-save "@chainlink/cre-sdk@file:$TARBALL_PATH"; then info " ❌ $PREFIX $DISPLAY_NAME" FAILED_TEMPLATES+=("$DISPLAY_NAME") FAILURE_STEPS+=("sdk install") @@ -249,7 +269,7 @@ for pkg in "${ALL_PKGS[@]}"; do # Typecheck if grep -q '"typecheck"' package.json; then vlog " Running typecheck..." - if ! run_captured _out npm run typecheck --silent; then + if ! run_captured _out bun run typecheck; then info " ❌ $PREFIX $DISPLAY_NAME" FAILED_TEMPLATES+=("$DISPLAY_NAME") FAILURE_STEPS+=("typecheck") @@ -268,7 +288,7 @@ for pkg in "${ALL_PKGS[@]}"; do if [ -f "main.ts" ]; then GENERATED_FILES+=("$WORKFLOW_DIR/main.js" "$WORKFLOW_DIR/main.wasm") vlog " Running cre-compile..." - if ! run_captured _out npx --no cre-compile main.ts; then + if ! run_captured _out bunx cre-compile main.ts; then info " ❌ $PREFIX $DISPLAY_NAME" FAILED_TEMPLATES+=("$DISPLAY_NAME") FAILURE_STEPS+=("cre-compile") @@ -312,8 +332,8 @@ if [ $FAIL_COUNT -gt 0 ]; then info "" info "❌ ${FAILED_TEMPLATES[$i]} — ${FAILURE_STEPS[$i]}" info "--------" - # Print the captured output, stripping npm warn noise to highlight real errors - echo "${FAILURE_OUTPUTS[$i]}" | grep -v "^npm warn" | grep -v "^$" || true + # Print the captured output, stripping common PM noise to highlight real errors + echo "${FAILURE_OUTPUTS[$i]}" | grep -v "^npm warn" | grep -v "^bun install v" | grep -v "^$" || true done exit 1 From aa4078ca90d2ce6316f743b82b74ace66f31a95d Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 4 May 2026 11:11:26 -0400 Subject: [PATCH 4/8] Added debugging script --- .../template-compatibility-comment-debug.yml | 142 ++++++++++++++++++ 1 file changed, 142 insertions(+) create mode 100644 .github/workflows/template-compatibility-comment-debug.yml diff --git a/.github/workflows/template-compatibility-comment-debug.yml b/.github/workflows/template-compatibility-comment-debug.yml new file mode 100644 index 00000000..0fa860e6 --- /dev/null +++ b/.github/workflows/template-compatibility-comment-debug.yml @@ -0,0 +1,142 @@ +# Maintainer debug workflow: runs the same comment script as +# template-compatibility-comment.yml, but from workflow_dispatch so you can +# test from a feature branch (workflow file + script come from the branch you select). +# +# SECURITY: Anyone who can run this workflow can post/delete comments on the PR +# number you supply (same as the production comment job). Restrict repo/workflow +# permissions accordingly. +# +# Two modes: +# 1) Leave "source_run_id" empty — writes synthetic /tmp/compat-results from inputs. +# 2) Set "source_run_id" — downloads the template-compat-results artifact from that +# run (same repository), same layout as Template Compatibility Check uploads. + +name: Template Compatibility Comment (debug) + +on: + workflow_dispatch: + inputs: + pr_number: + description: 'Required when source_run_id is empty. Ignored when downloading an artifact (pr-number.txt comes from the artifact).' + required: false + type: string + source_run_id: + description: 'Optional. Workflow run ID to download template-compat-results from. Leave empty to use synthetic inputs below.' + required: false + type: string + exit_code: + description: 'Synthetic only. 0 = success (delete stale comment); 1 = failure (post/update comment).' + required: true + type: choice + default: '1' + options: + - '0' + - '1' + templates_ref: + description: 'Synthetic only. Value for templates-ref.txt (e.g. main or compat/your-branch).' + required: true + type: string + default: 'main' + head_ref: + description: 'Synthetic only. Value for head-ref.txt (SDK branch name in compat instructions).' + required: true + type: string + default: 'workflow-debug' + link_run_id: + description: 'Optional. Actions run id for the "View full output" link. Defaults to this debug run.' + required: false + type: string + +permissions: + contents: read + pull-requests: write + actions: read + +jobs: + debug-comment: + runs-on: ubuntu-latest + steps: + - name: Checkout repository + uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + + - name: Validate inputs + run: | + if [[ -z '${{ inputs.source_run_id }}' && -z '${{ inputs.pr_number }}' ]]; then + echo '::error::pr_number is required when source_run_id is empty (synthetic mode).' + exit 1 + fi + + - name: Download compat results artifact + if: ${{ inputs.source_run_id != '' }} + uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + with: + name: template-compat-results + path: /tmp/compat-results + run-id: ${{ inputs.source_run_id }} + github-token: ${{ secrets.GITHUB_TOKEN }} + + - name: Write synthetic compat results + if: ${{ inputs.source_run_id == '' }} + run: | + mkdir -p /tmp/compat-results + printf '%s' '${{ inputs.pr_number }}' > /tmp/compat-results/pr-number.txt + printf '%s' '${{ inputs.exit_code }}' > /tmp/compat-results/exit-code.txt + printf '%s' '${{ inputs.templates_ref }}' > /tmp/compat-results/templates-ref.txt + printf '%s' '${{ inputs.head_ref }}' > /tmp/compat-results/head-ref.txt + python3 << 'PY' + import pathlib + + exit_code = "${{ inputs.exit_code }}" + out_pass = ( + "Building SDK...\n" + "✅ SDK built.\n\n" + "Found 2 TypeScript templates to test.\n\n" + " ✅ [1/2] example-a/workflow\n" + " ✅ [2/2] example-b/workflow\n\n" + "========================================================\n" + "Results: 2 passed, 0 failed\n" + "========================================================\n\n" + "All templates passed!\n" + ) + out_fail = ( + "Building SDK...\n" + "✅ SDK built.\n\n" + "Found 2 TypeScript templates to test.\n\n" + " ❌ [1/2] example-a/workflow\n" + " ✅ [2/2] example-b/workflow\n\n" + "========================================================\n" + "Results: 1 passed, 1 failed\n" + "========================================================\n\n" + "Failed templates:\n" + " ❌ example-a/workflow\n\n" + "========================================================\n" + "Failure Details\n" + "========================================================\n\n" + "❌ example-a/workflow — typecheck\n" + "--------\n" + "error: synthetic debug failure (template-compatibility-comment-debug.yml)\n" + ) + pathlib.Path("/tmp/compat-results/output.txt").write_text( + out_pass if exit_code == "0" else out_fail + ) + PY + + - name: Post or remove PR comment (debug) + uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + env: + LINK_RUN_ID: ${{ inputs.link_run_id }} + with: + script: | + const path = require('path'); + const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.js')); + const linkRaw = (process.env.LINK_RUN_ID || '').trim(); + const linkParsed = parseInt(linkRaw, 10); + const linkId = Number.isFinite(linkParsed) && linkParsed > 0 ? linkParsed : context.runId; + const rawServer = process.env.GITHUB_SERVER_URL || 'https://github.com'; + const serverUrl = String(rawServer).replace(/\/$/, ''); + const ctx = { + ...context, + serverUrl: context.serverUrl || serverUrl, + payload: { ...context.payload, workflow_run: { id: linkId } }, + }; + await run({ github, context: ctx }); From 5f31231d487d42174673a50c122d3c2a0b1d34ce Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 4 May 2026 11:17:49 -0400 Subject: [PATCH 5/8] Remvoed debug script --- .../template-compatibility-comment-debug.yml | 142 ------------------ 1 file changed, 142 deletions(-) delete mode 100644 .github/workflows/template-compatibility-comment-debug.yml diff --git a/.github/workflows/template-compatibility-comment-debug.yml b/.github/workflows/template-compatibility-comment-debug.yml deleted file mode 100644 index 0fa860e6..00000000 --- a/.github/workflows/template-compatibility-comment-debug.yml +++ /dev/null @@ -1,142 +0,0 @@ -# Maintainer debug workflow: runs the same comment script as -# template-compatibility-comment.yml, but from workflow_dispatch so you can -# test from a feature branch (workflow file + script come from the branch you select). -# -# SECURITY: Anyone who can run this workflow can post/delete comments on the PR -# number you supply (same as the production comment job). Restrict repo/workflow -# permissions accordingly. -# -# Two modes: -# 1) Leave "source_run_id" empty — writes synthetic /tmp/compat-results from inputs. -# 2) Set "source_run_id" — downloads the template-compat-results artifact from that -# run (same repository), same layout as Template Compatibility Check uploads. - -name: Template Compatibility Comment (debug) - -on: - workflow_dispatch: - inputs: - pr_number: - description: 'Required when source_run_id is empty. Ignored when downloading an artifact (pr-number.txt comes from the artifact).' - required: false - type: string - source_run_id: - description: 'Optional. Workflow run ID to download template-compat-results from. Leave empty to use synthetic inputs below.' - required: false - type: string - exit_code: - description: 'Synthetic only. 0 = success (delete stale comment); 1 = failure (post/update comment).' - required: true - type: choice - default: '1' - options: - - '0' - - '1' - templates_ref: - description: 'Synthetic only. Value for templates-ref.txt (e.g. main or compat/your-branch).' - required: true - type: string - default: 'main' - head_ref: - description: 'Synthetic only. Value for head-ref.txt (SDK branch name in compat instructions).' - required: true - type: string - default: 'workflow-debug' - link_run_id: - description: 'Optional. Actions run id for the "View full output" link. Defaults to this debug run.' - required: false - type: string - -permissions: - contents: read - pull-requests: write - actions: read - -jobs: - debug-comment: - runs-on: ubuntu-latest - steps: - - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 - - - name: Validate inputs - run: | - if [[ -z '${{ inputs.source_run_id }}' && -z '${{ inputs.pr_number }}' ]]; then - echo '::error::pr_number is required when source_run_id is empty (synthetic mode).' - exit 1 - fi - - - name: Download compat results artifact - if: ${{ inputs.source_run_id != '' }} - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 - with: - name: template-compat-results - path: /tmp/compat-results - run-id: ${{ inputs.source_run_id }} - github-token: ${{ secrets.GITHUB_TOKEN }} - - - name: Write synthetic compat results - if: ${{ inputs.source_run_id == '' }} - run: | - mkdir -p /tmp/compat-results - printf '%s' '${{ inputs.pr_number }}' > /tmp/compat-results/pr-number.txt - printf '%s' '${{ inputs.exit_code }}' > /tmp/compat-results/exit-code.txt - printf '%s' '${{ inputs.templates_ref }}' > /tmp/compat-results/templates-ref.txt - printf '%s' '${{ inputs.head_ref }}' > /tmp/compat-results/head-ref.txt - python3 << 'PY' - import pathlib - - exit_code = "${{ inputs.exit_code }}" - out_pass = ( - "Building SDK...\n" - "✅ SDK built.\n\n" - "Found 2 TypeScript templates to test.\n\n" - " ✅ [1/2] example-a/workflow\n" - " ✅ [2/2] example-b/workflow\n\n" - "========================================================\n" - "Results: 2 passed, 0 failed\n" - "========================================================\n\n" - "All templates passed!\n" - ) - out_fail = ( - "Building SDK...\n" - "✅ SDK built.\n\n" - "Found 2 TypeScript templates to test.\n\n" - " ❌ [1/2] example-a/workflow\n" - " ✅ [2/2] example-b/workflow\n\n" - "========================================================\n" - "Results: 1 passed, 1 failed\n" - "========================================================\n\n" - "Failed templates:\n" - " ❌ example-a/workflow\n\n" - "========================================================\n" - "Failure Details\n" - "========================================================\n\n" - "❌ example-a/workflow — typecheck\n" - "--------\n" - "error: synthetic debug failure (template-compatibility-comment-debug.yml)\n" - ) - pathlib.Path("/tmp/compat-results/output.txt").write_text( - out_pass if exit_code == "0" else out_fail - ) - PY - - - name: Post or remove PR comment (debug) - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 - env: - LINK_RUN_ID: ${{ inputs.link_run_id }} - with: - script: | - const path = require('path'); - const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.js')); - const linkRaw = (process.env.LINK_RUN_ID || '').trim(); - const linkParsed = parseInt(linkRaw, 10); - const linkId = Number.isFinite(linkParsed) && linkParsed > 0 ? linkParsed : context.runId; - const rawServer = process.env.GITHUB_SERVER_URL || 'https://github.com'; - const serverUrl = String(rawServer).replace(/\/$/, ''); - const ctx = { - ...context, - serverUrl: context.serverUrl || serverUrl, - payload: { ...context.payload, workflow_run: { id: linkId } }, - }; - await run({ github, context: ctx }); From 4b2cac140b8dd8c0fa7b716a41856f8faa44662f Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 4 May 2026 11:25:16 -0400 Subject: [PATCH 6/8] Switched to using major tags --- .github/workflows/template-compatibility-comment.yml | 6 +++--- .github/workflows/template-compatibility.yml | 12 ++++++------ 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/.github/workflows/template-compatibility-comment.yml b/.github/workflows/template-compatibility-comment.yml index 2ba864d8..662ff079 100644 --- a/.github/workflows/template-compatibility-comment.yml +++ b/.github/workflows/template-compatibility-comment.yml @@ -25,10 +25,10 @@ jobs: steps: - name: Checkout repository - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6 - name: Download results artifact - uses: actions/download-artifact@d3f86a106a0bac45b974a628896c90dbdf5c8093 # v4.3.0 + uses: actions/download-artifact@v4 with: name: template-compat-results path: /tmp/compat-results @@ -36,7 +36,7 @@ jobs: github-token: ${{ secrets.GITHUB_TOKEN }} - name: Post or remove PR comment - uses: actions/github-script@60a0d83039c74a4aee543508d2ffcb1c3799cdea # v7.0.1 + uses: actions/github-script@v8 with: script: | const path = require('path'); diff --git a/.github/workflows/template-compatibility.yml b/.github/workflows/template-compatibility.yml index ea60b7a3..28af4ceb 100644 --- a/.github/workflows/template-compatibility.yml +++ b/.github/workflows/template-compatibility.yml @@ -32,7 +32,7 @@ jobs: steps: - name: Checkout SDK - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6 with: submodules: recursive @@ -49,13 +49,13 @@ jobs: bun-version: 1.3.12 - name: Cache Bun dependencies - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@v5 with: path: ~/.bun/install/cache key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} - name: Cache cargo + Javy - uses: actions/cache@668228422ae6a00e4ad889ee87cd7109ec5666a7 # v5.0.4 + uses: actions/cache@v5 with: path: | ~/.cargo/registry @@ -87,7 +87,7 @@ jobs: fi - name: Checkout cre-templates (${{ steps.detect-ref.outputs.ref }}) - uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2 + uses: actions/checkout@v6 with: repository: ${{ env.TEMPLATES_REPO }} ref: ${{ steps.detect-ref.outputs.ref }} @@ -95,7 +95,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node (for npm) - uses: actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020 # v4.4.0 + uses: actions/setup-node@v4 with: node-version: '22' @@ -138,7 +138,7 @@ jobs: - name: Upload results artifact if: always() - uses: actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02 # v4.6.2 + uses: actions/upload-artifact@v4 with: name: template-compat-results path: /tmp/compat-results/ From b707964d51d217a58cd43d8660aa84a011f3a137 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 4 May 2026 11:36:23 -0400 Subject: [PATCH 7/8] Switched to node version 24 --- .github/workflows/template-compatibility.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/template-compatibility.yml b/.github/workflows/template-compatibility.yml index 28af4ceb..dd1b6d19 100644 --- a/.github/workflows/template-compatibility.yml +++ b/.github/workflows/template-compatibility.yml @@ -97,7 +97,7 @@ jobs: - name: Setup Node (for npm) uses: actions/setup-node@v4 with: - node-version: '22' + node-version: '24' - name: Run template compatibility check id: template-check From 316eac1f52383123a43010b01dac3f0d97d68c03 Mon Sep 17 00:00:00 2001 From: Russell Stern Date: Mon, 4 May 2026 12:25:18 -0400 Subject: [PATCH 8/8] updated action versions --- .github/workflows/template-compatibility-comment.yml | 2 +- .github/workflows/template-compatibility.yml | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/template-compatibility-comment.yml b/.github/workflows/template-compatibility-comment.yml index 662ff079..1f66df7c 100644 --- a/.github/workflows/template-compatibility-comment.yml +++ b/.github/workflows/template-compatibility-comment.yml @@ -28,7 +28,7 @@ jobs: uses: actions/checkout@v6 - name: Download results artifact - uses: actions/download-artifact@v4 + uses: actions/download-artifact@v8 with: name: template-compat-results path: /tmp/compat-results diff --git a/.github/workflows/template-compatibility.yml b/.github/workflows/template-compatibility.yml index dd1b6d19..95b41b76 100644 --- a/.github/workflows/template-compatibility.yml +++ b/.github/workflows/template-compatibility.yml @@ -95,7 +95,7 @@ jobs: token: ${{ secrets.GITHUB_TOKEN }} - name: Setup Node (for npm) - uses: actions/setup-node@v4 + uses: actions/setup-node@v6 with: node-version: '24' @@ -138,7 +138,7 @@ jobs: - name: Upload results artifact if: always() - uses: actions/upload-artifact@v4 + uses: actions/upload-artifact@v7 with: name: template-compat-results path: /tmp/compat-results/