-
Notifications
You must be signed in to change notification settings - Fork 25
feat: extract CI lifecycle and Prow job management skills from openshift/release #25
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Merged
Merged
Changes from 24 commits
Commits
Show all changes
27 commits
Select commit
Hold shift + click to select a range
a386e38
feat: extract CI lifecycle and Prow job management skills from opensh…
zdrapela 3ed1915
fix: mark pre-commit hook as executable in git index
zdrapela 7e5a2b5
refactor: add shared Python modules for repo resolution and OCP lifec…
zdrapela 8531321
refactor: rewrite lifecycle-aks and lifecycle-eks scripts in Python
zdrapela 46a7f2c
refactor: rewrite K8s test config listing scripts in Python
zdrapela 80196a3
refactor: rewrite prow-ocp-jobs scripts in Python
zdrapela 4ff58cc
refactor: rewrite prow-ocp-pools scripts in Python
zdrapela be3431a
refactor: rewrite prow-ocp-coverage analysis script in Python
zdrapela 8784234
docs: update SKILL.md files to reference Python scripts
zdrapela 09752d2
refactor: remove bash/jq scripts replaced by Python equivalents
zdrapela 9a477cb
fix: use relative script paths per Agent Skills spec
zdrapela 1176606
fix: unify all scripts to use uv run for consistent invocation
zdrapela a4dd8c6
feat: add lifecycle-rhdh skill and split lifecycle-ocp into focused s…
zdrapela 13a5d61
refactor: unify Red Hat Product Life Cycles API access in redhat_life…
zdrapela 4e78ad1
feat: add lifecycle-redhat skill for generic Red Hat product lifecycl…
zdrapela 9796bc1
feat: add lifecycle-pg skill for PostgreSQL lifecycle across cloud pr…
zdrapela 7b87715
feat: add --json flag to AKS and EKS lifecycle scripts
zdrapela b14ec7d
refactor: split _shared/ flat modules into rhdh_lifecycle and rhdh_pr…
zdrapela d98e8f0
refactor: move pg_lifecycle into lifecycle-pg skill, remove dead code
zdrapela 385f50d
fix: address PR review comments on shared library code
zdrapela e987168
refactor: consolidate 14 skills into 2 with workflow routing
zdrapela bcdb0af
fix: remove redundant sys.path.insert from all scripts
zdrapela e489b60
fix: address skill-maker review findings
zdrapela 3822c23
refactor: make lifecycle the canonical owner of repo/YAML utilities
zdrapela 56e08bf
fix: address durandom review feedback
zdrapela 8a51130
refactor: deduplicate fetch_json, EOL filtering, and version sort
zdrapela d4b252e
refactor: extract generic utilities into utils.py modules
zdrapela File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
Empty file.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| --- | ||
| name: lifecycle | ||
| description: >- | ||
| Check version lifecycle and support status for platforms and integrations | ||
| used by RHDH. Covers OCP, AKS, EKS, GKE, RHDH releases, RHBK, Quay, | ||
| PostgreSQL, and any Red Hat product via the Product Life Cycles API. | ||
| Use when asking about version support, EOL dates, GA dates, support | ||
| phases, or planning version upgrades. Also use for "is X still | ||
| supported", "what versions should we test", or "when does X reach EOL". | ||
| --- | ||
| # Version Lifecycle Checks | ||
|
|
||
| Check version lifecycle and support status for platforms and integrations used by RHDH. | ||
|
|
||
| ## Prerequisites | ||
|
|
||
| - Python 3.9+ | ||
| - Internet connectivity for API access | ||
| - For configured K8s version display (AKS/EKS): local `openshift/release` checkout or `gh` CLI | ||
|
|
||
| ## Identify Platform | ||
|
|
||
| What platform or integration lifecycle do you need to check? | ||
|
|
||
| | Query matches | Workflow | | ||
| |---|---| | ||
| | "OCP", "OpenShift version", "OpenShift EOL", "OpenShift support" | `workflows/check-ocp.md` | | ||
| | "RHDH version", "Developer Hub release", "is RHDH 1.x supported" | `workflows/check-rhdh.md` | | ||
| | "AKS", "Azure Kubernetes" | `workflows/check-aks.md` | | ||
| | "EKS", "Amazon EKS" | `workflows/check-eks.md` | | ||
| | "GKE", "Google Kubernetes" | `workflows/check-gke.md` | | ||
| | "RHBK", "Keycloak", "Red Hat Build of Keycloak", "Quay", any Red Hat product | `workflows/check-redhat.md` | | ||
| | "PostgreSQL", "Postgres", "PG", "database versions" | `workflows/check-pg.md` | | ||
| | "all platforms", "full lifecycle check" | Run all workflows in sequence | | ||
|
|
||
| After reading the workflow, follow it exactly. | ||
|
|
||
| ## Available Scripts | ||
|
|
||
| All scripts support `--help` for usage details and `--json` for structured output. | ||
|
|
||
| | Script | Purpose | | ||
| |--------|---------| | ||
| | `scripts/check_ocp_lifecycle.py` | OCP version lifecycle with EUS phases | | ||
| | `scripts/check_rhdh_lifecycle.py` | RHDH release lifecycle with OCP compatibility | | ||
| | `scripts/check_lifecycle.py` | Generic Red Hat product (RHBK, Quay, etc.) | | ||
| | `scripts/check_aks_lifecycle.py` | AKS K8s version lifecycle | | ||
| | `scripts/check_eks_lifecycle.py` | EKS K8s version lifecycle | | ||
| | `scripts/check_gke_lifecycle.py` | GKE K8s version lifecycle | | ||
| | `scripts/check_pg_lifecycle.py` | PostgreSQL lifecycle across cloud providers | | ||
|
|
||
| ## Library (`rhdh_lifecycle` package) | ||
|
|
||
| Shared utilities used by both lifecycle and prow skills: | ||
|
|
||
| | Module | Purpose | | ||
| |--------|---------| | ||
| | `rhdh_lifecycle.repo` | Resolve openshift/release repository root (local or remote) | | ||
| | `rhdh_lifecycle.yaml` | Read and parse YAML files from openshift/release | | ||
| | `rhdh_lifecycle.configured_versions` | Print configured K8s versions per branch | | ||
| | `rhdh_lifecycle.redhat` | Red Hat Product Life Cycles API client | | ||
| | `rhdh_lifecycle.ocp` | OCP version phase classification | | ||
| | `rhdh_lifecycle.rhdh` | RHDH release lifecycle data | | ||
| | `rhdh_lifecycle.pg` | PostgreSQL lifecycle across cloud providers | | ||
|
|
||
| ## Related Skills | ||
|
|
||
| - **`prow`**: Manage Prow CI job configurations for RHDH in openshift/release |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,163 @@ | ||
| #!/usr/bin/env -S uv run --script | ||
| # /// script | ||
| # requires-python = ">=3.9" | ||
| # dependencies = ["ruamel.yaml"] | ||
| # /// | ||
| """Check AKS Kubernetes version lifecycle using the official AKS release status API. | ||
|
|
||
| Primary source: https://releases.aks.azure.com/parsed_data.json | ||
| Cross-verify: https://endoflife.date/api/azure-kubernetes-service.json | ||
| """ | ||
|
|
||
| from __future__ import annotations | ||
|
|
||
| import argparse | ||
| import json | ||
| import sys | ||
| import urllib.error | ||
| import urllib.request | ||
| from datetime import datetime, timezone | ||
|
|
||
| from rhdh_lifecycle.configured_versions import print_configured_versions | ||
| from rhdh_lifecycle.repo import resolve_repo_root | ||
|
|
||
| AKS_API_URL = "https://releases.aks.azure.com/parsed_data.json" | ||
| EOL_API_URL = "https://endoflife.date/api/azure-kubernetes-service.json" | ||
| CONFIG_DIR = "ci-operator/config/redhat-developer/rhdh" | ||
|
|
||
|
|
||
| def fetch_json(url): | ||
| """Fetch JSON from a URL.""" | ||
| req = urllib.request.Request(url, headers={"User-Agent": "rhdh-skill"}) | ||
| try: | ||
| with urllib.request.urlopen(req, timeout=30) as resp: | ||
| return json.loads(resp.read().decode("utf-8")) | ||
| except (urllib.error.URLError, OSError) as exc: | ||
| print(f"ERROR: Failed to fetch {url}: {exc}", file=sys.stderr) | ||
| return None | ||
|
|
||
|
|
||
| def main(argv=None): | ||
| parser = argparse.ArgumentParser(description="Check AKS K8s version lifecycle.") | ||
| parser.add_argument("--mapt-ref", help="Path to MAPT ref YAML (repo-relative)") | ||
| parser.add_argument("--test-pattern", help="Regex to match test names") | ||
| parser.add_argument("--config-dir", default=CONFIG_DIR, help="CI config directory") | ||
| parser.add_argument("--repo-dir", help="Path to openshift/release checkout") | ||
| parser.add_argument("--json", dest="json_output", action="store_true", help="Output as JSON") | ||
| args = parser.parse_args(argv) | ||
|
|
||
| root, is_remote = resolve_repo_root(args.repo_dir) | ||
|
|
||
| # Print configured versions if test pattern provided | ||
| if args.test_pattern: | ||
| print_configured_versions( | ||
| args.config_dir, args.test_pattern, root, is_remote, args.mapt_ref | ||
| ) | ||
|
|
||
| # Fetch AKS release data (primary source) | ||
| data = fetch_json(AKS_API_URL) | ||
| if not data: | ||
| sys.exit(1) | ||
|
|
||
| # Extract supported versions from the first region | ||
| try: | ||
| regional = data["Sections"]["KubernetesSupportedVersions"]["Components"][ | ||
| "KubernetesVersions" | ||
| ]["RegionalStatuses"] | ||
| first_region = list(regional.values())[0][0]["Current"]["KubernetesVersionList"] | ||
| except (KeyError, IndexError, TypeError): | ||
| print("ERROR: Unexpected AKS API response structure", file=sys.stderr) | ||
| sys.exit(1) | ||
|
|
||
| # Group by minor version | ||
| minor_versions = {} | ||
| for entry in first_region: | ||
| parts = entry["VersionName"].split(".") | ||
| minor = f"{parts[0]}.{parts[1]}" | ||
| if minor not in minor_versions: | ||
| minor_versions[minor] = {"is_lts": False, "is_preview": False} | ||
| if entry.get("IsLTS"): | ||
| minor_versions[minor]["is_lts"] = True | ||
| if entry.get("IsPreview"): | ||
| minor_versions[minor]["is_preview"] = True | ||
|
|
||
| sorted_versions = sorted( | ||
| minor_versions.items(), | ||
| key=lambda x: [int(n) for n in x[0].split(".")], | ||
| reverse=True, | ||
| ) | ||
|
|
||
| # Deprecated version | ||
| try: | ||
| deprecated = regional[list(regional.keys())[0]][0]["Current"].get( | ||
| "DeprecatedVersion", "N/A" | ||
| ) | ||
| except (KeyError, IndexError): | ||
| deprecated = "N/A" | ||
|
|
||
| # Cross-verify with endoflife.date | ||
|
zdrapela marked this conversation as resolved.
|
||
| today = datetime.now(timezone.utc).strftime("%Y-%m-%d") | ||
| eol_data = fetch_json(EOL_API_URL) | ||
| eol_supported = [] | ||
| if eol_data: | ||
| for entry in eol_data: | ||
| eol = entry.get("eol", "N/A") | ||
| ext = entry.get("extendedSupport", "N/A") | ||
| has_support = False | ||
| if eol == "N/A": | ||
| has_support = True | ||
| elif isinstance(eol, bool): | ||
| has_support = not eol | ||
| elif isinstance(eol, str) and eol > today: | ||
| has_support = True | ||
| if not has_support and isinstance(ext, str) and ext > today: | ||
| has_support = True | ||
| if has_support: | ||
| eol_supported.append(entry) | ||
| eol_supported.sort(key=lambda e: [int(x) for x in e["cycle"].split(".")], reverse=True) | ||
|
zdrapela marked this conversation as resolved.
Outdated
|
||
|
|
||
| # JSON output | ||
| if args.json_output: | ||
| result = { | ||
| "versions": [ | ||
| { | ||
| "version": ver, | ||
| "status": "LTS" | ||
| if info["is_lts"] | ||
| else ("Preview" if info["is_preview"] else "GA"), | ||
| } | ||
| for ver, info in sorted_versions | ||
| ], | ||
| "deprecated": deprecated, | ||
| "endoflife_date": [ | ||
| {"version": e["cycle"], "eol": str(e.get("eol", "N/A"))} for e in eol_supported | ||
| ], | ||
| } | ||
| json.dump(result, sys.stdout, indent=2) | ||
| print() | ||
| return | ||
|
|
||
| # Human-readable output | ||
| print("=== AKS Release Status (releases.aks.azure.com) ===") | ||
| print("Supported minor versions (newest first):") | ||
| for ver, info in sorted_versions: | ||
| if info["is_lts"]: | ||
| status = "LTS" | ||
| elif info["is_preview"]: | ||
| status = "Preview" | ||
| else: | ||
| status = "GA" | ||
| print(f" {ver:<8s} {status}") | ||
| print(f"Recently deprecated: {deprecated}") | ||
|
|
||
| print() | ||
| print("=== Cross-verify (endoflife.date) ===") | ||
| if not eol_data: | ||
| print("WARNING: Failed to fetch endoflife.date", file=sys.stderr) | ||
| return | ||
| for entry in eol_supported: | ||
| print(f" {entry['cycle']}\tEOL: {entry.get('eol', 'N/A')}") | ||
|
|
||
|
|
||
| if __name__ == "__main__": | ||
| main() | ||
Oops, something went wrong.
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
Uh oh!
There was an error while loading. Please reload this page.