Skip to content

add script to generate prow status for dev jobs - #67

Open
weshayutin wants to merge 1 commit into
medik8s:mainfrom
weshayutin:devstatus
Open

add script to generate prow status for dev jobs#67
weshayutin wants to merge 1 commit into
medik8s:mainfrom
weshayutin:devstatus

Conversation

@weshayutin

@weshayutin weshayutin commented Aug 5, 2026

Copy link
Copy Markdown

update the wiki page for the status of prow jobs in dev e2e

Summary by CodeRabbit

  • New Features
    • Added automated CI status data generation across operators, branches, and platform versions.
    • Reports the latest completed job results, including pending, missing, unknown, and lookup-error statuses.
    • Supports concurrent status retrieval for faster results.
    • Added command-line options for custom output locations and worker counts.
    • Generates structured data and status files for downstream CI status displays.

Signed-off-by: Wesley Hayutin <weshayutin@gmail.com>
@openshift-ci

openshift-ci Bot commented Aug 5, 2026

Copy link
Copy Markdown

[APPROVALNOTIFIER] This PR is APPROVED

This pull-request has been approved by: weshayutin

The full list of commands accepted by this bot can be found here.

The pull request process is described here

Details Needs approval from an approver in each of these files:

Approvers can indicate their approval by writing /approve in a comment
Approvers can cancel approval by writing /approve cancel in a comment

@openshift-ci
openshift-ci Bot requested review from beekhof and jmontleon August 5, 2026 00:55
@openshift-ci openshift-ci Bot added the approved label Aug 5, 2026
@coderabbitai

coderabbitai Bot commented Aug 5, 2026

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

Adds a standalone script that discovers Medik8s CI configurations, generates presubmit job records, retrieves results from GCS concurrently, and writes data.json and statuses.json through a configurable CLI.

Changes

CI status generation

Layer / File(s) Summary
Configuration discovery and job generation
scripts/ci-status/generate_data.py
Defines operator metadata, discovers GitHub configuration files, sorts branch versions, and generates presubmit openshift-e2e job names.
GCS status collection
scripts/ci-status/generate_data.py
Adds HTTP and GCS lookups for build results, classifies missing, pending, completed, and failed lookups, and collects statuses concurrently.
CLI output generation
scripts/ci-status/generate_data.py
Adds configurable output and worker options, writes JSON files, reports aggregate state counts, and supports direct script execution.

Estimated code review effort: 3 (Moderate) | ~20 minutes

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly summarizes the main change: adding a script to generate Prow status information for development jobs.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@qodo-2-for-medik8s

Copy link
Copy Markdown

PR Summary by Qodo

Add script to generate durable CI status data for dev Prow jobs

✨ Enhancement 🕐 20-40 Minutes

Grey Divider

AI Description

• Discover medik8s presubmit openshift-e2e jobs from openshift/release config.
• Query GCS directly for each job’s latest completed result (durable status signal).
• Emit JSON inputs for wiki CI-Status page rendering (job matrix + per-job status).
Diagram

graph TD
  A["generate_data.py"] --> B{{"GitHub API"}} --> C[("data.json")]
  A --> D{{"GCS test-platform-results"}} --> E[("statuses.json")]
  C --> F["render_markdown.py"] --> G["Wiki CI-Status"]
  E --> F

  subgraph Legend
    direction LR
    _tool["Tool/Script"] ~~~ _ext{{"External system"}} ~~~ _art[("Artifact")]
  end
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Use Prow /badge.svg for status
  • ➕ Very simple implementation; no GCS parsing
  • ➕ No need to discover finished.json paths
  • ➖ Not durable for presubmits due to ProwJob GC (badge reflects live objects only)
  • ➖ Can misrepresent 'latest completed' status, which is the page’s goal
2. Use TestGrid API as the source of truth
  • ➕ Purpose-built for job result history and dashboards
  • ➕ Avoids bucket-specific assumptions about file layout
  • ➖ May not cover all job variants consistently (depends on dashboard configuration)
  • ➖ Additional mapping needed from job names to dashboard tabs
3. Scrape Prow UI HTML/JSON endpoints
  • ➕ Could avoid dealing with GCS metadata headers
  • ➕ May provide normalized job/run metadata
  • ➖ More brittle (UI/API changes), and may still not provide stable 'latest completed' signal
  • ➖ Often rate-limited / less cache-friendly than reading public artifacts

