From e39624e83b97908fa69034f6ed9cca479e742a88 Mon Sep 17 00:00:00 2001 From: Andrei Neon Labs <169074588+andrei-neon@users.noreply.github.com> Date: Wed, 15 May 2024 15:08:54 +0200 Subject: [PATCH] trigger tracer ci tests --- .github/workflows/deploy.py | 151 +++++++++++++++++++++++++++++++++ .github/workflows/pipeline.yml | 30 ++++++- 2 files changed, 180 insertions(+), 1 deletion(-) diff --git a/.github/workflows/deploy.py b/.github/workflows/deploy.py index bcdb2ce8a..d15645491 100644 --- a/.github/workflows/deploy.py +++ b/.github/workflows/deploy.py @@ -3,6 +3,7 @@ import re import time import sys +from datetime import datetime import docker import subprocess @@ -62,6 +63,7 @@ def cli(): __file__).parent / "full_test_suite") VERSION_BRANCH_TEMPLATE = r"[vt]{1}\d{1,2}\.\d{1,2}\.x.*" +DATE_FORMAT = "%Y-%m-%d" def docker_compose(args: str): command = f'docker login -u {DOCKER_USERNAME} -p {DOCKER_PASSWORD}' @@ -424,6 +426,155 @@ def send_notification(url, build_url): requests.post(url=url, data=json.dumps(tpl)) +def get_tracer_workflows(session: requests.Session): + """ + Get all avialable workflows for Tracer CI. + """ + return session.get(f"https://api.github.com/repos/neonlabsorg/tracer-api/actions/workflows").json() + + +def find_workflow_id_by_key(workflows, key: str, value): + """ + Get workflow ID by key and value. + """ + for workflow in workflows["workflows"]: + if workflow[key] == value: + return workflow["id"] + return None + + +def get_runs_by_workflow_id( + session: requests.Session, workflow_id: int, created_time: str = datetime.now().strftime(DATE_FORMAT) +): + """ + Get all runs for Tracer CI and filter out them by workflow ID. + """ + result = set() + payload = {"created": created_time} + resp = session.get("https://api.github.com/repos/neonlabsorg/tracer-api/actions/runs", params=payload).json() + if resp["total_count"] > 0: + for run in resp["workflow_runs"]: + if run["workflow_id"] == workflow_id: + result.add(run["id"]) + click.echo(f"Workflow id {workflow_id} has runs: {result}.") + return result + + +def get_runs_count(session: requests.Session, created_time: str = datetime.now().strftime(DATE_FORMAT)): + """ + Get all runs for Tracer CI from particular date. + """ + payload = {"created": created_time} + resp = session.get("https://api.github.com/repos/neonlabsorg/tracer-api/actions/runs", params=payload).json() + return resp["total_count"] + + +def run_workflow( + session: requests.Session, + workflow_id: int, + ref: str = "main", + neon_tests_image: str = "latest", + proxy_image: str = "latest", + evm_loader_image: str = "latest", + geyser_neon_plugin_image: str = "main", +): + """ + Start a new dispatch workflow by it's ID. + """ + payload = { + "ref": ref, + "inputs": { + "neon_tests_image": neon_tests_image, + "proxy_image": proxy_image, + "evm_loader_image": evm_loader_image, + "geyser_neon_plugin_image": geyser_neon_plugin_image, + }, + } + + # Running workflow + resp = session.post( + f"https://api.github.com/repos/neonlabsorg/tracer-api/actions/workflows/{workflow_id}/dispatches", json=payload + ) + assert ( + resp.status_code == 204 + ), f"Failed to start workflow. Status code: {resp.status_code} with message: {resp.text}" + return resp + + +def wait_for_run_completion(session: requests.Session, run_id: int, timeout: int = 1500): + """ + Wait for run execution completion within a timeout. + """ + initial_time = time.time() + run_url = f"https://api.github.com/repos/neonlabsorg/tracer-api/actions/runs/{run_id}" + + r = session.get(run_url) + assert r.status_code == 200, f"Failed to get run {r.json()['html_url']}. Status code: {r.status_code} with message: {r.text}" + click.echo(f"Waiting for a run {r.json()['html_url']} for {timeout} sec.") + + status = r.json()["status"] + statuses = ["completed", "action_required", "cancelled", "failure", "skipped", "stale", "timed_out"] + while time.time() - initial_time < timeout and status not in statuses: + r = session.get(run_url) + assert r.status_code == 200 + status = r.json()["status"] + click.echo(f"Time elapsed: {int(time.time() - initial_time)}. Status: {status}.") + time.sleep(10) + assert status in ["completed", "success"] + + r = session.get(run_url) + assert r.status_code == 200 + conclusion = r.json()["conclusion"] + click.echo(f"Run {r.json()['html_url']} completed with conclusion: {conclusion}.") + assert conclusion == "success", f"Expected conclusion: success but got: {conclusion}. Run URL: {r.json()['html_url']} ." + + +@cli.command(name="trigger_tracer_tests", help="Triggers tests for Tracer CI workflow.") +@click.option("--ref", default="main", help="The branch to trigger the tests.") +@click.option("--neon_tests_image", default="latest", help="The image of the neon_tests.") +@click.option("--proxy_image_name", default="proxy", help="The name of the proxy image.") +@click.option("--proxy_image_tag", default="latest", help="The tag of the proxy image.") +@click.option("--evm_loader_image_tag", default="latest",help="The tag of the evm_loader image.") +@click.option("--geyser_neon_plugin_image", default="main",help="The image of the geyser_neon_plugin.") +@click.option("--token", help="The token for the GitHub API.") +def trigger_tracer_tests(ref, neon_tests_image, proxy_image_tag, proxy_image_name, evm_loader_image_tag, geyser_neon_plugin_image, token): + session = requests.Session() + + session.headers = { + "Accept": "application/vnd.github+json", + "X-GitHub-Api-Version": "2022-11-28", + "Authorization": f"Bearer {token}", + } + created_time = datetime.now().strftime(DATE_FORMAT) + workflows = get_tracer_workflows(session) + + workflow_id = find_workflow_id_by_key(workflows, "name", "tracer api main pipeline") + + runs_before_start = get_runs_by_workflow_id(session, workflow_id, created_time) + runs_count = get_runs_count(session, created_time) + + run_workflow( + session, + workflow_id, + ref=ref, + neon_tests_image=neon_tests_image, + proxy_image=proxy_image_tag, + evm_loader_image=evm_loader_image_tag, + geyser_neon_plugin_image=geyser_neon_plugin_image, + ) + + initial_time = time.time() + while runs_count == get_runs_count(session, created_time) and time.time() - initial_time < 100: + time.sleep(10) + + runs_after_start = get_runs_by_workflow_id(session, workflow_id, created_time) + active_runs = runs_after_start - runs_before_start + # Wait for results + assert len(active_runs) == 1, f"Expected 1 active run but got: {active_runs}." + active_run = active_runs.pop() + wait_for_run_completion(session, active_run) + + def process_output(output): for line in output: if line: diff --git a/.github/workflows/pipeline.yml b/.github/workflows/pipeline.yml index c1104eb41..d1bc85025 100644 --- a/.github/workflows/pipeline.yml +++ b/.github/workflows/pipeline.yml @@ -15,6 +15,14 @@ on: initial_pr: required: false description: "Initial PR" + trigger_tracer_tests: + required: false + description: "Trigger Tracer CI tests" + default: 'false' + type: choice + options: + - 'true' + - 'false' pull_request: types: [opened, reopened, synchronize, labeled, unlabeled, ready_for_review] push: @@ -423,6 +431,24 @@ jobs: python3 ./.github/workflows/deploy.py destroy_terraform \ --run_number=${{env.GITHUB_RUN_NUMBER}} \ --proxy_tag=${{ needs.build-image.outputs.proxy_tag }} + trigger-tracer-tests: + if: ${{ (contains(github.event.pull_request.labels.*.name, 'runTracerTests')) || (inputs.trigger_tracer_tests == 'true') }} + needs: build-image + runs-on: test-runner + steps: + - name: Run a script + run: echo "PR was labeled with runTracerTests so triggering Tracer CI tests!" + - uses: actions/checkout@v4 + with: + fetch-depth: 0 + - name: Run Tracer CI test workflow + run: | + python3 ./.github/workflows/deploy.py trigger_tracer_tests --proxy_image_tag=${{ needs.build-image.outputs.proxy_tag }} \ + --evm_loader_image_tag=${{ needs.build-image.outputs.neon_evm_tag }} \ + --token=${{ secrets.GHTOKEN }} --ref=update_hardcoded_proxy_image_name + - name: Cancel the build if job failed + if: "failure()" + uses: "andymckay/cancel-action@0.4" finalize-image: runs-on: build-runner needs: @@ -431,12 +457,14 @@ jobs: - basic-tests - dapps-tests - deploy-check + - trigger-tracer-tests if: | always() && needs.deploy-check.result == 'success' && needs.basic-tests.result == 'success' && needs.dapps-tests.result == 'success' && - contains(fromJSON('["success", "skipped"]'), needs.openzeppelin-tests.result) + contains(fromJSON('["success", "skipped"]'), needs.openzeppelin-tests.result) && + contains(fromJSON('["success", "skipped"]'), needs.trigger-tracer-tests.result) steps: - uses: actions/checkout@v4 - name: Tag published image