diff --git a/plugins/prow-job/commands/list-qe-jobs.md b/plugins/prow-job/commands/list-qe-jobs.md new file mode 100644 index 0000000..51f37c8 --- /dev/null +++ b/plugins/prow-job/commands/list-qe-jobs.md @@ -0,0 +1,34 @@ +--- +description: list all CI jobs +argument-hint: keywords +--- + +## Name +/prow-job:list-qe-jobs + +## Synopsis +Generate a report showing all openshift qe ci jobs: +``` +/prow-job:list-qe-jobs +``` +Generate a report showing all openshift qe ci jobs with given words in job name: +``` +/prow-job:list-qe-jobs [name] +``` + +## Description +Openshift QE often needs to debug ci job failures, get fault trends, the first step is to get job names and job links. + +## Implementation +Pass the user's request to the skill, which will: +- Load environment variable "LIST_QE_JOBS_BEARER" +- If environment variable "LIST_QE_JOBS_BEARER" is not given, ask user to give one, otherwise stop the skill +- Access https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/prowjobs.js?var=allBuilds&omit=annotations,labels,decoration_config,pod_spec API with above environment variable LIST_QE_JOBS_BEARER as bearer token and list all [spec/job, status/url, status/state] in the respond json. +- Accepts an optional name argument ($1) +- If $1 is provided, "$1" is a space-delimited list of words, outputs all jobs which name contains each word in "$1" +- If no argument is provided, outputs all jobs +- Get all job names, URL and state +- Generate an interactive HTML report which can filter jobs by job name +- The command is stateless and has no side effects + +The skill handles all the implementation details including extract data from URL and HTML report generation. diff --git a/plugins/prow-job/skills/prow-job-list-qe-jobs/README.md b/plugins/prow-job/skills/prow-job-list-qe-jobs/README.md new file mode 100644 index 0000000..ad98fa5 --- /dev/null +++ b/plugins/prow-job/skills/prow-job-list-qe-jobs/README.md @@ -0,0 +1,83 @@ +# Prow Job List Job Skill + +This skill list all Prow CI jobs by accessing https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/prowjobs.js?var=allBuilds&omit=annotations,labels,decoration_config,pod_spec. + +## Overview + +The skill provides a Claude Code skill interface for listing Prow CI jobs. It helps get current test coverage and job status. + +## Components + +### 1. SKILL.md +Claude Code skill definition that provides detailed implementation instructions for the AI assistant. + +### 2. Python Scripts + +#### generate_report.py +Generates interactive HTML reports from parsed job data. +- Get job data from https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/prowjobs.js?var=allBuilds&omit=annotations,labels,decoration_config,pod_spec +- Fill job data into html +- Creates interactive timeline visualization +- Adds filtering and search capabilities + +**Usage:** +```bash +python3 plugins/prow-job/skills/prow-job-list-qe-jobs/generate_report.py \ + -t .work/prow-job-list-qe-jobs/template.html \ + -d .work/prow-job-list-qe-jobs/all_jobs.json \ + -o .work/prow-job-list-qe-jobs/prow_jobs_report.html \ + -k "{KEYWORDS}" +``` + +### 3. HTML Template + +#### template.html +Modern, responsive HTML template for reports featuring: +- Color-coded job state +- Filtering by job state +- Search functionality +- Mobile-responsive design + +## Prerequisites + +1. **Python 3** - For running parser and report generator scripts +2. **Bearer token** - Export bearer token, the token can be found in https://console-openshift-console.apps.ci.l2s4.p1.openshiftapps.com/topology/all-namespaces?view=graph -> click your name in right-top corner -> click `Copy login command` -> the token can be found in the new page + +## Workflow + +1. **Get Data** + - Access https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/prowjobs.js?var=allBuilds&omit=annotations,labels,decoration_config,pod_spec + - Extract [spec/job, status/url, status/state] from the respond json + +2. **Report Generation** + - Render HTML with template + - Output to `.work/prow-job-list-qe-jobs/prow_jobs_report.html` + +## Output + +### HTML Report +- Header with metadata +- Filterable job entries +- Search functionality + +### Directory Structure +``` +. +└── prow-job-list-qe-jobs + ├── all_jobs.json + ├── filtered_jobs.json + ├── prow_jobs_report.html +``` + +## Using with Claude Code + +When you ask Claude to list Prow jobs, it will automatically use this skill. The skill provides detailed instructions that guide Claude through: +- Give keywords if needed +- Generating reports + +You can simply ask: +> "List all prow jobs which name contains release-4.21 and upgrade" +Or +> /prow-job:list-qe-jobs "release-4.21 upgrade" + +Claude will execute the workflow and generate the interactive HTML report. diff --git a/plugins/prow-job/skills/prow-job-list-qe-jobs/SKILL.md b/plugins/prow-job/skills/prow-job-list-qe-jobs/SKILL.md new file mode 100644 index 0000000..3922baf --- /dev/null +++ b/plugins/prow-job/skills/prow-job-list-qe-jobs/SKILL.md @@ -0,0 +1,79 @@ +--- +name: Prow Job list jobs +description: List all jobs for further debugging or analyze +--- + +# Prow Job list jobs + +This skill list all Prow CI jobs by accessing https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/prowjobs.js?var=allBuilds&omit=annotations,labels,decoration_config,pod_spec. + +## When to Use This Skill + +Use this skill when the user wants to: +- Analyze a batch of CI jobs +- Check which area have been covered +- Share CI jobs + +## Prerequisites + +Before starting, verify these prerequisites: + +1. **Python 3** - For running parser and report generator scripts +2. **Bearer token** - Export bearer token, the token can be found in https://console-openshift-console.apps.ci.l2s4.p1.openshiftapps.com/topology/all-namespaces?view=graph -> click your name in right-top corner -> click `Copy login command` -> the token can be found in the new page + - Example: `sha256~_xxxxxxxxxxxxxxxxxxxx` + +## Input Format + +The user will provide: +1. **Key words** - space-delimited list in format `word1 word2 word3` + - Example: `release-4.21 upgrade` + +## Implementation Steps + +### Step 1: Create directory structure + +**Usage:** + ```bash + mkdir -p .work/prow-job-list-qe-jobs/ + ``` + +### Step 2: get all jobs data by accessing URL + +**Usage:** +```bash +curl -H "Authorization: Bearer {LIST_QE_JOBS_BEARER}" -o .work/prow-job-list-qe-jobs/all_jobs.json https://qe-private-deck-ci.apps.ci.l2s4.p1.openshiftapps.com/prowjobs.js?var=allBuilds&omit=annotations,labels,decoration_config,pod_spec +``` + +### Step 3: filter out the jobs which name contains all keywords + +**IMPORTANT: Use the provided shell script `filter_jobs.sh` from the skill directory.** + +**Usage:** +```bash +sh plugins/prow-job/skills/prow-job-list-qe-jobs/filter_jobs.sh \ + .work/prow-job-list-qe-jobs/all_jobs.json \ + .work/prow-job-list-qe-jobs/filtered_jobs.json \ + "{KEYWORDS}" +``` + +### Step 4: Generate HTML Report + +**IMPORTANT: Use the provided Python script `generate_report.py` from the skill directory.** + +**Usage:** +```bash +python3 plugins/prow-job/skills/prow-job-list-qe-jobs/generate_report.py \ + -t plugins/prow-job/skills/prow-job-list-qe-jobs/template.html \ + -d .work/prow-job-list-qe-jobs/filtered_jobs.json \ + -o .work/prow-job-list-qe-jobs/prow_jobs_report.html \ + -k "{KEYWORDS}" +``` + +### Step 4: Present Results to User + +1. **Open report in browser** + - Detect platform and automatically open the HTML report in the default browser + - Linux: `xdg-open .work/prow-job-list-qe-jobs/prow_jobs_report.html` + - macOS: `open .work/prow-job-list-qe-jobs/prow_jobs_report.html` + - Windows: `start .work/prow-job-list-qe-jobs/prow_jobs_report.html` + - On Linux (most common for this environment), use `xdg-open` diff --git a/plugins/prow-job/skills/prow-job-list-qe-jobs/filter_jobs.sh b/plugins/prow-job/skills/prow-job-list-qe-jobs/filter_jobs.sh new file mode 100755 index 0000000..65eb6b3 --- /dev/null +++ b/plugins/prow-job/skills/prow-job-list-qe-jobs/filter_jobs.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# Script to filter jobs from all_jobs.json +# Filters jobs where the name contains ALL specified keywords +# Usage: ./filter_jobs.sh INPUT_FILE OUTPUT_FILE [keyword1] +# Example: ./filter_jobs.sh input.json output.json +# Example: ./filter_jobs.sh input.json output.json "release-4.21 upgrade" + +# Check if at least 2 arguments are provided (input and output files) +if [ $# -lt 2 ]; then + echo "Error: Missing required arguments." + echo "Usage: $0 INPUT_FILE OUTPUT_FILE [keyword1]" + echo "Example: $0 all_jobs.json filtered_jobs.json" + echo "Example: $0 all_jobs.json filtered_jobs.json 'release-4.21 upgrade'" + exit 1 +fi + +INPUT_FILE="$1" +OUTPUT_FILE="$2" +shift 2 # Remove first two arguments, remaining are keywords + +# Check if jq is installed +if ! command -v jq &> /dev/null; then + echo "Error: jq is not installed. Please install jq to use this script." + exit 1 +fi + +# Check if input file exists +if [ ! -f "$INPUT_FILE" ]; then + echo "Error: Input file $INPUT_FILE not found." + exit 1 +fi + +echo "Filtering jobs from $INPUT_FILE..." + +# If no keywords provided, copy all jobs +jsonStr=$(sed 's/^var allBuilds = //' "$INPUT_FILE" | sed 's/.$//') + +if [ $# -eq 0 ]; then + echo "No keywords provided - copying all jobs..." + echo "$jsonStr" | jq '{items: .items}' > "$OUTPUT_FILE" +else + # Build the jq filter condition dynamically + echo "Looking for jobs containing ALL of the following keywords:" + JQ_FILTER="" + for keyword in $@; do + echo " - $keyword" + JQ_FILTER="$JQ_FILTER | select(.spec.job | contains(\"$keyword\"))" + done + + echo -e "Filter: " '.items[] | select(.kind == "ProwJob")'"$JQ_FILTER"'' + + # Extract the JavaScript variable and convert to proper JSON, then filter + # The file starts with "var allBuilds = " so we need to strip that + + echo "$jsonStr" | \ + jq '.items[] | select(.kind == "ProwJob")'"$JQ_FILTER"'' | \ + jq -s 'map({name: .spec.job, state: .status.state, url: .status.url})'> "$OUTPUT_FILE" +fi + +# Count the filtered jobs +TOTAL_COUNT=$(echo "$jsonStr" | jq '.items | length') +FILTERED_COUNT=$(cat "$OUTPUT_FILE" | jq ".|length") + +echo "" +echo "Done!" +echo "Total jobs: $TOTAL_COUNT" +echo "Filtered jobs: $FILTERED_COUNT" +echo "Output saved to: $OUTPUT_FILE" diff --git a/plugins/prow-job/skills/prow-job-list-qe-jobs/generate_report.py b/plugins/prow-job/skills/prow-job-list-qe-jobs/generate_report.py new file mode 100755 index 0000000..83869ca --- /dev/null +++ b/plugins/prow-job/skills/prow-job-list-qe-jobs/generate_report.py @@ -0,0 +1,196 @@ +#!/usr/bin/env python3 +""" +HTML Report Generator + +This script generates HTML reports from a template and job data. + +Usage: + python3 generate_report.py --template template.html --output report.html --data jobs.json + python3 generate_report.py --template template.html --output report.html --data jobs.json --keywords "release-4.21 upgrade" + +Data format (JSON): + [ + { + "job": "job-name-1", + "state": "pending", + "url": "http://example.com/job1" + }, + { + "job": "job-name-2", + "state": "success", + "url": "http://example.com/job2" + } + ] + +Alternative key names supported: + - "job" or "name" + - "state" or "status" + - "url" or "link" +""" + +import json +import sys +import argparse +from datetime import datetime +from collections import Counter + + +def load_template(template_file): + """Load HTML template from file.""" + try: + with open(template_file, 'r') as f: + return f.read() + except FileNotFoundError: + print(f"Error: Template file '{template_file}' not found.") + sys.exit(1) + except Exception as e: + print(f"Error loading template: {e}") + sys.exit(1) + + +def load_job_data(data_file): + """Load job data from JSON file.""" + try: + with open(data_file, 'r') as f: + data = json.load(f) + + if not isinstance(data, list): + print("Error: Job data must be a JSON array/list.") + sys.exit(1) + + return data + except FileNotFoundError: + print(f"Error: Data file '{data_file}' not found.") + sys.exit(1) + except json.JSONDecodeError as e: + print(f"Error parsing JSON data: {e}") + sys.exit(1) + except Exception as e: + print(f"Error loading data: {e}") + sys.exit(1) + + +def generate_stats_cards(jobs): + """Generate HTML for statistics cards.""" + state_counts = Counter(job['state'] for job in jobs) + + stats_html = '' + for state, count in sorted(state_counts.items(), key=lambda x: x[1], reverse=True): + stats_html += f'''
| Job Name | +State | +URL | +
|---|