Skip to content

Commit 9e46d99

Browse files
committed
feat: extract CI lifecycle and Prow job management skills from openshift/release
Extract 10 skills from openshift/release .claude/skills/ into this repository, renaming them with concern-first naming (lifecycle-*, prow-*) and adding dual-mode support for local/remote repo access. Lifecycle skills (portable, pure API): - lifecycle-ocp: OCP version lifecycle via Red Hat Product Life Cycles API - lifecycle-aks: AKS K8s version lifecycle via AKS release status API - lifecycle-eks: EKS K8s version lifecycle via AWS docs - lifecycle-gke: GKE K8s version lifecycle via endoflife.date API Prow job management skills (openshift/release): - prow-ocp-jobs: list/generate OCP cluster-claim test entries - prow-ocp-pools: list/generate Hive ClusterPool YAML - prow-ocp-coverage: cross-reference pools, jobs, and lifecycle data - prow-aks-jobs: list AKS MAPT test entries - prow-eks-jobs: list EKS MAPT test entries - prow-gke-jobs: list GKE test entries Shared infrastructure (skills/_shared/): - resolve-repo.sh: auto-detect local checkout or GitHub API fallback - fetch-yaml.sh: dual-mode file listing/fetching helpers - list-k8s-test-configs.sh: deduplicated from 3 identical copies Key improvements over originals: - 3x list-k8s-test-configs.sh duplication eliminated - 2x print-configured-versions.sh duplication eliminated - ocp-lifecycle.jq cross-reference is now a clean sibling path - All read-only scripts support dual-mode (local or gh CLI) - rhdh-decommission-release intentionally left in openshift/release Assisted-by: OpenCode
1 parent e5a1e6d commit 9e46d99

26 files changed

Lines changed: 2635 additions & 0 deletions

File tree

skills/_shared/fetch-yaml.sh

