From a7d2e552af16de8bc0fafd49bc0954835fa88f73 Mon Sep 17 00:00:00 2001 From: dresber Date: Thu, 3 Sep 2026 21:03:12 +0000 Subject: [PATCH] fix private pip installation and notification of healed build --- .gitea/workflows/docker-publish.yml | 6 +- .gitea/workflows/notifications.yml | 114 ++++++++++---------- .gitea/workflows/python-checks.yml | 51 ++++++++- .gitea/workflows/python-package-publish.yml | 6 +- 4 files changed, 113 insertions(+), 64 deletions(-) diff --git a/.gitea/workflows/docker-publish.yml b/.gitea/workflows/docker-publish.yml index 93f8ead..211fea6 100644 --- a/.gitea/workflows/docker-publish.yml +++ b/.gitea/workflows/docker-publish.yml @@ -38,8 +38,10 @@ on: # whose Dockerfile installs the in-house BB* packages. The Dockerfile must consume it # via `RUN --mount=type=secret,id=pip_extra_index`, never as an ARG or ENV, so the # token is not baked into an image layer. - type: boolean - default: false + # Gitea 1.27.x does not reliably propagate boolean inputs through + # reusable workflows. Callers pass the literal string "true". + type: string + default: "false" secrets: REGISTRY_USERNAME: { required: true } REGISTRY_PASSWORD: { required: true } diff --git a/.gitea/workflows/notifications.yml b/.gitea/workflows/notifications.yml index a6b357a..1f2adda 100644 --- a/.gitea/workflows/notifications.yml +++ b/.gitea/workflows/notifications.yml @@ -43,72 +43,76 @@ jobs: current_event = "${{ gitea.event_name }}" token = os.environ["API_GITEA_TOKEN"] - url = ( - f"{server}/api/v1/repos/{repo}/actions/runs" - f"?page=1&limit=5" - ) - - print(f"Fetching workflow runs from: {url}") print(f"Current run number: {current_run_number}") print(f"Current branch: {current_branch}") print(f"Current event: {current_event}") - - req = urllib.request.Request( - url, - headers={ - "Authorization": f"token {token}", - "Accept": "application/json", - }, - ) - - with urllib.request.urlopen(req) as response: - data = json.loads(response.read().decode("utf-8")) - - runs = data.get("workflow_runs", []) - - print(f"Received {len(runs)} workflow runs") - previous = "unknown" - for run in runs: - run_number = run.get("run_number") - status = run.get("status") - conclusion = run.get("conclusion") - branch = run.get("head_branch") - event = run.get("event") - - print( - f"Inspecting run #{run_number}: " - f"status={status}, " - f"conclusion={conclusion}, " - f"branch={branch}, " - f"event={event}" + # Scheduled runs can push the prior run for this branch/event beyond the + # first page. Scan a bounded history so a succeeding push after a failure + # is still reported as healed. + for page in range(1, 11): + url = f"{server}/api/v1/repos/{repo}/actions/runs?page={page}&limit=100" + print(f"Fetching workflow runs page {page}: {url}") + req = urllib.request.Request( + url, + headers={ + "Authorization": f"token {token}", + "Accept": "application/json", + }, ) + with urllib.request.urlopen(req) as response: + data = json.loads(response.read().decode("utf-8")) - # aktuellen Run überspringen - if int(run_number) == current_run_number: - print(" -> skipping current run") - continue + runs = data.get("workflow_runs", []) + if not isinstance(runs, list): + print("Received an invalid workflow-runs payload; stopping lookup.") + break + print(f"Received {len(runs)} workflow runs on page {page}") + if not runs: + break - # nur abgeschlossene Runs - if status != "completed": - print(" -> skipping non-completed run") - continue + for run in runs: + run_number = run.get("run_number") + status = run.get("status") + conclusion = run.get("conclusion") + branch = run.get("head_branch") + event = run.get("event") - # nur gleicher Branch - if branch != current_branch: - print(" -> skipping different branch") - continue + print( + f"Inspecting run #{run_number}: " + f"status={status}, " + f"conclusion={conclusion}, " + f"branch={branch}, " + f"event={event}" + ) - # nur gleiches Event - if event != current_event: - print(" -> skipping different event") - continue + # aktuellen Run überspringen + if str(run_number) == str(current_run_number): + print(" -> skipping current run") + continue - previous = conclusion or "unknown" + # nur abgeschlossene Runs + if status != "completed": + print(" -> skipping non-completed run") + continue - print(f" -> selected previous conclusion: {previous}") - break + # nur gleicher Branch + if branch != current_branch: + print(" -> skipping different branch") + continue + + # nur gleiches Event + if event != current_event: + print(" -> skipping different event") + continue + + previous = conclusion or "unknown" + print(f" -> selected previous conclusion: {previous}") + break + + if previous != "unknown" or len(runs) < 100: + break print(f"Previous conclusion final: {previous}") @@ -170,4 +174,4 @@ jobs: -H "Authorization: Bearer ${{ secrets.NTFY_TOKEN }}" \ -H "Content-Type: application/json" \ -d @/tmp/ntfy-payload.json \ - "${{ secrets.NTFY_SERVER }}" \ No newline at end of file + "${{ secrets.NTFY_SERVER }}" diff --git a/.gitea/workflows/python-checks.yml b/.gitea/workflows/python-checks.yml index ed9b98a..b1661ce 100644 --- a/.gitea/workflows/python-checks.yml +++ b/.gitea/workflows/python-checks.yml @@ -22,8 +22,10 @@ on: type: boolean default: true use_private_index: - type: boolean - default: false + # Gitea 1.27.x does not reliably propagate boolean inputs through + # reusable workflows. Keep this a string until that regression is fixed. + type: string + default: "false" jobs: check: @@ -38,15 +40,54 @@ jobs: - name: Checkout uses: actions/checkout@v4 - - name: Configure private package index - if: ${{ inputs.use_private_index }} + # Keep this step unconditional. A Gitea Actions update can otherwise silently + # skip it when a boolean workflow_call input is not propagated as expected; + # pip then misleadingly reports that the private package does not exist. + - name: Configure and verify private package index env: + PRIVATE_INDEX_ENABLED: ${{ inputs.use_private_index }} PRIVATE_INDEX_URL: ${{ secrets.PIP_EXTRA_INDEX_URL }} run: | + echo "Private package index requested: ${PRIVATE_INDEX_ENABLED:-}" + if [ "${PRIVATE_INDEX_ENABLED}" != "true" ]; then + echo "Private package index is not requested for this workflow call." + exit 0 + fi if [ -z "$PRIVATE_INDEX_URL" ]; then echo "use_private_index=true but the PIP_EXTRA_INDEX_URL secret is empty or not set" exit 1 fi + + # Probe the configured simple index before pip resolves dependencies. The + # probe deliberately prints only scheme/host/path, never URL credentials. + python - "$PRIVATE_INDEX_URL" <<'PY' + import base64 + import sys + from urllib.error import HTTPError, URLError + from urllib.parse import unquote, urlsplit, urlunsplit + from urllib.request import Request, urlopen + + configured_url = sys.argv[1] + parsed = urlsplit(configured_url) + hostname = parsed.hostname or "" + port = f":{parsed.port}" if parsed.port else "" + safe_url = urlunsplit((parsed.scheme, f"{hostname}{port}", parsed.path, parsed.query, "")) + request = Request(safe_url, headers={"Accept": "application/vnd.pypi.simple.v1+json"}) + if parsed.username is not None: + credentials = f"{unquote(parsed.username)}:{unquote(parsed.password or '')}".encode() + request.add_header("Authorization", "Basic " + base64.b64encode(credentials).decode()) + try: + with urlopen(request, timeout=15) as response: # nosec B310 -- configured CI package index + print(f"Private package-index probe: HTTP {response.status} at {safe_url}") + except HTTPError as error: + print(f"Private package-index probe: HTTP {error.code} at {safe_url}") + if error.code in {401, 403}: + print("Authentication was rejected by the private package index.") + raise SystemExit(1) + except URLError as error: + print(f"Private package-index probe could not reach {safe_url}: {error.reason}") + raise SystemExit(1) + PY pip config --site set global.extra-index-url "$PRIVATE_INDEX_URL" - name: Install Tools & Deps @@ -86,4 +127,4 @@ jobs: with: name: coverage-xml path: coverage.xml - if-no-files-found: warn \ No newline at end of file + if-no-files-found: warn diff --git a/.gitea/workflows/python-package-publish.yml b/.gitea/workflows/python-package-publish.yml index 3e0c79e..d731c63 100644 --- a/.gitea/workflows/python-package-publish.yml +++ b/.gitea/workflows/python-package-publish.yml @@ -17,8 +17,10 @@ on: type: string use_private_index: # build backends that need the in-house BB* packages to resolve build dependencies - type: boolean - default: false + # Gitea 1.27.x does not reliably propagate boolean inputs through + # reusable workflows. Callers pass the literal string "true". + type: string + default: "false" secrets: REGISTRY_USERNAME: { required: true } REGISTRY_PASSWORD: { required: true }