From 8b7c536d10fd74adce510479d664d61b3fed2111 Mon Sep 17 00:00:00 2001 From: Bartek Tofel Date: Thu, 2 Apr 2026 10:50:21 +0200 Subject: [PATCH] add a dedicated bash script for resolving chainlink image to use in the pipeline; bump a couple of actions' versions --- .github/scripts/resolve-chainlink-image.sh | 48 ++++++ .../scripts/resolve-chainlink-image_test.sh | 159 ++++++++++++++++++ .github/workflows/cre-system-tests.yaml | 121 +++++++------ .github/workflows/integration-tests.yml | 2 +- .../resolve-chainlink-image-tests.yml | 27 +++ 5 files changed, 295 insertions(+), 62 deletions(-) create mode 100755 .github/scripts/resolve-chainlink-image.sh create mode 100755 .github/scripts/resolve-chainlink-image_test.sh create mode 100644 .github/workflows/resolve-chainlink-image-tests.yml diff --git a/.github/scripts/resolve-chainlink-image.sh b/.github/scripts/resolve-chainlink-image.sh new file mode 100755 index 00000000000..7dad432f654 --- /dev/null +++ b/.github/scripts/resolve-chainlink-image.sh @@ -0,0 +1,48 @@ +#!/usr/bin/env bash +# Resolves the Chainlink Docker image to use based on environment variables. +# Priority: +# 1. If CHAINLINK_FULL_IMAGE is set, use it directly (must include registry and tag/digest). +# 2. Otherwise, construct the image from: +# - Repository: CHAINLINK_IMAGE_REPO (default: "chainlink") +# - Tag: CHAINLINK_IMAGE_TAG or fallback to CHAINLINK_VERSION +# - Registry: AWS ECR using AWS_ACCOUNT_NUMBER and AWS_REGION +# Enforces mutual exclusivity between FULL_IMAGE and repo/tag inputs, and validates required fields. +set -euo pipefail + +error() { + echo "Error: $1" >&2 + exit 1 +} + +full_image="${CHAINLINK_FULL_IMAGE:-}" +explicit_repo="${CHAINLINK_IMAGE_REPO:-}" +tag="${CHAINLINK_IMAGE_TAG:-}" +version="${CHAINLINK_VERSION:-}" +aws_account="${AWS_ACCOUNT_NUMBER:-}" +aws_region="${AWS_REGION:-}" + +resolved_repo="${explicit_repo:-chainlink}" + +if [[ -n "${full_image}" ]]; then + if [[ -n "${tag}" || -n "${explicit_repo}" ]]; then + error "'CHAINLINK_FULL_IMAGE' is mutually exclusive with 'CHAINLINK_IMAGE_TAG' and 'CHAINLINK_IMAGE_REPO'." + fi + + if [[ ! "${full_image}" =~ .+/.+(:[^@]+|@sha256:[a-f0-9]{64})$ ]]; then + error "Invalid 'CHAINLINK_FULL_IMAGE' format: '${full_image}'. Expected '/:' or '/@sha256:'." + fi + + printf '%s\n' "${full_image}" + exit 0 +fi + +resolved_tag="${tag:-$version}" +if [[ -z "${resolved_tag}" ]]; then + error "Either provide 'CHAINLINK_FULL_IMAGE' or provide a tag via 'CHAINLINK_IMAGE_TAG' (or 'CHAINLINK_VERSION')." +fi + +if [[ -z "${aws_account}" || -z "${aws_region}" ]]; then + error "When 'CHAINLINK_FULL_IMAGE' is not provided, both 'AWS_ACCOUNT_NUMBER' and 'AWS_REGION' environment variables must be set and non-empty." +fi + +printf '%s\n' "${aws_account}.dkr.ecr.${aws_region}.amazonaws.com/${resolved_repo}:${resolved_tag}" diff --git a/.github/scripts/resolve-chainlink-image_test.sh b/.github/scripts/resolve-chainlink-image_test.sh new file mode 100755 index 00000000000..2f379758117 --- /dev/null +++ b/.github/scripts/resolve-chainlink-image_test.sh @@ -0,0 +1,159 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +TARGET_SCRIPT="${SCRIPT_DIR}/resolve-chainlink-image.sh" + +TESTS_RUN=0 +TESTS_FAILED=0 + +RUN_STATUS=0 +RUN_STDOUT="" +RUN_STDERR="" + +run_script() { + local -a env_vars=("$@") + local stdout_file + local stderr_file + stdout_file="$(mktemp)" + stderr_file="$(mktemp)" + + set +e + env -i PATH="${PATH}" "${env_vars[@]}" bash "${TARGET_SCRIPT}" >"${stdout_file}" 2>"${stderr_file}" + RUN_STATUS=$? + set -e + + RUN_STDOUT="$(<"${stdout_file}")" + RUN_STDERR="$(<"${stderr_file}")" + rm -f "${stdout_file}" "${stderr_file}" +} + +assert_eq() { + local got="$1" + local want="$2" + local msg="$3" + if [[ "${got}" != "${want}" ]]; then + echo "FAIL: ${msg}" + echo " expected: ${want}" + echo " got: ${got}" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +assert_contains() { + local haystack="$1" + local needle="$2" + local msg="$3" + if [[ "${haystack}" != *"${needle}"* ]]; then + echo "FAIL: ${msg}" + echo " expected substring: ${needle}" + echo " got: ${haystack}" + TESTS_FAILED=$((TESTS_FAILED + 1)) + fi +} + +test_full_image_tag() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_FULL_IMAGE=public.ecr.aws/chainlink/chainlink:2.0.0" + assert_eq "${RUN_STATUS}" "0" "full image with tag exits 0" + assert_eq "${RUN_STDOUT}" "public.ecr.aws/chainlink/chainlink:2.0.0" "full image is returned to stdout" + assert_eq "${RUN_STDERR}" "" "full image success does not write stderr" +} + +test_full_image_digest() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_FULL_IMAGE=public.ecr.aws/chainlink/chainlink@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" + assert_eq "${RUN_STATUS}" "0" "full image with digest exits 0" + assert_eq "${RUN_STDOUT}" "public.ecr.aws/chainlink/chainlink@sha256:aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa" "digest image is returned to stdout" +} + +test_repo_and_tag() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_IMAGE_REPO=chainlink-integration-tests" \ + "CHAINLINK_IMAGE_TAG=v2.1.0" \ + "AWS_ACCOUNT_NUMBER=123456789012" \ + "AWS_REGION=us-west-2" + assert_eq "${RUN_STATUS}" "0" "repo and tag exits 0" + assert_eq "${RUN_STDOUT}" "123456789012.dkr.ecr.us-west-2.amazonaws.com/chainlink-integration-tests:v2.1.0" "repo and tag resolve to ECR image" + assert_eq "${RUN_STDERR}" "" "repo/tag success does not write stderr" +} + +test_default_repo_and_version() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_VERSION=abc123" \ + "AWS_ACCOUNT_NUMBER=123456789012" \ + "AWS_REGION=us-west-2" + assert_eq "${RUN_STATUS}" "0" "version fallback exits 0" + assert_eq "${RUN_STDOUT}" "123456789012.dkr.ecr.us-west-2.amazonaws.com/chainlink:abc123" "default repo is chainlink" +} + +test_tag_wins_over_version() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_IMAGE_TAG=explicit-tag" \ + "CHAINLINK_VERSION=abc123" \ + "AWS_ACCOUNT_NUMBER=123456789012" \ + "AWS_REGION=us-west-2" + assert_eq "${RUN_STATUS}" "0" "tag override exits 0" + assert_eq "${RUN_STDOUT}" "123456789012.dkr.ecr.us-west-2.amazonaws.com/chainlink:explicit-tag" "tag wins over version" +} + +test_mutual_exclusion_full_and_tag() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_FULL_IMAGE=public.ecr.aws/chainlink/chainlink:2.0.0" \ + "CHAINLINK_IMAGE_TAG=v2.1.0" + assert_eq "${RUN_STATUS}" "1" "full image and tag exits 1" + assert_contains "${RUN_STDERR}" "mutually exclusive" "mutual exclusion error is reported" +} + +test_invalid_full_image() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_FULL_IMAGE=chainlink:2.0.0" + assert_eq "${RUN_STATUS}" "1" "invalid full image exits 1" + assert_contains "${RUN_STDERR}" "Invalid 'CHAINLINK_FULL_IMAGE' format" "invalid full image format is reported" +} + +test_missing_tag_and_version() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "AWS_ACCOUNT_NUMBER=123456789012" \ + "AWS_REGION=us-west-2" + assert_eq "${RUN_STATUS}" "1" "missing tag and version exits 1" + assert_contains "${RUN_STDERR}" "Either provide 'CHAINLINK_FULL_IMAGE'" "missing tag/version error is reported" +} + +test_missing_aws_envs() { + TESTS_RUN=$((TESTS_RUN + 1)) + run_script \ + "CHAINLINK_IMAGE_TAG=v2.1.0" + assert_eq "${RUN_STATUS}" "1" "missing AWS env vars exits 1" + assert_contains "${RUN_STDERR}" "'AWS_ACCOUNT_NUMBER' and 'AWS_REGION'" "missing AWS env vars error is reported" +} + +main() { + test_full_image_tag + test_full_image_digest + test_repo_and_tag + test_default_repo_and_version + test_tag_wins_over_version + test_mutual_exclusion_full_and_tag + test_invalid_full_image + test_missing_tag_and_version + test_missing_aws_envs + + if [[ "${TESTS_FAILED}" -ne 0 ]]; then + echo + echo "Tests failed: ${TESTS_FAILED}/${TESTS_RUN}" + exit 1 + fi + + echo "All tests passed: ${TESTS_RUN}" +} + +main diff --git a/.github/workflows/cre-system-tests.yaml b/.github/workflows/cre-system-tests.yaml index 2ad376b542c..dffb2896a01 100644 --- a/.github/workflows/cre-system-tests.yaml +++ b/.github/workflows/cre-system-tests.yaml @@ -3,36 +3,48 @@ name: CRE System Tests on: workflow_dispatch: inputs: - ecr_name: - description: "The name of the ECR repository to pull the image from, defaults to 'chainlink'" + chainlink_image_repo: + description: "ECR repository name used to compose image with chainlink_image_tag (for example: chainlink-integration-tests or chainlink)." required: false type: string - default: "chainlink" + default: "" chainlink_image_tag: - required: true + required: false + type: string + default: "" + description: "Tag used with chainlink_image_repo. If empty, chainlink_version is used." + chainlink_full_image: + required: false type: string - description: "The tag of the Chainlink image to use for the tests." + default: "" + description: "Full Chainlink image reference including tag or digest (for example: public.ecr.aws/chainlink/chainlink:2.0.0 or ...@sha256:). Mutually exclusive with chainlink_image_repo/chainlink_image_tag." chainlink_version: - required: true + required: false type: string - description: "The version of Chainlink repository to use for the tests." - default: "develop" + description: "The version of Chainlink repository to use for the tests. If empty, defaults to github.sha." + default: "" workflow_call: inputs: - ecr_name: - description: "The name of the ECR repository to pull the image from, defaults to 'chainlink'" + chainlink_image_repo: + description: "ECR repository name used to compose image with chainlink_image_tag (for example: chainlink-integration-tests or chainlink)." required: false type: string - default: "chainlink" + default: "" chainlink_image_tag: - required: true + required: false type: string - description: "The tag of the Chainlink image to use for the tests." + default: "" + description: "Tag used with chainlink_image_repo. If empty, chainlink_version is used." + chainlink_full_image: + required: false + type: string + default: "" + description: "Full Chainlink image reference including tag or digest (for example: public.ecr.aws/chainlink/chainlink:2.0.0 or ...@sha256:). Mutually exclusive with chainlink_image_repo/chainlink_image_tag." chainlink_version: - required: true + required: false type: string - description: "The version of Chainlink repository to use for the tests." - default: "develop" + description: "The version of Chainlink repository to use for the tests. If empty, defaults to github.sha." + default: "" jobs: define-test-matrix: @@ -45,7 +57,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 with: - ref: ${{ inputs.chainlink_version }} + ref: ${{ inputs.chainlink_version && inputs.chainlink_version || github.sha }} persist-credentials: false - name: Enable S3 Cache for Self-Hosted Runners @@ -167,7 +179,7 @@ jobs: - name: Checkout uses: actions/checkout@v6 with: - ref: ${{ inputs.chainlink_version }} + ref: ${{ inputs.chainlink_version || github.sha }} persist-credentials: false - name: Set up Go @@ -177,7 +189,7 @@ jobs: go-version-file: system-tests/tests/go.mod cache: true - # We need to login to ECR to allow the test to pull the Job Distributor (main), Chip Ingress (main) and Chainlink (sdlc) images + # Required to pull Job Distributor (main), Chip Ingress (main) and Chainlink (sdlc) private images - name: Configure AWS Credentials uses: aws-actions/configure-aws-credentials@010d0da01d0b5a38af31e9c3470dbfdabdecca3a # v4.0.1 with: @@ -188,69 +200,56 @@ jobs: - name: Login to Amazon ECR id: login-ecr - uses: aws-actions/amazon-ecr-login@062b18b96a7aff071d4dc91bc00c4c1a7945b076 # v2.0.1 + uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2.1.1 with: registries: ${{ format('{0},{1}', secrets.QA_AWS_ACCOUNT_NUMBER, secrets.AWS_ACCOUNT_ID_PROD) }} env: AWS_REGION: ${{ secrets.QA_AWS_REGION }} + # Required to allow pulling public images + - name: Authenticate to ECR (public) + id: login-ecr-public + uses: aws-actions/amazon-ecr-login@183a1442edf41672e66566b7fc560e297a290896 # v2.1.1 + with: + registry-type: public + env: + AWS_REGION: us-east-1 + - name: Set up gotestsum shell: bash run: | echo "::startgroup::Install gotestsum" - go install gotest.tools/gotestsum@v1.12.3 - echo "::endgroup::" - - - name: Install CTF binary - shell: bash - working-directory: core/scripts/cre/environment - env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - CTF_TAG: "framework/v0.11.9" - run: | - echo "::startgroup::Install CTF binary" - mkdir -p bin - gh release download "${CTF_TAG}" --pattern "framework-v*-linux-amd64.tar.gz" --clobber --repo smartcontractkit/chainlink-testing-framework -O ctf.tar.gz - tar -xzf ctf.tar.gz - mv ctf bin/ctf - chmod +x bin/ctf + go install gotest.tools/gotestsum@v1.13.0 echo "::endgroup::" - name: Install Aptos CLI if: ${{ matrix.tests.test_name == 'Test_CRE_V2_Aptos_Suite' }} + uses: aptos-labs/actions/install-aptos-cli@63740b290d839b87ecfafbcf75ed03a36a54a29f # jan 15, 2025 + with: + CLI_VERSION: 7.8.0 + + - name: Resolve Chainlink image + id: resolve-chainlink-image + working-directory: .github/scripts shell: bash env: - GH_TOKEN: ${{ secrets.GITHUB_TOKEN }} - APTOS_CLI_TAG: "aptos-cli-v7.8.0" + CHAINLINK_IMAGE_REPO: ${{ inputs.chainlink_image_repo }} + CHAINLINK_IMAGE_TAG: ${{ inputs.chainlink_image_tag }} + CHAINLINK_FULL_IMAGE: ${{ inputs.chainlink_full_image }} + CHAINLINK_VERSION: ${{ inputs.chainlink_version || github.sha }} + AWS_ACCOUNT_NUMBER: ${{ secrets.QA_AWS_ACCOUNT_NUMBER }} + AWS_REGION: ${{ secrets.QA_AWS_REGION }} run: | - echo "::startgroup::Install Aptos CLI" - bin_dir="$HOME/.local/bin" - mkdir -p "$bin_dir" - - gh release download "${APTOS_CLI_TAG}" \ - --pattern "aptos-cli-*-Ubuntu-24.04-x86_64.zip" \ - --clobber \ - --repo aptos-labs/aptos-core \ - -O aptos-cli.zip - - unzip -o aptos-cli.zip -d aptos-cli-extract >/dev/null - aptos_path="$(find aptos-cli-extract -type f -name aptos | head -n1)" - if [[ -z "$aptos_path" ]]; then - echo "failed to locate aptos binary in release archive" - exit 1 - fi - - install -m 0755 "$aptos_path" "$bin_dir/aptos" - echo "$bin_dir" >> "$GITHUB_PATH" - "$bin_dir/aptos" --version - echo "::endgroup::" + resolved_image="$(bash resolve-chainlink-image.sh)" + echo "$resolved_image" + echo "resolved_image=${resolved_image}" >> "${GITHUB_OUTPUT}" - name: Start local CRE${{ matrix.tests.cre_version }} shell: bash id: start-local-cre env: CTF_JD_IMAGE: "${{ secrets.AWS_ACCOUNT_ID_PROD }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/job-distributor:0.22.1" - CTF_CHAINLINK_IMAGE: "${{ secrets.QA_AWS_ACCOUNT_NUMBER }}.dkr.ecr.${{ secrets.QA_AWS_REGION }}.amazonaws.com/${{ inputs.ecr_name }}:${{ inputs.chainlink_image_tag != '' && inputs.chainlink_image_tag || inputs.chainlink_version }}" + CTF_CHAINLINK_IMAGE: "${{ steps.resolve-chainlink-image.outputs.resolved_image }}" CTF_CONFIGS: ${{ matrix.tests.configs }} CRE_VERSION: ${{ matrix.tests.cre_version }} TEST_NAME: ${{ matrix.tests.test_name }} @@ -263,7 +262,7 @@ jobs: echo "Test requires observability stack, starting..." # Start observability stack - ./bin/ctf obs up -f + go run . obs up -f echo "::endgroup::" echo "::group::Validating Observability Stack" diff --git a/.github/workflows/integration-tests.yml b/.github/workflows/integration-tests.yml index 9c63b5ce131..7e09b38ab11 100644 --- a/.github/workflows/integration-tests.yml +++ b/.github/workflows/integration-tests.yml @@ -386,7 +386,7 @@ jobs: if: needs.run-core-cre-e2e-tests-setup.outputs.should-run == 'true' uses: ./.github/workflows/cre-system-tests.yaml with: - ecr_name: ${{ inputs.ecr_name || 'chainlink-integration-tests' }} + chainlink_image_repo: ${{ inputs.ecr_name || 'chainlink-integration-tests' }} chainlink_version: ${{ inputs.evm-ref || inputs.cl_ref || github.sha }} chainlink_image_tag: ${{ inputs.evm-ref && format('{0}', inputs.evm-ref) || inputs.cl_ref && format('{0}', inputs.cl_ref) || format('{0}', github.sha) }} secrets: inherit diff --git a/.github/workflows/resolve-chainlink-image-tests.yml b/.github/workflows/resolve-chainlink-image-tests.yml new file mode 100644 index 00000000000..7b1748d2018 --- /dev/null +++ b/.github/workflows/resolve-chainlink-image-tests.yml @@ -0,0 +1,27 @@ +name: Resolve Chainlink Image Tests + +on: + pull_request: + paths: + - ".github/scripts/resolve-chainlink-image.sh" + - ".github/scripts/resolve-chainlink-image_test.sh" + - ".github/workflows/cre-system-tests.yaml" + - ".github/workflows/cre-regression-system-tests.yaml" + - ".github/workflows/system-tests-nightly.yml" + workflow_dispatch: + +jobs: + resolve-chainlink-image-tests: + name: Resolve Chainlink Image Tests + runs-on: ubuntu-latest + permissions: + contents: read + steps: + - name: Checkout repository + uses: actions/checkout@v6 + with: + persist-credentials: false + + - name: Run resolver tests + shell: bash + run: bash ./.github/scripts/resolve-chainlink-image_test.sh