Lines changed: 171 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,171 @@
1+
#!/usr/bin/env bash
2+
# Fetch YAML files from the openshift/release repository (local or remote).
3+
#
4+
# Provides helper functions for listing and reading YAML files from either
5+
# a local checkout or the GitHub API, abstracting the dual-mode access
6+
# pattern used by all prow-* and lifecycle-* skills.
7+
#
8+
# Usage (in consuming scripts):
9+
# SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
10+
# source "${SCRIPT_DIR}/../../_shared/fetch-yaml.sh"
11+
# init_repo "$@" # parses --repo-dir, resolves root
12+
#
13+
# # List YAML files matching a glob in a repo directory
14+
# list_yaml_files "ci-operator/config/redhat-developer/rhdh" "redhat-developer-rhdh-*.yaml"
15+
#
16+
# # Read a single YAML file (local path or remote repo path)
17+
# fetch_yaml "ci-operator/config/redhat-developer/rhdh/redhat-developer-rhdh-main.yaml"
18+
#
19+
# Requires: curl (remote mode), gh (remote mode)
20+
21+
set -euo pipefail
22+
23+
_SHARED_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
24+
source "${_SHARED_DIR}/resolve-repo.sh"
25+
26+
# Parse --repo-dir from args and resolve the repo root.
27+
# Strips --repo-dir from the args and exports remaining args as SCRIPT_ARGS.
28+
# Call this at the top of each consuming script.
29+
init_repo() {
30+
local args=()
31+
while [[ $# -gt 0 ]]; do
32+
case "$1" in
33+
--repo-dir)
34+
export OPENSHIFT_RELEASE_DIR="$2"
35+
shift 2
36+
;;
37+
*)
38+
args+=("$1")
39+
shift
40+
;;
41+
esac
42+
done
43+
SCRIPT_ARGS=("${args[@]+"${args[@]}"}")
44+
resolve_repo_root >/dev/null
45+
}
46+
47+
# List YAML files in a directory, one path per line.
48+
#
49+
# In local mode, returns absolute paths.
50+
# In remote mode, returns repo-relative paths.
51+
#
52+
# Args:
53+
# $1 - repo-relative directory path (e.g., "ci-operator/config/redhat-developer/rhdh")
54+
# $2 - filename glob pattern (e.g., "redhat-developer-rhdh-*.yaml")
55+
list_yaml_files() {
56+
local dir_path="$1"
57+
local pattern="$2"
58+
59+
if is_remote; then
60+
# Use GitHub Contents API to list directory, filter by pattern
61+
local api_path="repos/${OPENSHIFT_RELEASE_REPO}/contents/${dir_path}"
62+
gh api "$api_path" --jq ".[] | select(.name | test(\"${pattern}\")) | .path" 2>/dev/null || {
63+
echo "ERROR: Failed to list ${dir_path} via GitHub API" >&2
64+
return 1
65+
}
66+
else
67+
local full_dir
68+
full_dir="$(repo_path "$dir_path")"
69+
if [[ ! -d "$full_dir" ]]; then
70+
echo "ERROR: Directory not found: ${full_dir}" >&2
71+
return 1
72+
fi
73+
# Convert glob pattern to find-compatible form and return repo-relative paths
74+
for f in "${full_dir}"/${pattern}; do
75+
[[ -f "$f" ]] || continue
76+
echo "$f"
77+
done
78+
fi
79+
}
80+
81+
# Fetch a single YAML file's content to stdout.
82+
#
83+
# In local mode, the argument is an absolute path (as returned by list_yaml_files).
84+
# In remote mode, the argument is a repo-relative path.
85+
#
86+
# Args:
87+
# $1 - file path (absolute local or repo-relative for remote)
88+
fetch_yaml() {
89+
local file_path="$1"
90+
91+
if is_remote; then
92+
# Fetch via GitHub raw content
93+
local raw_url="https://raw.githubusercontent.com/${OPENSHIFT_RELEASE_REPO}/HEAD/${file_path}"
94+
curl -sL --fail "$raw_url" || {
95+
echo "ERROR: Failed to fetch ${file_path} from GitHub" >&2
96+
return 1
97+
}
98+
else
99+
if [[ ! -f "$file_path" ]]; then
100+
echo "ERROR: File not found: ${file_path}" >&2
101+
return 1
102+
fi
103+
cat "$file_path"
104+
fi
105+
}
106+
107+
# Extract the branch/filename stem from a config file path.
108+
# Works for both absolute local paths and repo-relative remote paths.
109+
#
110+
# Example:
111+
# extract_branch "redhat-developer-rhdh-" ".../redhat-developer-rhdh-main.yaml"
112+
# => "main"
113+
# extract_branch "redhat-developer-rhdh-" ".../redhat-developer-rhdh-release-1.9.yaml"
114+
# => "release-1.9"
115+
extract_branch() {
116+
local prefix="$1"
117+
local filepath="$2"
118+
local filename
119+
filename="$(basename "$filepath")"
120+
echo "$filename" | sed "s/^${prefix}//;s/\\.yaml$//"
121+
}
122+
123+
# Print configured MAPT_KUBERNETES_VERSION per branch from CI config files.
124+
#
125+
# Shared helper used by lifecycle-aks and lifecycle-eks scripts.
126+
#
127+
# Args:
128+
# $1 - repo-relative config dir (e.g., "ci-operator/config/redhat-developer/rhdh")
129+
# $2 - test name regex pattern (e.g., "^e2e-aks-")
130+
# $3 - (optional) repo-relative path to MAPT ref YAML
131+
#
132+
# Requires: yq (v4+)
133+
print_configured_versions() {
134+
local config_dir="$1"
135+
local test_pattern="$2"
136+
local mapt_ref="${3:-}"
137+
138+
local mapt_tag=""
139+
if [[ -n "$mapt_ref" ]]; then
140+
local ref_content
141+
ref_content="$(fetch_yaml "$mapt_ref" 2>/dev/null)" || true
142+
if [[ -n "$ref_content" ]]; then
143+
mapt_tag=$(echo "$ref_content" | grep 'tag:' | awk '{print $2}' | head -1 || true)
144+
fi
145+
fi
146+
147+
if ! command -v yq &>/dev/null; then
148+
echo "WARNING: yq not available, skipping configured versions" >&2
149+
return 0
150+
fi
151+
152+
local prefix="redhat-developer-rhdh-"
153+
local files
154+
files="$(list_yaml_files "$config_dir" "${prefix}*.yaml")" || return 0
155+
[[ -z "$files" ]] && return 0
156+
157+
echo "Configured MAPT_KUBERNETES_VERSION per branch:"
158+
while IFS= read -r f; do
159+
[[ -z "$f" ]] && continue
160+
local branch
161+
branch="$(extract_branch "$prefix" "$f")"
162+
local content
163+
content="$(fetch_yaml "$f" 2>/dev/null)" || continue
164+
local ver
165+
ver=$(echo "$content" | yq -o=json "[.tests[] | select(.as | test(\"${test_pattern}\")) | .steps.env.MAPT_KUBERNETES_VERSION // \"N/A\"] | unique | .[]" 2>/dev/null | sort -u | paste -sd',' - || echo "N/A")
166+
[[ -z "$ver" ]] && ver="N/A"
167+
echo " ${branch}: ${ver}"
168+
done <<< "$files"
169+
[[ -n "$mapt_tag" ]] && echo "MAPT image: mapt:${mapt_tag}"
170+
echo ""
171+
}
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
#!/usr/bin/env bash
2+
# List K8s platform test entries in RHDH CI config files.
3+
#
4+
# Shared script used by prow-aks-jobs, prow-eks-jobs, and prow-gke-jobs skills.
5+
# Supports both local openshift/release checkout and remote GitHub API access.
6+
#
7+
# Usage:
8+
# list-k8s-test-configs.sh --pattern <regex> [--branch <name>] [--repo-dir <path>]
9+
#
10+
# Examples:
11+
# list-k8s-test-configs.sh --pattern "^e2e-aks-"
12+
# list-k8s-test-configs.sh --pattern "^e2e-eks-" --branch main
13+
# list-k8s-test-configs.sh --pattern "^e2e-gke-"
14+
#
15+
# Requires: yq (v4+), jq
16+
17+
set -euo pipefail
18+
19+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
20+
source "${SCRIPT_DIR}/fetch-yaml.sh"
21+
22+
CONFIG_DIR="ci-operator/config/redhat-developer/rhdh"
23+
PATTERN=""
24+
FILTER_BRANCH=""
25+
26+
# Parse --repo-dir first, then skill-specific args
27+
init_repo "$@"
28+
set -- "${SCRIPT_ARGS[@]+"${SCRIPT_ARGS[@]}"}"
29+
30+
while [[ $# -gt 0 ]]; do
31+
case "$1" in
32+
--pattern|-p) PATTERN="$2"; shift 2 ;;
33+
--branch|-b) FILTER_BRANCH="$2"; shift 2 ;;
34+
--config-dir|-d) CONFIG_DIR="$2"; shift 2 ;;
35+
*) echo "Usage: $0 --pattern <regex> [--branch <name>] [--config-dir <path>] [--repo-dir <path>]" >&2; exit 1 ;;
36+
esac
37+
done
38+
39+
[[ -n "$PATTERN" ]] || { echo "ERROR: --pattern is required" >&2; exit 1; }
40+
command -v yq &>/dev/null || { echo "ERROR: yq (v4+) required" >&2; exit 1; }
41+
42+
PREFIX="redhat-developer-rhdh-"
43+
HAS_MAPT_VERSION=false
44+
45+
FILES=$(list_yaml_files "$CONFIG_DIR" "${PREFIX}*.yaml") || { echo "ERROR: Failed to list config files" >&2; exit 1; }
46+
47+
while IFS= read -r f; do
48+
[[ -z "$f" ]] && continue
49+
branch=$(extract_branch "$PREFIX" "$f")
50+
[[ -n "$FILTER_BRANCH" && "$branch" != "$FILTER_BRANCH" ]] && continue
51+
52+
content=$(fetch_yaml "$f" 2>/dev/null) || continue
53+
entries=$(echo "$content" | yq -o=json -I=0 "[.tests[] | select(.as | test(\"${PATTERN}\"))]" 2>/dev/null || true)
54+
[[ -z "$entries" || "$entries" == "[]" ]] && continue
55+
56+
# Check if any entry has MAPT_KUBERNETES_VERSION
57+
has_ver=$(echo "$entries" | jq -r '[.[] | .steps.env.MAPT_KUBERNETES_VERSION // empty] | length')
58+
59+
echo ""
60+
echo "=== Branch: ${branch} ==="
61+
if [[ "$has_ver" -gt 0 ]]; then
62+
HAS_MAPT_VERSION=true
63+
printf " %-40s %-13s %-30s %-10s\n" TEST_NAME K8S_VERSION CRON OPTIONAL
64+
printf " %-40s %-13s %-30s %-10s\n" --------- ----------- ---- --------
65+
echo "$entries" | jq -r '.[] | [.as, (.steps.env.MAPT_KUBERNETES_VERSION//"N/A"), (.cron//"N/A"), (.optional//false|tostring)] | @tsv' | sort | \
66+
while IFS=$'\t' read -r name ver cron opt; do
67+
printf " %-40s %-13s %-30s %-10s\n" "$name" "$ver" "$cron" "$opt"
68+
done
69+
else
70+
printf " %-40s %-30s %-10s\n" TEST_NAME CRON OPTIONAL
71+
printf " %-40s %-30s %-10s\n" --------- ---- --------
72+
echo "$entries" | jq -r '.[] | [.as, (.cron//"N/A"), (.optional//false|tostring)] | @tsv' | sort | \
73+
while IFS=$'\t' read -r name cron opt; do
74+
printf " %-40s %-30s %-10s\n" "$name" "$cron" "$opt"
75+
done
76+
fi
77+
done <<< "$FILES"
78+
79+
echo ""
80+
if [[ "$HAS_MAPT_VERSION" == "true" ]]; then
81+
echo "K8s version source: MAPT_KUBERNETES_VERSION in steps.env per test entry" >&2
82+
else
83+
echo "K8s version: managed outside CI config (static cluster)" >&2
84+
fi

skills/_shared/resolve-repo.sh

Lines changed: 78 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,78 @@
1+
#!/usr/bin/env bash
2+
# Resolve the openshift/release repository root for local or remote access.
3+
#
4+
# Provides a single function that all scripts source to determine whether
5+
# to read files from a local checkout or via the GitHub API.
6+
#
7+
# Resolution order:
8+
# 1. Explicit --repo-dir flag (passed via OPENSHIFT_RELEASE_DIR before sourcing)
9+
# 2. OPENSHIFT_RELEASE_DIR environment variable
10+
# 3. Walk up from $PWD looking for the ci-operator sentinel directory
11+
# 4. Fall back to REMOTE mode (GitHub API via gh CLI)
12+
#
13+
# Usage (in consuming scripts):
14+
# source "$(dirname "${BASH_SOURCE[0]}")/../../_shared/resolve-repo.sh"
15+
# ROOT=$(resolve_repo_root)
16+
# if is_remote; then
17+
# # use gh api ...
18+
# else
19+
# # use local files at $ROOT/...
20+
# fi
21+
#
22+
# No external dependencies beyond bash 3.2+.
23+
24+
set -euo pipefail
25+
26+
# Sentinel path that identifies an openshift/release checkout.
27+
_OR_SENTINEL="ci-operator/config/redhat-developer/rhdh"
28+
29+
# GitHub repository for remote access.
30+
OPENSHIFT_RELEASE_REPO="${OPENSHIFT_RELEASE_REPO:-openshift/release}"
31+
32+
# Resolved root -- populated by resolve_repo_root().
33+
_RESOLVED_ROOT=""
34+
35+
resolve_repo_root() {
36+
# 1. Explicit override via env var
37+
if [[ -n "${OPENSHIFT_RELEASE_DIR:-}" ]]; then
38+
if [[ -d "${OPENSHIFT_RELEASE_DIR}/${_OR_SENTINEL}" ]]; then
39+
_RESOLVED_ROOT="$OPENSHIFT_RELEASE_DIR"
40+
echo "$_RESOLVED_ROOT"
41+
return 0
42+
else
43+
echo "WARNING: OPENSHIFT_RELEASE_DIR is set but ${_OR_SENTINEL} not found there" >&2
44+
fi
45+
fi
46+
47+
# 2. Walk up from cwd
48+
local dir="$PWD"
49+
while [[ "$dir" != "/" ]]; do
50+
if [[ -d "${dir}/${_OR_SENTINEL}" ]]; then
51+
_RESOLVED_ROOT="$dir"
52+
echo "$_RESOLVED_ROOT"
53+
return 0
54+
fi
55+
dir="$(dirname "$dir")"
56+
done
57+
58+
# 3. Remote mode
59+
_RESOLVED_ROOT="REMOTE"
60+
echo "REMOTE"
61+
return 0
62+
}
63+
64+
# Returns 0 (true) if we are in remote mode.
65+
is_remote() {
66+
[[ "$_RESOLVED_ROOT" == "REMOTE" ]]
67+
}
68+
69+
# Returns the full local path for a repo-relative path, or the path as-is
70+
# in remote mode (callers should check is_remote first).
71+
repo_path() {
72+
local relpath="$1"
73+
if is_remote; then
74+
echo "$relpath"
75+
else
76+
echo "${_RESOLVED_ROOT}/${relpath}"
77+
fi
78+
}

0 commit comments

Comments
 (0)