-
Notifications
You must be signed in to change notification settings - Fork 6
Added template tests #249
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Added template tests #249
Changes from all commits
9bbbc7c
7d13f82
0cf9877
aa4078c
5f31231
4b2cac1
b707964
316eac1
a001633
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,44 @@ | ||
| 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. | ||
| # 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 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: | ||
| workflows: ["Template Compatibility Check"] | ||
| types: [completed] | ||
|
|
||
| permissions: | ||
| contents: read | ||
| pull-requests: write | ||
| actions: read # required to download artifacts from another workflow run | ||
|
|
||
| jobs: | ||
| post-comment: | ||
| runs-on: ubuntu-latest | ||
|
|
||
| steps: | ||
| - name: Checkout repository | ||
| uses: actions/checkout@v6 | ||
|
|
||
| - name: Download results artifact | ||
| uses: actions/download-artifact@v8 | ||
| 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@v8 | ||
| with: | ||
| script: | | ||
| const path = require('path'); | ||
| const run = require(path.join(process.env.GITHUB_WORKSPACE, 'scripts', 'template-compatibility-comment.js')); | ||
| await run({ github, context }); | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,158 @@ | ||
| name: Template Compatibility Check | ||
|
|
||
| on: | ||
| pull_request: | ||
| types: [opened, synchronize, reopened] | ||
|
|
||
|
Collaborator
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I would also add As template testing could take some time and upon multiple commits we would have improved efficiency.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Good call |
||
| 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. | ||
| # For intentional breaking changes, create a matching branch in cre-templates | ||
| # named compat/<your-sdk-branch-name> 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 | ||
| timeout-minutes: 20 | ||
|
|
||
| defaults: | ||
| run: | ||
| shell: bash {0} | ||
|
|
||
| env: | ||
| TEMPLATES_REPO: smartcontractkit/cre-templates | ||
|
|
||
| steps: | ||
| - name: Checkout SDK | ||
| uses: actions/checkout@v6 | ||
| 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@v5 | ||
| with: | ||
| path: ~/.bun/install/cache | ||
| key: ${{ runner.os }}-bun-${{ hashFiles('**/bun.lock') }} | ||
|
|
||
| - name: Cache cargo + Javy | ||
| uses: actions/cache@v5 | ||
| 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@v6 | ||
| with: | ||
| repository: ${{ env.TEMPLATES_REPO }} | ||
| ref: ${{ steps.detect-ref.outputs.ref }} | ||
| path: cre-templates | ||
| token: ${{ secrets.GITHUB_TOKEN }} | ||
|
|
||
| - name: Setup Node (for npm) | ||
Check warningCode scanning / CodeQL Checkout of untrusted code in trusted context Medium
Potential unsafe checkout of untrusted pull request on privileged workflow.
|
||
|
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment on lines
+89
to
+97
|
||
| uses: actions/setup-node@v6 | ||
| with: | ||
| node-version: '24' | ||
|
|
||
| - 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@v7 | ||
| 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 | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -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 = '<!-- template-compat-comment -->'; | ||
|
|
||
| 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})`, | ||
| '', | ||
| '<details>', | ||
| '<summary>What should I do?</summary>', | ||
| '', | ||
| '- **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.`, | ||
| '', | ||
| '</details>', | ||
| ].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, | ||
| }); | ||
| } | ||
| }; |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I feel it would be cleaner and easier to maintain if we put script in file together with other scripts we had and just call it here in 1-2 lines.