Skip to content
4 changes: 4 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,10 @@ Track work across the four RHDH Jira projects.

- **[rhdh-pr-review](./skills/rhdh-pr-review/SKILL.md)** — Test PR changes on a live RHDH cluster. Swaps CI images, verifies code changes, and reports findings.

### Test Plan

- **[rhdh-test-plan-review](./skills/rhdh-test-plan-review/SKILL.md)** — Reviews an RHDH test plan Jira ticket and suggests platform/integration version updates based on support lifecycle pages and RHDH release milestones

### Orchestration

- **[rhdh](./skills/rhdh/SKILL.md)** — Entry point and router. Detects your environment, runs `doctor` checks, maintains a cross-session worklog, and routes to the right skill. Start here if you're not sure what you need.
Expand Down
78 changes: 78 additions & 0 deletions skills/rhdh-test-plan-review/SKILL.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
---
name: rhdh-test-plan-review
description: |
Reviews an RHDH test plan Jira ticket and suggests platform/integration version updates based on support lifecycle pages and RHDH release milestones. Use when given an RHDH test plan Jira ticket ID to check which platform/integration versions to add or remove. Use when asked to "update test plan", "review test plan", "check platform versions in test plan", "review RHDH test plan", "what platforms should we test for RHDH X", or "update supported versions in test plan".
---

<essential_principles>

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

The workflow (Steps 1–8, ~300 lines) is inlined directly here. Other skills split this into SKILL.md (entry point with <intake>, <routing>, <reference_index>) and workflows/<name>.md (the actual process). This keeps SKILL.md scannable and the workflow separately readable.

Also missing the XML structure tags (<essential_principles>, <intake>, <routing>) that other skills use per the Agent Skills spec. See skills/rhdh-pr-review/SKILL.md or skills/overlay/SKILL.md for the pattern.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I have used the skill-maker skill. If this is a conceptual thing or a pattern we want to follow, would it be possible to reflect/embed those principles in the skill-maker skill?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Added routing & intake

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Hopefully resolved too. Following project patterns.

<principle name="no_changes_without_approval">
Never modify Jira tickets without explicit user approval. Collect all decisions first, summarize them, then ask whether to apply — direct update [d], comment [c], or discard [n].
</principle>

<principle name="version_tracking_rules">
Different platforms accumulate versions differently:
- **OCP**: accumulate all active versions
- **AKS, EKS, GKE, Quay**: single latest version only — replace, never accumulate
- **ARO, OSD, ROSA**: single version each, evaluated independently — replace if a newer version is GA before code_freeze
- **RHBK**: track major versions only (e.g., `26` not `26.0`); accumulate all active majors
- **PostgreSQL**: RHDH support policy is the baseline; Backstage-only versions require a Jira Feature ticket warning
</principle>

<principle name="milestone_cutoffs">
Add/remove decisions are based on `code_freeze` and `ga_date` from the RHDH schedule sheet — not today's date.
- **Add**: version GA date ≤ `code_freeze`
- **Remove**: version EOL date ≤ `ga_date`
</principle>

<principle name="adf_preservation">
Jira descriptions are ADF (nested JSON). When updating, modify only version strings and date cells inside existing table cells — never convert to plain text and back. Preserve all other ADF structure exactly.
</principle>

<principle name="token_safety">
Never read `.jira-token` into context. Always use shell substitution: `"$(cat "$TOKEN_FILE")"`.
</principle>

</essential_principles>

<intake>

## RHDH Test Plan Review

Provide a Jira ticket ID or URL (e.g., `RHIDP-8994`) to begin.

**Wait for response before proceeding.**

</intake>

<routing>

| Input | Workflow |
|-------|----------|
| Jira ticket ID or URL | Read `workflows/review-test-plan.md` and follow it |

</routing>

<reference_index>

| Reference | Purpose | Path |
|-----------|---------|------|
| sources | Lifecycle URLs and extraction guidance per platform/integration | `references/sources.md` |
| google-sheets-setup | One-time gcloud auth setup for schedule sheet access | `references/google-sheets-setup.md` |
| rhdh-jira auth | Jira REST API token setup and curl patterns | `~/.claude/skills/rhdh-jira/references/auth.md` |

</reference_index>

<success_criteria>

- [ ] RHDH version extracted from ticket
- [ ] Milestone dates fetched from schedule sheet
- [ ] All platform and integration versions checked against lifecycle sources
- [ ] Overview diff presented (key dates, platforms, integrations)
- [ ] Each proposed change reviewed interactively (a/k/e)
- [ ] User chose how to apply: [d] direct update, [c] comment, [n] discard
- [ ] If [d]: Jira description updated via REST API; child tasks offered one at a time
- [ ] If [c]: comment posted to ticket
- [ ] If [n]: confirmed no changes were made

</success_criteria>
42 changes: 42 additions & 0 deletions skills/rhdh-test-plan-review/references/google-sheets-setup.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
# Google Sheets API Setup

The skill accesses the RHDH schedule Google Sheet using your existing Google account via `gcloud`.

## Setup (one-time)

**Step 1: Install gcloud** (skip if already installed)