Recommendation: Keep the current approach (GitHub config discovery + direct GCS lookup). It matches the stated durability requirement for presubmit “latest status” and stays dependency-free. If brittleness around the GCS x-goog-meta link header becomes an issue, consider switching to a TestGrid-backed solution next.

Files changed (1) +207 / -0

Enhancement (1) +207 / -0
generate_data.pyAdd CI-status data generator (GitHub discovery + GCS latest-run lookup) +207/-0

Add CI-status data generator (GitHub discovery + GCS latest-run lookup)

• Introduces a standalone Python script that discovers medik8s openshift-e2e presubmit jobs by listing openshift/release ci-operator config files via the GitHub API, then queries the test-platform-results GCS bucket for each job’s latest completed run. Writes data.json (job matrix) and statuses.json (per-job latest result) for downstream markdown/wiki rendering.

scripts/ci-status/generate_data.py

@qodo-2-for-medik8s

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (3) 📘 Rule violations (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Wrong GCS header lookup 🐞 Bug ≡ Correctness
Description
fetch_status() documents reading the x-goog-meta-link header but the code looks up
x-goog-meta-x-goog-meta-link, so it will typically fail to find the link and return
state=unknown instead of reading finished.json. This undermines the script’s primary purpose by
producing mostly unknown statuses.
Code

scripts/ci-status/generate_data.py[R142-145]

+        link = next(
+            (v for k, v in headers.items() if k.lower() == "x-goog-meta-x-goog-meta-link"),
+            None,
+        )
Relevance

●●● Strong

Deterministic correctness bug (header typo) that breaks script’s main status resolution path.

PR-#25

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code claims it will use the x-goog-meta-link header to locate finished.json, but it actually
searches for a different header key and returns unknown if not found, preventing status
resolution.

scripts/ci-status/generate_data.py[128-152]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`fetch_status()` expects to retrieve a GCS metadata header named `x-goog-meta-link`, but the implementation searches for `x-goog-meta-x-goog-meta-link`. This mismatch prevents resolving the `gs://...` link to the build directory, leading to `state: unknown` and skipping `finished.json`.

### Issue Context
The docstring explicitly describes the header as `x-goog-meta-link`, and the code’s fallback path returns `unknown` when the lookup fails.

### Fix Focus Areas
- scripts/ci-status/generate_data.py[128-147]

### Suggested fix
- Change the header comparison to `k.lower() == "x-goog-meta-link"`.
- Optionally support both keys (try `x-goog-meta-link` first, then `x-goog-meta-x-goog-meta-link`) to be resilient if the upstream metadata key differs.
- Consider including the available header keys in the `unknown` state (or stderr log) to simplify debugging.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

2. HTTP errors misclassified 🐞 Bug ☼ Reliability
Description
_get_text()/_head() convert all HTTPError responses into None, which fetch_status() then
treats as normal states like no-history or pending. This will silently misreport statuses on
non-404 HTTP failures (e.g., 403/429/5xx) instead of surfacing an error state.
Code

scripts/ci-status/generate_data.py[R106-111]

+def _get_text(url):
+    try:
+        with urllib.request.urlopen(url, timeout=15) as resp:
+            return resp.read().decode()
+    except urllib.error.HTTPError:
+        return None
Relevance

●●● Strong

Team has accepted fixes preventing silent false states from swallowed errors/vacuous success.

PR-#39
PR-#25

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The helpers explicitly swallow all HTTPError conditions and return None, and fetch_status()
converts those None values into semantically meaningful states, which will be incorrect for
non-404 failures like rate limiting or server errors.

scripts/ci-status/generate_data.py[106-112]
scripts/ci-status/generate_data.py[119-126]
scripts/ci-status/generate_data.py[134-152]
PR-#39

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
The helpers `_get_text()` and `_head()` catch any `urllib.error.HTTPError` and return `None`. Callers treat `None` as meaningful absence ("no-history" / "pending"), which is only valid for 404-like "not found" situations. Non-404 HTTP errors (rate limiting, permission issues, transient 5xx) should not be silently reinterpreted as normal job states.

### Issue Context
- `_get_text()` returns `None` for any `HTTPError`.
- `fetch_status()` maps `latest-build.txt`/HEAD failures to `state: no-history` and `finished.json` failure to `state: pending`.

### Fix Focus Areas
- scripts/ci-status/generate_data.py[106-112]
- scripts/ci-status/generate_data.py[119-126]
- scripts/ci-status/generate_data.py[134-152]

### Suggested fix
- In `_get_text()` / `_head()`, catch `HTTPError as e` and:
 - return `None` only when `e.code == 404` (and possibly 410),
 - otherwise re-raise so `fetch_status()` returns `state: error` with details, or explicitly return a structured error object.
- Optionally capture and include `e.code` and a short body snippet in the returned error to improve debuggability.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Informational

3. Workers arg can crash 🐞 Bug ☼ Reliability
Description
--workers accepts zero/negative values and is passed directly to
ThreadPoolExecutor(max_workers=workers), which raises ValueError and terminates with a
traceback. This makes the script fragile in automation if the argument is mis-set.
Code

scripts/ci-status/generate_data.py[R166-167]

+    with ThreadPoolExecutor(max_workers=workers) as ex:
+        futs = {ex.submit(fetch_status, j): j for j in jobs}
Relevance

●●● Strong

They commonly accept adding precondition/guard checks to avoid runtime crashes and make automation
robust.

PR-#52

ⓘ Recommendations generated based on similar findings in past PRs

Evidence
The code passes the raw parsed --workers value into ThreadPoolExecutor, which is known to reject
non-positive values, causing a runtime exception instead of a controlled CLI error.

scripts/ci-status/generate_data.py[164-170]
scripts/ci-status/generate_data.py[180-181]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

### Issue description
`ThreadPoolExecutor` requires `max_workers >= 1`, but the CLI allows `--workers 0` or negative values, which will crash the script.

### Issue Context
`--workers` is parsed as an int and passed unvalidated into `ThreadPoolExecutor(max_workers=workers)`.

### Fix Focus Areas
- scripts/ci-status/generate_data.py[164-170]
- scripts/ci-status/generate_data.py[173-182]

### Suggested fix
- Validate after parsing and call `parser.error("--workers must be >= 1")` if invalid, or
- Use a custom `type=` function in argparse that enforces positivity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Context used
✅ Compliance rules (platform): 13 rules

To customize comments, go to the Qodo configuration screen, or learn more in the docs.

Qodo Logo

Comment on lines +142 to +145
link = next(
(v for k, v in headers.items() if k.lower() == "x-goog-meta-x-goog-meta-link"),
None,
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Action required

1. Wrong gcs header lookup 🐞 Bug ≡ Correctness

fetch_status() documents reading the x-goog-meta-link header but the code looks up
x-goog-meta-x-goog-meta-link, so it will typically fail to find the link and return
state=unknown instead of reading finished.json. This undermines the script’s primary purpose by
producing mostly unknown statuses.
Agent Prompt
### Issue description
`fetch_status()` expects to retrieve a GCS metadata header named `x-goog-meta-link`, but the implementation searches for `x-goog-meta-x-goog-meta-link`. This mismatch prevents resolving the `gs://...` link to the build directory, leading to `state: unknown` and skipping `finished.json`.

### Issue Context
The docstring explicitly describes the header as `x-goog-meta-link`, and the code’s fallback path returns `unknown` when the lookup fails.

### Fix Focus Areas
- scripts/ci-status/generate_data.py[128-147]

### Suggested fix
- Change the header comparison to `k.lower() == "x-goog-meta-link"`.
- Optionally support both keys (try `x-goog-meta-link` first, then `x-goog-meta-x-goog-meta-link`) to be resilient if the upstream metadata key differs.
- Consider including the available header keys in the `unknown` state (or stderr log) to simplify debugging.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +106 to +111
def _get_text(url):
try:
with urllib.request.urlopen(url, timeout=15) as resp:
return resp.read().decode()
except urllib.error.HTTPError:
return None

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Remediation recommended

2. Http errors misclassified 🐞 Bug ☼ Reliability

_get_text()/_head() convert all HTTPError responses into None, which fetch_status() then
treats as normal states like no-history or pending. This will silently misreport statuses on
non-404 HTTP failures (e.g., 403/429/5xx) instead of surfacing an error state.
Agent Prompt
### Issue description
The helpers `_get_text()` and `_head()` catch any `urllib.error.HTTPError` and return `None`. Callers treat `None` as meaningful absence ("no-history" / "pending"), which is only valid for 404-like "not found" situations. Non-404 HTTP errors (rate limiting, permission issues, transient 5xx) should not be silently reinterpreted as normal job states.

### Issue Context
- `_get_text()` returns `None` for any `HTTPError`.
- `fetch_status()` maps `latest-build.txt`/HEAD failures to `state: no-history` and `finished.json` failure to `state: pending`.

### Fix Focus Areas
- scripts/ci-status/generate_data.py[106-112]
- scripts/ci-status/generate_data.py[119-126]
- scripts/ci-status/generate_data.py[134-152]

### Suggested fix
- In `_get_text()` / `_head()`, catch `HTTPError as e` and:
  - return `None` only when `e.code == 404` (and possibly 410),
  - otherwise re-raise so `fetch_status()` returns `state: error` with details, or explicitly return a structured error object.
- Optionally capture and include `e.code` and a short body snippet in the returned error to improve debuggability.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment on lines +166 to +167
with ThreadPoolExecutor(max_workers=workers) as ex:
futs = {ex.submit(fetch_status, j): j for j in jobs}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Informational

3. Workers arg can crash 🐞 Bug ☼ Reliability

--workers accepts zero/negative values and is passed directly to
ThreadPoolExecutor(max_workers=workers), which raises ValueError and terminates with a
traceback. This makes the script fragile in automation if the argument is mis-set.
Agent Prompt
### Issue description
`ThreadPoolExecutor` requires `max_workers >= 1`, but the CLI allows `--workers 0` or negative values, which will crash the script.

### Issue Context
`--workers` is parsed as an int and passed unvalidated into `ThreadPoolExecutor(max_workers=workers)`.

### Fix Focus Areas
- scripts/ci-status/generate_data.py[164-170]
- scripts/ci-status/generate_data.py[173-182]

### Suggested fix
- Validate after parsing and call `parser.error("--workers must be >= 1")` if invalid, or
- Use a custom `type=` function in argparse that enforces positivity.

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@scripts/ci-status/generate_data.py`:
- Around line 49-51: Update FNAME_RE to accept operator release branch names
such as release-0.10 by matching the known dirname prefix and capturing the
branch token through the __ delimiter, while preserving main and existing
release branch matches and the repo, variant, and YAML filename structure.
- Around line 180-181: Validate args.workers immediately after
parser.parse_args() in the argument-parsing flow, and call parser.error when the
value is less than 1 so only positive worker counts reach ThreadPoolExecutor.
🪄 Autofix

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro Plus

Run ID: e9b1332e-4a1f-4db6-8d39-24014fcd1668

📥 Commits

Reviewing files that changed from the base of the PR and between 11c924d and 2bc2861.

📒 Files selected for processing (1)
  • scripts/ci-status/generate_data.py

Comment on lines +49 to +51
FNAME_RE = re.compile(
r"^medik8s-(?P<repo>.+)-(?P<branch>main|release-[0-9]+\.[0-9]+)__(?P<variant>[0-9]+\.[0-9]+)\.yaml$"
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Accept operator release branch names.

FNAME_RE accepts only main and release-<OCP version>. It rejects operator branches such as release-0.10. Current Prow history includes pull-ci-medik8s-node-healthcheck-operator-release-0.10-4.16-openshift-e2e. (deck-ci.apps.ci.l2s4.p1.openshiftapps.com)

Lines 83-86 then skip these configurations. The generated matrix omits valid E2E jobs. Match the known dirname prefix and capture the branch token up to __.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci-status/generate_data.py` around lines 49 - 51, Update FNAME_RE to
accept operator release branch names such as release-0.10 by matching the known
dirname prefix and capturing the branch token through the __ delimiter, while
preserving main and existing release branch matches and the repo, variant, and
YAML filename structure.

Comment on lines +180 to +181
parser.add_argument("--workers", type=int, default=16, help="concurrent GCS status lookups (default: 16)")
args = parser.parse_args()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
from concurrent.futures import ThreadPoolExecutor

try:
    ThreadPoolExecutor(max_workers=0)
except ValueError:
    print("confirmed: non-positive max_workers is invalid")
else:
    raise SystemExit("expected ValueError for max_workers=0")
PY

Repository: medik8s/system-tests

Length of output: 205


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant parser/workers setup and any existing validation/usages.
sed -n '150,210p' scripts/ci-status/generate_data.py
printf '\n--- occurrences of workers/max_workers ---\n'
rg -n "\b(workers|max_workers)\b|ThreadPoolExecutor" scripts/ci-status/generate_data.py

Repository: medik8s/system-tests

Length of output: 2721


Reject non-positive worker counts.

--workers 0 and negative values parse successfully, but ThreadPoolExecutor(max_workers=...) raises ValueError for non-positive values. Validate args.workers after parsing and call parser.error for values below 1.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@scripts/ci-status/generate_data.py` around lines 180 - 181, Validate
args.workers immediately after parser.parse_args() in the argument-parsing flow,
and call parser.error when the value is less than 1 so only positive worker
counts reach ThreadPoolExecutor.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant