-
Notifications
You must be signed in to change notification settings - Fork 4
NONEVM-3123: Add staging contract compatibility testing #403
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
base: main
Are you sure you want to change the base?
Conversation
| - name: Checkout Chainlink Core repo | ||
| if: steps.core-cache.outputs.cache-hit != 'true' | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| repository: smartcontractkit/chainlink | ||
| ref: ${{ steps.read_core_ref.outputs.CORE_REF }} | ||
| path: chainlink | ||
|
|
||
| - name: Build contracts |
Check warning
Code scanning / CodeQL
Checkout of untrusted code in trusted context Medium
Show autofix suggestion
Hide autofix suggestion
Copilot Autofix
AI 21 days ago
The best way to fix this problem is to avoid checking out arbitrary, potentially untrusted commits from the Chainlink core repo in a privileged context. Ideally, workflows that deal with pull requests or other untrusted input should not check out untrusted code and run it with access to secrets or write permissions.
Instead, split the workflow into two stages:
- Stage 1 (unprivileged): In response to pull requests or untrusted events, build and test the code as needed, collecting artifacts/results but not checking out arbitrary SHAs in a context with secrets.
- Stage 2 (privileged): Triggered by
workflow_runafter Stage 1 completes successfully, download and verify the results/artifacts, and interact with secrets or make privileged changes only after verification.
For the file .github/actions/ccip-e2e-setup/action.yml, you should:
- Remove or restrict the step that checks out code at an arbitrary SHA from a potentially untrusted source.
- If build/test of untrusted code is absolutely necessary, ensure this workflow runs without repository secrets and with minimal permissions, and never stores results that are trusted without verification.
- If privileged operations are needed, move them to a separate workflow.
In summary:
- Remove the checkout step from the privileged workflow (the block starting with
- name: Checkout Chainlink Core repo). - If checking out is still required, ensure only trusted refs (e.g., protected branch names, tags) may be used, or perform the checkout in an isolated and unprivileged context.
-
Copy modified lines R74-R85
| @@ -71,15 +71,18 @@ | ||
| path: chainlink | ||
| key: core-${{ steps.read_core_ref.outputs.CORE_REF }} | ||
|
|
||
| - name: Checkout Chainlink Core repo | ||
| if: steps.core-cache.outputs.cache-hit != 'true' | ||
| uses: actions/checkout@v5 | ||
| with: | ||
| repository: smartcontractkit/chainlink | ||
| ref: ${{ steps.read_core_ref.outputs.CORE_REF }} | ||
| path: chainlink | ||
| persist-credentials: false | ||
|
|
||
| # [REMOVED]: Unsafe checkout of arbitrary Chainlink Core repo commit. | ||
| # To safely use the Chainlink Core repo, move this step into an unprivileged workflow triggered by pull_request, | ||
| # or check out only trusted refs (e.g., main/master) here, never using a SHA from potentially untrusted input. | ||
| # For example, if only trusted branch names are allowed: | ||
| # - name: Safe checkout of Chainlink Core repo | ||
| # if: steps.core-cache.outputs.cache-hit != 'true' | ||
| # uses: actions/checkout@v5 | ||
| # with: | ||
| # repository: smartcontractkit/chainlink | ||
| # ref: main # or another trusted branch or tag | ||
| # path: chainlink | ||
| # persist-credentials: false | ||
| - name: Build contracts | ||
| if: inputs.build_contracts == 'true' | ||
| shell: bash |
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.
This seems to indicate this part of the workflow must be run in a separate unprivileged one?
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.
@patricios-space Thanks for taking a look. core ref is read from a version controlled file we maintain and it's deterministic(not from untrusted PR input). Also we are not using pull_request_target, which can do more than just pull_request(like accessing repo secret) there's no privileged context to exploit.
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.
Pull request overview
This PR introduces automated staging contract compatibility testing to detect breaking changes before they reach the staging environment. The implementation adds a new CI workflow that validates plugin compatibility with staging-deployed contract versions, providing early visibility into compatibility issues.
Key Changes:
- New staging compatibility test workflow that runs in parallel with existing integration tests
- Centralized test definition file to maintain consistency across both workflows
- Reusable GitHub Action for common E2E test setup, reducing code duplication
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 3 comments.
Show a summary per file
| File | Description |
|---|---|
.github/workflows/ccip-staging-compat.yml |
New workflow for testing compatibility with staging contract versions |
.github/workflows/ccip-integration-test.yml |
Refactored to use centralized test definitions and reusable setup action |
.github/actions/ccip-e2e-setup/action.yml |
New reusable action consolidating common E2E setup steps |
.github/ccip-ton-tests.yml |
Centralized test definition file used by both workflows |
scripts/.staging_contract_version |
Version pointer for staging-deployed contracts |
scripts/e2e/lib.sh |
Modified to support external contract version configuration |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
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'm not a CI expert by any means, but this warning seems concerning. See https://github.com/smartcontractkit/chainlink-ton/security/code-scanning/56
| @@ -0,0 +1 @@ | |||
| 43d7a93089fe | |||
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.
Let's load this from the CLD state file which should be the source of truth here, right? vs. hardcoding a magic sha
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.
100% agree, was looking for a shortcut 😅
Exploring state update here
| @@ -0,0 +1,16 @@ | |||
| # CCIP TON E2E Test Definitions | |||
| # This file is the single source of truth for CCIP TON integration tests. | |||
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.
Why is the single source truth for our tests/apps in .github CI context?
I would prefer this source of truth to be in the core project, some task manager (nix, makefile, just) to define and run common operations, so everybody can easily trigger, and then the CI uses that.
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.
Hmm I thought this list is mostly for CI. On local development, devs usually run tests they're interested in(mostly single test). But I can see the point where CI would only pick up predefined test organization, not define by itself
| uses: ./.github/actions/ccip-e2e-setup | ||
| with: | ||
| contract_version: 'local' | ||
| build_contracts: 'true' |
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.
What's the point of this build_contracts flag you were thinking of?
I think we need it, as the above action should not build project components, but prepare a CI environment (nix, docker, cache, repo ref) in a reusable way, and then the action on top builds and prepares whatever it needs.
We define what we need in specific environments via Nix, an env like nix develop .#ccip-e2e will prepare/build/provide contracts to the e2e env, and whatever is defined in the Nix shell derivation.
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.
What's the point of this build_contracts flag you were thinking of?
- build_contracts: true → Build latest contracts from this branch
- build_contracts: false → Use released/deployed version
yeah I remember there was a missing piece in e2e Nix shell, contracts flake is already available but we were building them separately. Probably we can use or override it? Let me see if I can iterate on this
| - name: Load test definitions | ||
| id: set-matrix | ||
| run: | | ||
| MATRIX=$(yq -o=json -I=0 '.tests' .github/ccip-ton-tests.yml) |
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.
Think it would be great if immediately extend this to compat test matrix per env - it's gonna useful to run this against prod-testnet, and we can imagine we having more than one staging (sandbox?) envs.
We might have prod-testnet live, and need to patch off-chain, this will be useful to run.
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.
We might have prod-testnet live, and need to patch off-chain, this will be useful to run.
yeah that would be great, I'll see how we can extend it targeting different environment(importing each state file seems promising)
This PR adds CI tests that verify plugin compatibility with staging-deployed contracts, serving as an early warning system for contract deployers and staging owners before breaking changes reach the staging environment.
Problem
When chainlink-ton/main is updated, the plugin gets redeployed to staging. However, contract deployment is manual and separate. If there's a discrepancy between the plugin version and deployed contract version, staging fails silently until someone manually tests it.
Solution
Run E2E tests against both:
When the staging compatibility tests fail, it indicates a breaking change is incoming.
This provides visibility into compatibility issues before they break staging, not after.
Next Steps
The current approach requires manual sync of two version pointers (.staging_contract_version in chainlink-ton and contractsVersion in chainlink-deployments).
Future improvement: Automatically resolve the staging contract version from chainlink-deployments at CI runtime, making chainlink-deployments the single source of truth. This requires additional design work around: