mirror of
https://github.com/trezor/blockbook.git
synced 2026-03-23 16:07:24 +01:00
ci: deploy workflow matrix
This commit is contained in:
121
.github/scripts/prepare_deploy_plan.py
vendored
Executable file
121
.github/scripts/prepare_deploy_plan.py
vendored
Executable file
@@ -0,0 +1,121 @@
|
||||
#!/usr/bin/env python3
|
||||
|
||||
import json
|
||||
import os
|
||||
import re
|
||||
import sys
|
||||
from pathlib import Path
|
||||
|
||||
|
||||
def fail(message: str) -> None:
|
||||
print(f"error: {message}", file=sys.stderr)
|
||||
raise SystemExit(1)
|
||||
|
||||
|
||||
def matchable_name(coin: str) -> str:
|
||||
marker = "_testnet"
|
||||
idx = coin.find(marker)
|
||||
if idx != -1:
|
||||
return coin[:idx] + "=test"
|
||||
return coin + "=main"
|
||||
|
||||
|
||||
def load_runner_map(vars_map: dict) -> dict:
|
||||
prefix = "BB_RUNNER_"
|
||||
mapping = {}
|
||||
for key, value in vars_map.items():
|
||||
if not key.startswith(prefix):
|
||||
continue
|
||||
coin = key[len(prefix):].strip()
|
||||
runner = "" if value is None else str(value).strip()
|
||||
if coin and runner:
|
||||
mapping[coin] = runner
|
||||
return mapping
|
||||
|
||||
|
||||
def parse_requested_coins(raw: str, available: dict) -> list[str]:
|
||||
text = raw.strip()
|
||||
if not text:
|
||||
fail("coins input is empty")
|
||||
|
||||
if text.upper() == "ALL":
|
||||
coins = sorted(available.keys())
|
||||
if not coins:
|
||||
fail("no BB_RUNNER_* variables found")
|
||||
return coins
|
||||
|
||||
tokens = [part.strip() for part in re.split(r"[\s,]+", text) if part.strip()]
|
||||
if not tokens:
|
||||
fail("coins input resolved to an empty list")
|
||||
if any(token.upper() == "ALL" for token in tokens):
|
||||
fail("ALL must be used alone")
|
||||
|
||||
seen = set()
|
||||
result = []
|
||||
for coin in tokens:
|
||||
if coin in seen:
|
||||
continue
|
||||
seen.add(coin)
|
||||
result.append(coin)
|
||||
return result
|
||||
|
||||
|
||||
def main() -> None:
|
||||
workspace = Path(os.environ.get("GITHUB_WORKSPACE", ".")).resolve()
|
||||
vars_map = json.loads(os.environ.get("VARS_JSON", "{}"))
|
||||
coins_input = os.environ.get("COINS_INPUT", "")
|
||||
|
||||
runner_map = load_runner_map(vars_map)
|
||||
if not runner_map:
|
||||
fail("no BB_RUNNER_* variables found")
|
||||
|
||||
requested = parse_requested_coins(coins_input, runner_map)
|
||||
|
||||
tests_path = workspace / "tests" / "tests.json"
|
||||
configs_dir = workspace / "configs" / "coins"
|
||||
|
||||
try:
|
||||
tests_cfg = json.loads(tests_path.read_text(encoding="utf-8"))
|
||||
except Exception as exc:
|
||||
fail(f"cannot read {tests_path}: {exc}")
|
||||
|
||||
deploy_matrix = []
|
||||
e2e_names = []
|
||||
|
||||
for coin in requested:
|
||||
if coin not in runner_map:
|
||||
fail(f"missing BB_RUNNER_{coin}")
|
||||
|
||||
coin_cfg_path = configs_dir / f"{coin}.json"
|
||||
if not coin_cfg_path.exists():
|
||||
fail(f"unknown coin '{coin}' (missing {coin_cfg_path})")
|
||||
|
||||
test_cfg = tests_cfg.get(coin)
|
||||
if not isinstance(test_cfg, dict) or "connectivity" not in test_cfg:
|
||||
fail(f"coin '{coin}' has no connectivity tests in tests/tests.json")
|
||||
|
||||
deploy_matrix.append({"coin": coin, "runner": runner_map[coin]})
|
||||
e2e_names.append(matchable_name(coin))
|
||||
|
||||
unique_names = sorted(set(e2e_names))
|
||||
if not unique_names:
|
||||
fail("no coins selected after validation")
|
||||
|
||||
escaped = [re.escape(name) for name in unique_names]
|
||||
e2e_regex = "TestIntegration/(" + "|".join(escaped) + ")/api"
|
||||
|
||||
output_file = os.environ.get("GITHUB_OUTPUT")
|
||||
if not output_file:
|
||||
fail("GITHUB_OUTPUT is not set")
|
||||
|
||||
with open(output_file, "a", encoding="utf-8") as out:
|
||||
out.write(f"deploy_matrix={json.dumps(deploy_matrix, separators=(',', ':'))}\n")
|
||||
out.write(f"e2e_regex={e2e_regex}\n")
|
||||
out.write(f"coins_csv={','.join(requested)}\n")
|
||||
|
||||
print("Selected coins:", ", ".join(requested))
|
||||
print("E2E regex:", e2e_regex)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
79
.github/workflows/deploy.yml
vendored
Normal file
79
.github/workflows/deploy.yml
vendored
Normal file
@@ -0,0 +1,79 @@
|
||||
name: Deploy
|
||||
|
||||
on:
|
||||
workflow_dispatch:
|
||||
inputs:
|
||||
coins:
|
||||
description: "Comma-separated coin aliases from configs/coins, or ALL"
|
||||
required: true
|
||||
ref:
|
||||
description: "Git ref to deploy (leave empty for current ref)"
|
||||
required: false
|
||||
default: ""
|
||||
|
||||
permissions:
|
||||
contents: read
|
||||
|
||||
jobs:
|
||||
prepare:
|
||||
name: Prepare Plan
|
||||
runs-on: ubuntu-latest
|
||||
outputs:
|
||||
deploy_matrix: ${{ steps.plan.outputs.deploy_matrix }}
|
||||
e2e_regex: ${{ steps.plan.outputs.e2e_regex }}
|
||||
coins_csv: ${{ steps.plan.outputs.coins_csv }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref != '' && inputs.ref || github.ref }}
|
||||
|
||||
- name: Build deploy/e2e plan
|
||||
id: plan
|
||||
env:
|
||||
VARS_JSON: ${{ toJSON(vars) }}
|
||||
COINS_INPUT: ${{ inputs.coins }}
|
||||
run: ./.github/scripts/prepare_deploy_plan.py
|
||||
|
||||
deploy:
|
||||
name: Deploy (${{ matrix.coin }})
|
||||
needs: prepare
|
||||
strategy:
|
||||
fail-fast: false
|
||||
matrix:
|
||||
include: ${{ fromJSON(needs.prepare.outputs.deploy_matrix) }}
|
||||
runs-on: [self-hosted, Linux, X64, "${{ matrix.runner }}"]
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref != '' && inputs.ref || github.ref }}
|
||||
|
||||
- name: Export repository variables
|
||||
uses: ./.github/actions/export-repository-variables
|
||||
with:
|
||||
vars_json: ${{ toJSON(vars) }}
|
||||
|
||||
- name: Deploy blockbook package
|
||||
run: ./contrib/scripts/deploy-blockbook-local.sh "${{ matrix.coin }}"
|
||||
|
||||
e2e-tests:
|
||||
name: E2E Tests (post-deploy)
|
||||
needs: [prepare, deploy]
|
||||
if: ${{ needs.deploy.result == 'success' }}
|
||||
runs-on: [self-hosted, Linux, X64]
|
||||
env:
|
||||
E2E_REGEX: ${{ needs.prepare.outputs.e2e_regex }}
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
with:
|
||||
ref: ${{ inputs.ref != '' && inputs.ref || github.ref }}
|
||||
|
||||
- name: Export repository variables
|
||||
uses: ./.github/actions/export-repository-variables
|
||||
with:
|
||||
vars_json: ${{ toJSON(vars) }}
|
||||
|
||||
- name: Run e2e tests
|
||||
run: make test-e2e ARGS="-v -run ${E2E_REGEX}"
|
||||
20
.github/workflows/testing.yml
vendored
20
.github/workflows/testing.yml
vendored
@@ -1,4 +1,4 @@
|
||||
name: CI
|
||||
name: Testing
|
||||
|
||||
on:
|
||||
push:
|
||||
@@ -53,21 +53,3 @@ jobs:
|
||||
|
||||
- name: Run integration tests
|
||||
run: make test-integration ARGS="-v"
|
||||
|
||||
e2e-tests:
|
||||
name: E2E Tests (Blockbook API)
|
||||
runs-on: [self-hosted, Linux, X64]
|
||||
needs: integration-tests
|
||||
if: ${{ github.event_name != 'pull_request' || github.event.pull_request.head.repo.full_name == github.repository }}
|
||||
|
||||
steps:
|
||||
- name: Checkout code
|
||||
uses: actions/checkout@v4
|
||||
|
||||
- name: Export repository variables
|
||||
uses: ./.github/actions/export-repository-variables
|
||||
with:
|
||||
vars_json: ${{ toJSON(vars) }}
|
||||
|
||||
- name: Run e2e tests
|
||||
run: make test-e2e ARGS="-v"
|
||||
|
||||
Reference in New Issue
Block a user