Skip to content
Merged
Show file tree
Hide file tree
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 May 13, 2026
3ed1915
fix: mark pre-commit hook as executable in git index
zdrapela May 13, 2026
7e5a2b5
refactor: add shared Python modules for repo resolution and OCP lifec…
zdrapela May 13, 2026
8531321
refactor: rewrite lifecycle-aks and lifecycle-eks scripts in Python
zdrapela May 13, 2026
46a7f2c
refactor: rewrite K8s test config listing scripts in Python
zdrapela May 13, 2026
80196a3
refactor: rewrite prow-ocp-jobs scripts in Python
zdrapela May 13, 2026
4ff58cc
refactor: rewrite prow-ocp-pools scripts in Python
zdrapela May 13, 2026
be3431a
refactor: rewrite prow-ocp-coverage analysis script in Python
zdrapela May 13, 2026
8784234
docs: update SKILL.md files to reference Python scripts
zdrapela May 13, 2026
09752d2
refactor: remove bash/jq scripts replaced by Python equivalents
zdrapela May 13, 2026
9a477cb
fix: use relative script paths per Agent Skills spec
zdrapela May 13, 2026
1176606
fix: unify all scripts to use uv run for consistent invocation
zdrapela May 13, 2026
a4dd8c6
feat: add lifecycle-rhdh skill and split lifecycle-ocp into focused s…
zdrapela May 13, 2026
13a5d61
refactor: unify Red Hat Product Life Cycles API access in redhat_life…
zdrapela May 13, 2026
4e78ad1
feat: add lifecycle-redhat skill for generic Red Hat product lifecycl…
zdrapela May 13, 2026
9796bc1
feat: add lifecycle-pg skill for PostgreSQL lifecycle across cloud pr…
zdrapela May 13, 2026
7b87715
feat: add --json flag to AKS and EKS lifecycle scripts
zdrapela May 13, 2026
b14ec7d
refactor: split _shared/ flat modules into rhdh_lifecycle and rhdh_pr…
zdrapela May 13, 2026
d98e8f0
refactor: move pg_lifecycle into lifecycle-pg skill, remove dead code
zdrapela May 13, 2026
385f50d
fix: address PR review comments on shared library code
zdrapela May 15, 2026
e987168
refactor: consolidate 14 skills into 2 with workflow routing
zdrapela May 15, 2026
bcdb0af
fix: remove redundant sys.path.insert from all scripts
zdrapela May 15, 2026
e489b60
fix: address skill-maker review findings
zdrapela May 15, 2026
3822c23
refactor: make lifecycle the canonical owner of repo/YAML utilities
zdrapela May 15, 2026
56e08bf
fix: address durandom review feedback
zdrapela May 20, 2026
8a51130
refactor: deduplicate fetch_json, EOL filtering, and version sort
zdrapela May 20, 2026
d4b252e
refactor: extract generic utilities into utils.py modules
zdrapela May 20, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Empty file modified .githooks/pre-commit
100644 → 100755
Empty file.
68 changes: 68 additions & 0 deletions skills/lifecycle/SKILL.md
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
163 changes: 163 additions & 0 deletions skills/lifecycle/scripts/check_aks_lifecycle.py
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):
Comment thread
zdrapela marked this conversation as resolved.
Outdated
"""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
Comment thread
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)
Comment thread
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()
Loading
Loading