From 5bb65fc7005f770c5fff83831371a71cfac5ba50 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Wed, 15 Jul 2026 20:16:35 +0200 Subject: [PATCH 1/5] Build external with keystore --- .github/workflows/build_external.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/build_external.yml b/.github/workflows/build_external.yml index 8d13b53e52..e24745b885 100644 --- a/.github/workflows/build_external.yml +++ b/.github/workflows/build_external.yml @@ -73,6 +73,7 @@ jobs: working-directory: chainlink run: | go get "github.com/smartcontractkit/chainlink-common@${COMMON_SHA}" + go get "github.com/smartcontractkit/chainlink-common/keystore@${COMMON_SHA}" go mod tidy make install-chainlink From e613b9474fb63f08ad52d7873ec8daed99c8b2df Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 16 Jul 2026 13:07:06 +0200 Subject: [PATCH 2/5] force CI From b29206662a1252d714bf0dce84257f8714c61e76 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 16 Jul 2026 13:47:05 +0200 Subject: [PATCH 3/5] Move get-ref-from-pr to actions repo for reuse --- .github/scripts/get-refs-from-pr-body.js | 215 ----------------------- .github/workflows/build_external.yml | 12 +- 2 files changed, 10 insertions(+), 217 deletions(-) delete mode 100644 .github/scripts/get-refs-from-pr-body.js diff --git a/.github/scripts/get-refs-from-pr-body.js b/.github/scripts/get-refs-from-pr-body.js deleted file mode 100644 index 14b6a4f6f3..0000000000 --- a/.github/scripts/get-refs-from-pr-body.js +++ /dev/null @@ -1,215 +0,0 @@ -/** - * Configuration for repositories and their default refs - * Add new repos here to automatically include them in the ref extraction - */ -const REPO_CONFIG = { - core: { - pattern: /core ref:\s*(\S+)/i, - defaultRef: "develop", - outputKey: "core-ref", - }, - solana: { - pattern: /solana ref:\s*(\S+)/i, - defaultRef: "develop", - outputKey: "solana-ref", - }, - starknet: { - pattern: /starknet ref:\s*(\S+)/i, - defaultRef: "develop", - outputKey: "starknet-ref", - }, - // Add new config here: - // : { - // pattern: / ref:\s*(\S+)/i, - // defaultRef: "develop", - // outputKey: "-ref" - // } -}; - -/** - * Validates a Git reference (branch name, tag, or commit SHA). - * - * These git refs are extracted from PR body text and must be validated for safety. - * - * @param {string} ref - The Git reference to validate - * @returns {string} - The validated reference - * @throws {Error} - If the reference is invalid - */ -function validateGitRef(ref) { - if (!ref) return null; - - // Check length (Git refs can't be longer than 255 chars in practice) - if (ref.length > 255) { - throw new Error(`Git ref too long (${ref.length} chars): ${ref}`); - } - - // Check if the ref is a valid commit SHA - const isCommitSHA = /^[0-9a-f]{4,40}$/i.test(ref); - if (isCommitSHA) { - return ref; - } - - // Check if the ref is a valid SemVer tag - const isSemVer = - /^v?(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)\.(?:0|[1-9]\d*)(?:-(?:[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*))?(?:\+[0-9A-Za-z-]+(?:\.[0-9A-Za-z-]+)*)?$/.test( - ref, - ); - if (isSemVer) { - return ref; - } - - // Finally, check if it's a valid branch/tag name, which is the fallback - const isBranchOrTag = - /^(?!.*\.lock$)(?!.*\.\.)(?!.*\/\/)(?!.*@\{)(?!.*[ ~^:\?\*\[\\])(?!\.)(?!.*\.$)(?!.*\/\.)[A-Za-z0-9][A-Za-z0-9._\/-]*[A-Za-z0-9]$/.test( - ref, - ); - if (isBranchOrTag) { - // We add an extra check here: if it looks like a version but failed the SemVer check, reject it. - if (/^v?\d+\.\d+/.test(ref)) { - throw new Error( - `Invalid SemVer format for version-like tag: ${ref}. It contains leading zeros or other formatting errors.`, - ); - } - return ref; - } - - // If none of the patterns match, it's invalid. - throw new Error( - `Invalid Git reference format: ${ref}. Must be a valid branch name, semver tag, or commit SHA.`, - ); - - // Additional safety checks for dangerous sequences not covered by patterns above - if (ref.includes("\\")) { - throw new Error(`Git reference contains backslashes: ${ref}`); - } - - return ref; -} - -/** - * Extracts Git references from PR body text using the configured patterns - * @param {string} body - The PR body text - * @returns {Object} - Object containing matched references for each repo - */ -function extractRefsFromBody(body) { - const refs = {}; - - for (const [repoName, config] of Object.entries(REPO_CONFIG)) { - const match = body.match(config.pattern); - refs[repoName] = match?.[1]; - } - - return refs; -} - -/** - * Validates and processes a single git ref - * @param {string} refName - Name of the ref type (for error messages) - * @param {string|undefined} refValue - The ref value to validate (may be undefined) - * @param {string} defaultRef - Default value if ref is not provided - * @param {Object} core - GitHub Actions core object - * @returns {string|null} - Validated ref or null if validation failed - */ -function processRef(refName, refValue, defaultRef, core) { - // If no ref was specified in the PR body, use the default - if (!refValue) { - core.info(`No ${refName} ref specified, using default: ${defaultRef}`); - return defaultRef; - } - - try { - const validatedRef = validateGitRef(refValue); - core.info(`Using custom ${refName} ref: ${validatedRef}`); - return validatedRef; - } catch (error) { - core.setFailed(`Invalid ${refName} ref: ${error.message}`); - return null; - } -} - -/** - * Main function for the GitHub Action - * @param {object} { github, context, core } - */ -module.exports = async ({ github, context, core }) => { - try { - const pr = await github.rest.pulls.get({ - owner: context.repo.owner, - repo: context.repo.repo, - pull_number: context.issue.number, - }); - - const body = pr.data.body || ""; - core.info(`PR Body: ${body}`); - - // If the PR body is empty, use defaults for all repos and exit early - if (!body.trim()) { - core.info("PR body is empty. Using default refs for all repositories."); - for (const [, config] of Object.entries(REPO_CONFIG)) { - core.setOutput(config.outputKey, config.defaultRef); - } - core.info("All default refs set. Exiting."); - return; - } - - const extractedRefs = extractRefsFromBody(body); - core.info(`Extracted refs: ${JSON.stringify(extractedRefs)}`); - - // Log what was found in the PR body - const foundRefs = []; - for (const [repoName, refValue] of Object.entries(extractedRefs)) { - if (refValue) { - foundRefs.push(`${repoName}: ${refValue}`); - } - } - - if (foundRefs.length > 0) { - core.info(`Found custom refs in PR body: ${foundRefs.join(", ")}`); - } else { - core.info( - "No custom refs found in PR body, using defaults for all repos", - ); - } - - // Process and validate each ref dynamically - const processedRefs = {}; - let hasValidationError = false; - - for (const [repoName, config] of Object.entries(REPO_CONFIG)) { - const refValue = extractedRefs[repoName]; - const processedRef = processRef( - repoName, - refValue, - config.defaultRef, - core, - ); - - if (processedRef === null) { - hasValidationError = true; - break; - } - - processedRefs[repoName] = processedRef; - } - - // If any ref validation failed, the processRef function will have called core.setFailed - if (hasValidationError) { - return; - } - - // Set outputs dynamically using core.setOutput - for (const [repoName, config] of Object.entries(REPO_CONFIG)) { - core.setOutput(config.outputKey, processedRefs[repoName]); - } - - // Log final refs - const finalRefsList = Object.entries(processedRefs) - .map(([repo, ref]) => `${repo}: ${ref}`) - .join(", "); - core.info(`Final refs - ${finalRefsList}`); - - core.info("All refs processed successfully."); - } catch (error) { - core.setFailed(`Action failed with error: ${error.message}`); - } -}; diff --git a/.github/workflows/build_external.yml b/.github/workflows/build_external.yml index e24745b885..66709f97cb 100644 --- a/.github/workflows/build_external.yml +++ b/.github/workflows/build_external.yml @@ -21,15 +21,23 @@ jobs: solana-ref: ${{ steps.set-git-refs.outputs.solana-ref }} starknet-ref: ${{ steps.set-git-refs.outputs.starknet-ref }} steps: - - uses: actions/checkout@v4 + # Checkout the shared scripts from the smartcontractkit/.github repo. + - name: Checkout shared .github scripts + uses: actions/checkout@v4 with: + repository: smartcontractkit/.github + # TODO: pin to a commit SHA once the get-refs-from-pr-body.js PR is merged into smartcontractkit/.github. + ref: feature/get-ref-from-pr-body persist-credentials: false + sparse-checkout: tools/scripts/get-refs-from-pr-body.js + sparse-checkout-cone-mode: false + path: shared-github - name: Get refs from PR body id: set-git-refs uses: actions/github-script@v7 with: script: | - const script = require('./.github/scripts/get-refs-from-pr-body.js') + const script = require(`${process.env.GITHUB_WORKSPACE}/shared-github/tools/scripts/get-refs-from-pr-body.js`) await script({ github, context, core }) build-chainlink: From fedb193923634dc6e893ed1535cb94511b00d8c9 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 16 Jul 2026 14:16:29 +0200 Subject: [PATCH 4/5] use common name for .github --- .github/workflows/build_external.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build_external.yml b/.github/workflows/build_external.yml index 66709f97cb..6f8d36cb2b 100644 --- a/.github/workflows/build_external.yml +++ b/.github/workflows/build_external.yml @@ -31,13 +31,13 @@ jobs: persist-credentials: false sparse-checkout: tools/scripts/get-refs-from-pr-body.js sparse-checkout-cone-mode: false - path: shared-github + path: dot_github - name: Get refs from PR body id: set-git-refs uses: actions/github-script@v7 with: script: | - const script = require(`${process.env.GITHUB_WORKSPACE}/shared-github/tools/scripts/get-refs-from-pr-body.js`) + const script = require(`${process.env.GITHUB_WORKSPACE}/dot_github/tools/scripts/get-refs-from-pr-body.js`) await script({ github, context, core }) build-chainlink: From 64a24c5ea1a9a0697ea57f9dc54f8efc71b08e38 Mon Sep 17 00:00:00 2001 From: Dmytro Haidashenko Date: Thu, 16 Jul 2026 18:52:23 +0200 Subject: [PATCH 5/5] pin to a commit --- .github/workflows/build_external.yml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/.github/workflows/build_external.yml b/.github/workflows/build_external.yml index 6f8d36cb2b..bac6cd181f 100644 --- a/.github/workflows/build_external.yml +++ b/.github/workflows/build_external.yml @@ -26,8 +26,7 @@ jobs: uses: actions/checkout@v4 with: repository: smartcontractkit/.github - # TODO: pin to a commit SHA once the get-refs-from-pr-body.js PR is merged into smartcontractkit/.github. - ref: feature/get-ref-from-pr-body + ref: 79a5c885eba3814105eb7f4848b611e4e42dd55e persist-credentials: false sparse-checkout: tools/scripts/get-refs-from-pr-body.js sparse-checkout-cone-mode: false