Follow [Install the Google Cloud CLI](https://cloud.google.com/sdk/docs/install) for your OS. Examples:

- **Linux**: package manager (`apt`/`yum`/`dnf`), [Snap](https://cloud.google.com/sdk/docs/downloads-snap), or tarball — install so `gcloud` is on your `PATH`.
- **macOS**: [Installer pkg](https://cloud.google.com/sdk/docs/install-sdk#mac), [Homebrew cask](https://formulae.brew.sh/cask/google-cloud-sdk), or tarball — install so `gcloud` is on your `PATH`.

The scripts invoke `gcloud` only via `PATH`. If `which gcloud` fails, fix your install or shell configuration before retrying.

**Step 2: Authenticate with Google Drive access**

```bash
gcloud auth login --enable-gdrive-access
```

This opens a browser window. Sign in with the Google account that has access to the RHDH schedule sheet.

**Step 3: Verify**

```bash
python scripts/check_gsheets.py
```

Expected output:
```
✓ gcloud auth token available
```

## Troubleshooting

| Symptom | Fix |
|---------|-----|
| `gcloud not found` / `gcloud not on PATH` | Install the SDK (see step 1) so `gcloud` is on your `PATH` |
| `No active gcloud account` | Run `gcloud auth login --enable-gdrive-access` |
| `403 Forbidden` when fetching sheet | Sign in with an account that has Viewer access to the sheet |
| Token expired | Run `gcloud auth login --enable-gdrive-access` again |
108 changes: 108 additions & 0 deletions skills/rhdh-test-plan-review/references/sources.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
# Lifecycle Sources

URLs and extraction guidance for each platform and integration. Load this file during Step 4 of the workflow.

## Platforms

### OCP (OpenShift Container Platform)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

And I have similar for OCP: openshift/release#78203 😅


**URL:** https://access.redhat.com/support/policy/updates/openshift
**What to extract:** Minor version GA dates and end-of-life dates from the life cycle policy table.

### OSD (OpenShift Dedicated)

**URL:** https://access.redhat.com/product-life-cycles/api/v1/products/?name=Red+Hat+OpenShift+Container+Platform
**What to extract:** OSD follows OCP version availability. Use OCP GA and EOL dates as a proxy — versions available on OSD typically lag OCP GA by a few weeks. Apply the same add/remove rules as OCP.

**Note:** The OSD-specific lifecycle page (`docs.openshift.com/dedicated`) returns 403 for automated access. The Red Hat lifecycle API has no OSD-specific entry beyond 4.13. OCP lifecycle data is the best available proxy.

### ROSA (Red Hat OpenShift Service on AWS)

**URL:** https://access.redhat.com/product-life-cycles/api/v1/products/?name=Red+Hat+OpenShift+Container+Platform
**What to extract:** ROSA follows OCP version availability. Use OCP GA and EOL dates as a proxy. Apply the same add/remove rules as OCP.

**Note:** The ROSA-specific lifecycle page (`docs.openshift.com/rosa`) returns 403 for automated access. The Red Hat lifecycle API has ROSA entries only up to 4.13 (classic architecture). OCP lifecycle data is the best available proxy for current versions.

### ARO (Azure Red Hat OpenShift)

**URL:** https://learn.microsoft.com/en-us/azure/openshift/support-lifecycle
**What to extract:** Kubernetes version GA dates and end of support (EOL) dates.

### AKS (Azure Kubernetes Service)

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Hi, I just finished a set of skills for lifecycle management of K8s jobs. I placed them in openshift/release repo, because that's where the jobs are defined, but now I'm not convinced it's the best place, because they cannot be used in combination with yours skill.

You can check them for inspiration: openshift/release#79129. I chose a more programmatic approach, finding API endpoints and parsing JSONs.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

My idea was mostly around keeping the Test Plan JIRA up to date for a given release and eventually having child tasks for suggested changes that are needed. As jira is the source of truth for our release execution, I wanted to have a skill that helps making sure we have valid coverage. But i see there is some overlap with yours. Is there any preference on what you would like to see should happen with the approaches?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Your approach is fine. In my skills, I used more API endpoints and JSON parsing in bash scripts, which is more token-efficient. Maybe I can extract the reusable skills for programmatically retrieving supported versions into this repository, so they can be reused by your skill.


**URL:** https://learn.microsoft.com/en-us/azure/aks/supported-kubernetes-versions?tabs=azure-cli#aks-kubernetes-release-calendar
**What to extract:** The AKS Kubernetes release calendar table — GA date and end of life date per minor Kubernetes version.

### EKS (Amazon Elastic Kubernetes Service)

**URL:** https://docs.aws.amazon.com/eks/latest/userguide/kubernetes-versions.html
**What to extract:** Kubernetes minor version release dates and end of standard support dates for EKS.

### GKE (Google Kubernetes Engine)

**URL:** https://cloud.google.com/kubernetes-engine/docs/release-schedule
**What to extract:** GKE release schedule table — GA date and end of life per minor Kubernetes version.

## Integrations

### RHDH PostgreSQL Support Policy (baseline)

**URL:** https://access.redhat.com/support/policy/updates/developerhub
**What to extract:** The list of PostgreSQL major versions officially supported by RHDH. This is the **authoritative baseline** — these versions are already confirmed for RHDH support. Use these as the starting point; do not suggest removing versions that are on this list unless they are also EOL across all three providers.

### Backstage PostgreSQL Support Policy (candidate source)

**URL:** https://backstage.io/docs/overview/versioning-policy/#postgresql-releases
**What to extract:** The PostgreSQL versions Backstage currently supports (rolling window, typically last 5 major versions). Any version Backstage supports but that is NOT on the RHDH support policy page is a **candidate to suggest adding**, with the following mandatory warning:

> ⚠ Adding a new PostgreSQL version to RHDH requires a dedicated RHDH Jira Feature ticket to formally extend database support. Do not add this version without one.

### Amazon RDS for PostgreSQL

**URL:** https://docs.aws.amazon.com/AmazonRDS/latest/PostgreSQLReleaseNotes/postgresql-versions.html
**What to extract:** PostgreSQL major version end of support (EOL) dates on Amazon RDS.

### Azure Database for PostgreSQL

**URL:** https://learn.microsoft.com/en-us/azure/postgresql/flexible-server/concepts-supported-versions
**What to extract:** PostgreSQL major version support status and end of support dates for Azure Database Flexible Server.

### CloudSQL for PostgreSQL

**URL:** https://cloud.google.com/sql/postgresql
**What to extract:** PostgreSQL major version end of support dates for Google CloudSQL. The page may list supported versions in a table or prose — look for "end of support" or "EOL" next to each major version.

### RHBK (Red Hat Build of Keycloak)

**URL:** https://access.redhat.com/product-life-cycles/api/v1/products/?name=Red+Hat+build+of+Keycloak
**What to extract:** JSON response — each version entry has a `name` and a `phases` array. Extract dates from the phases array — **not** from a top-level `ga_date` field (there is none).

Phase extraction rules:
- **GA date**: `phases[name == "General availability"].end_date` — this is when the GA phase ended (i.e., when the product became GA). The `start_date` of this phase is always `"N/A"`.
- **EOL date**: the `end_date` of the last phase that has a real date (not `"N/A"`). Typically the last of: Maintenance support, Extended update support Term 2, etc.

Use minor version entries (e.g., `26.0`, `26.2`, `26.4`) to determine which **major versions** are still active — do not list minor versions in the table. A major version is active if at least one of its minor releases is GA on or before `code_freeze` and not EOL before `ga_date`. Report only the major version number (e.g., `26`).

### Quay (Red Hat Quay)

**URL:** https://access.redhat.com/product-life-cycles/api/v1/products/?name=Red+Hat+Quay
**What to extract:** JSON response — each version entry has a `name` and a `phases` array. Extract dates from the phases array — **not** from a top-level `ga_date` field (there is none).

Phase extraction rules:
- **GA date**: `phases[name == "General availability"].end_date` — this is when the GA phase ended (i.e., when the product became generally available). The `start_date` of this phase is always `"N/A"`.
- **EOL date**: the `end_date` of the last phase that has a real date (not `"N/A"`).

Identify only the **latest version** with a GA date on or before `code_freeze`. Suggest replacing the current version with that latest version.

## Date Interpretation Rules

When evaluating lifecycle dates against RHDH milestones:

| Rule | Condition | Action |
|------|-----------|--------|
| Add version | Version GA date ≤ RHDH Code Freeze | Suggest adding |
| Remove version | Version EOL date ≤ RHDH GA date | Suggest removing |
| No change | Version in table, not EOL, GA already passed | Leave as-is |
| No change | Version GA date > RHDH Code Freeze | Do not add |

If a lifecycle page provides only a quarter or year (not a precise date), use the **last day of that period** as a conservative estimate and note it in the justification (e.g., "GA estimated end of Q3 2025 → 2025-09-30").
56 changes: 56 additions & 0 deletions skills/rhdh-test-plan-review/scripts/check_gsheets.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
#!/usr/bin/env python3
"""Check if gcloud auth is configured for rhdh-test-plan-review."""

import argparse
import json
import os
import sys

from gcloud_token import get_gcloud_token

_no_color = os.environ.get("NO_COLOR") is not None
_is_tty = sys.stderr.isatty() and not _no_color


def colored(text, code):
if _is_tty:
return f"\033[{code}m{text}\033[0m"
return text


def main():
parser = argparse.ArgumentParser(
description="Check if gcloud auth is configured for the Google Sheets API."
)
parser.add_argument(
"--json",
action="store_true",
dest="json_output",
help="Output as JSON (default: human-readable)",
)
args = parser.parse_args()

token, error = get_gcloud_token()
result = {
"credentials_found": token is not None,
"method": "gcloud",
"error": error,
}

if args.json_output:
json.dump(result, sys.stdout, indent=2)
print()
else:
if result["credentials_found"]:
print(colored("✓", "32") + " gcloud auth token available")
else:
print(colored("✗", "31") + f" {error}")
print()
if not (error and "PATH" in error):
print("Run: gcloud auth login --enable-gdrive-access")

sys.exit(0 if result["credentials_found"] else 1)


if __name__ == "__main__":
main()
Loading
Loading