From d82749b4ab5d26b39e142686ecec5bfca137ba87 Mon Sep 17 00:00:00 2001 From: Neha Kumari Date: Wed, 6 May 2026 20:04:41 +0530 Subject: [PATCH] OAPE-692 | Add jira as input --- AGENTS.md | 22 ++- go-server/handlers.go | 61 +++--- go-server/k8s.go | 32 +++- go-server/static/homepage.html | 34 +++- go.work.sum | 25 +++ plugins/oape/README.md | 22 ++- plugins/oape/commands/api-generate.md | 242 ++++++++++++++++++++---- plugins/oape/commands/api-implement.md | 245 +++++++++++++++++++++---- 8 files changed, 568 insertions(+), 115 deletions(-) diff --git a/AGENTS.md b/AGENTS.md index 89dde36..459eae1 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -4,7 +4,7 @@ This document provides context for AI agents when working with the OAPE AI E2E F ## Purpose -This project provides AI-driven tools for end-to-end feature development in OpenShift operators. The workflow takes an Enhancement Proposal (EP) and/or design document (gist) and generates: +This project provides AI-driven tools for end-to-end feature development in OpenShift operators. The workflow takes an Enhancement Proposal (EP), design document (gist), and/or Jira ticket and generates: 1. API type definitions (Go structs) 2. Integration tests for the API types 3. Controller/reconciler implementation code @@ -14,9 +14,9 @@ This project provides AI-driven tools for end-to-end feature development in Open | Command | Purpose | | ---------------------------------------------------------------- | -------------------------------------------------------------- | | `/oape:init ` | Clone a Git repository and checkout the base branch | -| `/oape:api-generate [--design-doc ]` | Generate Go API types from EP and/or design doc | +| `/oape:api-generate [--design-doc ] [--jira ]` | Generate Go API types from EP, design doc, and/or Jira ticket | | `/oape:api-generate-tests ` | Generate integration test suites for API types | -| `/oape:api-implement [--design-doc ]` | Generate controller code from EP and/or design doc + API types | +| `/oape:api-implement [--design-doc ] [--jira ]` | Generate controller code from EP, design doc, and/or Jira ticket + API types | | `/oape:analyze-rfe ` | Analyze RFE and output EPIC, user stories, and outcomes | | `/oape:e2e-generate ` | Generate e2e test artifacts from git diff against base branch | | `/oape:predict-regressions ` | Predict API regressions and breaking changes from git diff | @@ -31,9 +31,13 @@ These commands support flexible input sources: | -------------------- | ---------------------------------------------------------------------------------- | | EP only | `/oape:api-generate https://github.com/openshift/enhancements/pull/1234` | | Design doc only | `/oape:api-generate --design-doc https://gist.github.com/user/abc123` | +| Jira ticket only | `/oape:api-generate --jira OCPBUGS-12345` | +| Jira ticket (URL) | `/oape:api-generate --jira https://issues.redhat.com/browse/OCPBUGS-12345` | | EP + Design doc | `/oape:api-generate https://github.com/openshift/enhancements/pull/1234 --design-doc https://gist.github.com/user/abc123` | +| EP + Jira ticket | `/oape:api-generate https://github.com/openshift/enhancements/pull/1234 --jira OCPBUGS-12345` | +| All three sources | `/oape:api-generate https://github.com/openshift/enhancements/pull/1234 --design-doc https://gist.github.com/user/abc123 --jira OCPBUGS-12345` | -When both sources are provided, the design document takes precedence for implementation details while the EP provides high-level context. +When multiple sources are provided, precedence is: design document > Jira ticket > EP. The design document provides exact implementation details, the Jira ticket provides specific requirements and acceptance criteria, and the EP provides high-level context. ## Typical Workflow @@ -47,6 +51,12 @@ When both sources are provided, the design document takes precedence for impleme # 2b. Or generate API types with a detailed design document /oape:api-generate https://github.com/openshift/enhancements/pull/XXXX --design-doc https://gist.github.com/user/my-design-doc +# 2c. Or generate API types from a Jira ticket +/oape:api-generate --jira OCPBUGS-12345 + +# 2d. Or combine Jira ticket with EP for richer context +/oape:api-generate https://github.com/openshift/enhancements/pull/XXXX --jira OCPBUGS-12345 + # 3. Generate integration tests for the API types /oape:api-generate-tests api/v1alpha1/ @@ -59,6 +69,9 @@ When both sources are provided, the design document takes precedence for impleme # 5b. Or generate with detailed design document /oape:api-implement https://github.com/openshift/enhancements/pull/XXXX --design-doc https://gist.github.com/user/my-design-doc +# 5c. Or generate from a Jira ticket +/oape:api-implement --jira OCPBUGS-12345 + # 6. Build and verify make generate && make manifests && make build && make test @@ -131,6 +144,7 @@ Before running commands, ensure: - **go** - Go toolchain installed - **git** - Git installed - **make** - Make installed +- **JIRA_PERSONAL_TOKEN** - Personal access token for Jira REST API (required when using `--jira` flag with api-generate or api-implement) --- diff --git a/go-server/handlers.go b/go-server/handlers.go index 8b6d238..c494350 100644 --- a/go-server/handlers.go +++ b/go-server/handlers.go @@ -27,12 +27,15 @@ type App struct { } var epURLPattern = regexp.MustCompile(`^https://github\.com/openshift/enhancements/pull/\d+/?$`) +var gistURLPattern = regexp.MustCompile(`^https://gist\.github(usercontent)?\.com/`) // CreateWorkflowRequest is the JSON body for POST /api/v1/workflows. type CreateWorkflowRequest struct { - EPUrl string `json:"ep_url"` - BaseBranch string `json:"base_branch"` - RepoURL string `json:"repo_url"` + EPUrl string `json:"ep_url"` + BaseBranch string `json:"base_branch"` + RepoURL string `json:"repo_url"` + DesignDocURL string `json:"design_doc_url,omitempty"` + JiraTicket string `json:"jira_ticket,omitempty"` } // WorkflowSummary is a compact representation for workflow lists. @@ -55,13 +58,15 @@ type RepoListResponse struct { // WorkflowDetailResponse for GET /api/v1/workflows/{job_id}. type WorkflowDetailResponse struct { - ID string `json:"id"` - Status string `json:"status"` - Message string `json:"message,omitempty"` - CreatedAt string `json:"createdAt"` - RepoURL string `json:"repoUrl"` - EPUrl string `json:"epUrl"` - BaseBranch string `json:"baseBranch"` + ID string `json:"id"` + Status string `json:"status"` + Message string `json:"message,omitempty"` + CreatedAt string `json:"createdAt"` + RepoURL string `json:"repoUrl"` + EPUrl string `json:"epUrl"` + BaseBranch string `json:"baseBranch"` + DesignDocURL string `json:"designDocUrl,omitempty"` + JiraTicket string `json:"jiraTicket,omitempty"` } // CreateWorkflowResponse for POST /api/v1/workflows. @@ -145,16 +150,26 @@ func (a *App) HandleCreateWorkflow(w http.ResponseWriter, r *http.Request) { return } - if req.EPUrl == "" || req.RepoURL == "" || req.BaseBranch == "" { - writeError(w, http.StatusBadRequest, "ep_url, repo_url, and base_branch are required") + if req.RepoURL == "" || req.BaseBranch == "" { + writeError(w, http.StatusBadRequest, "repo_url and base_branch are required") return } - if !epURLPattern.MatchString(req.EPUrl) { + if req.EPUrl == "" && req.DesignDocURL == "" && req.JiraTicket == "" { + writeError(w, http.StatusBadRequest, "at least one input source is required: ep_url, design_doc_url, or jira_ticket") + return + } + + if req.EPUrl != "" && !epURLPattern.MatchString(req.EPUrl) { writeError(w, http.StatusBadRequest, "ep_url must be a valid OpenShift enhancement PR URL") return } + if req.DesignDocURL != "" && !gistURLPattern.MatchString(req.DesignDocURL) { + writeError(w, http.StatusBadRequest, "design_doc_url must be a valid GitHub Gist URL") + return + } + jobID, err := generateJobID() if err != nil { writeError(w, http.StatusInternalServerError, "failed to generate job ID") @@ -172,6 +187,8 @@ func (a *App) HandleCreateWorkflow(w http.ResponseWriter, r *http.Request) { EPUrl: req.EPUrl, RepoURL: req.RepoURL, BaseBranch: req.BaseBranch, + DesignDocURL: req.DesignDocURL, + JiraTicket: req.JiraTicket, WorkerImage: a.cfg.WorkerImage, EnvConfigMap: a.cfg.WorkerEnvConfigMap, GCloudSecret: a.cfg.GCloudSecretName, @@ -188,7 +205,7 @@ func (a *App) HandleCreateWorkflow(w http.ResponseWriter, r *http.Request) { return } - log.Printf("Created workflow job %s for ep=%s repo=%s base_branch=%s", jobID, req.EPUrl, req.RepoURL, req.BaseBranch) + log.Printf("Created workflow job %s for ep=%s design_doc=%s jira=%s repo=%s base_branch=%s", jobID, req.EPUrl, req.DesignDocURL, req.JiraTicket, req.RepoURL, req.BaseBranch) writeJSON(w, http.StatusCreated, CreateWorkflowResponse{ ID: jobID, Status: "pending", @@ -232,13 +249,15 @@ func (a *App) HandleGetWorkflow(w http.ResponseWriter, r *http.Request) { } writeJSON(w, http.StatusOK, WorkflowDetailResponse{ - ID: info.ID, - Status: info.Status, - Message: info.Message, - CreatedAt: info.CreatedAt, - RepoURL: info.RepoURL, - EPUrl: info.EPUrl, - BaseBranch: info.BaseBranch, + ID: info.ID, + Status: info.Status, + Message: info.Message, + CreatedAt: info.CreatedAt, + RepoURL: info.RepoURL, + EPUrl: info.EPUrl, + BaseBranch: info.BaseBranch, + DesignDocURL: info.DesignDocURL, + JiraTicket: info.JiraTicket, }) } diff --git a/go-server/k8s.go b/go-server/k8s.go index c5e2fa5..fb9b928 100644 --- a/go-server/k8s.go +++ b/go-server/k8s.go @@ -45,6 +45,8 @@ type WorkflowParams struct { EPUrl string RepoURL string BaseBranch string + DesignDocURL string + JiraTicket string WorkerImage string EnvConfigMap string GCloudSecret string @@ -91,9 +93,11 @@ func (c *K8sClient) CreateWorkflowJob(ctx context.Context, jobID string, params "job-id": jobID, }, Annotations: map[string]string{ - "app-platform-shift.openshift.github.io/repo-url": params.RepoURL, - "app-platform-shift.openshift.github.io/ep-url": params.EPUrl, - "app-platform-shift.openshift.github.io/base-branch": params.BaseBranch, + "app-platform-shift.openshift.github.io/repo-url": params.RepoURL, + "app-platform-shift.openshift.github.io/ep-url": params.EPUrl, + "app-platform-shift.openshift.github.io/base-branch": params.BaseBranch, + "app-platform-shift.openshift.github.io/design-doc-url": params.DesignDocURL, + "app-platform-shift.openshift.github.io/jira-ticket": params.JiraTicket, }, }, Spec: batchv1.JobSpec{ @@ -117,6 +121,8 @@ func (c *K8sClient) CreateWorkflowJob(ctx context.Context, jobID string, params {Name: "EP_URL", Value: params.EPUrl}, {Name: "REPO_URL", Value: params.RepoURL}, {Name: "BASE_BRANCH", Value: params.BaseBranch}, + {Name: "DESIGN_DOC_URL", Value: params.DesignDocURL}, + {Name: "JIRA_TICKET", Value: params.JiraTicket}, {Name: "PYTHONUNBUFFERED", Value: "1"}, {Name: "GOOGLE_APPLICATION_CREDENTIALS", Value: "/secrets/gcloud/application_default_credentials.json"}, }, @@ -248,13 +254,15 @@ func (c *K8sClient) StreamPodLogs(ctx context.Context, podName string, follow bo // JobInfo contains extended job information from K8s. type JobInfo struct { - ID string - Status string - Message string - CreatedAt string - RepoURL string - EPUrl string - BaseBranch string + ID string + Status string + Message string + CreatedAt string + RepoURL string + EPUrl string + BaseBranch string + DesignDocURL string + JiraTicket string } // ListJobs returns all workflow jobs with app=shift-worker label. @@ -303,6 +311,8 @@ func (c *K8sClient) ListJobs(ctx context.Context) ([]JobInfo, error) { info.RepoURL = job.Annotations["app-platform-shift.openshift.github.io/repo-url"] info.EPUrl = job.Annotations["app-platform-shift.openshift.github.io/ep-url"] info.BaseBranch = job.Annotations["app-platform-shift.openshift.github.io/base-branch"] + info.DesignDocURL = job.Annotations["app-platform-shift.openshift.github.io/design-doc-url"] + info.JiraTicket = job.Annotations["app-platform-shift.openshift.github.io/jira-ticket"] } result = append(result, info) @@ -349,6 +359,8 @@ func (c *K8sClient) GetJobInfo(ctx context.Context, jobID string) (*JobInfo, err info.RepoURL = job.Annotations["app-platform-shift.openshift.github.io/repo-url"] info.EPUrl = job.Annotations["app-platform-shift.openshift.github.io/ep-url"] info.BaseBranch = job.Annotations["app-platform-shift.openshift.github.io/base-branch"] + info.DesignDocURL = job.Annotations["app-platform-shift.openshift.github.io/design-doc-url"] + info.JiraTicket = job.Annotations["app-platform-shift.openshift.github.io/jira-ticket"] } return info, nil diff --git a/go-server/static/homepage.html b/go-server/static/homepage.html index c4a0150..175ade7 100644 --- a/go-server/static/homepage.html +++ b/go-server/static/homepage.html @@ -48,7 +48,7 @@

OAPE Operator Feature Developer

-

Generate complete operator implementation from an OpenShift Enhancement Proposal

+

Generate complete operator implementation from an Enhancement Proposal, Design Document, and/or Jira Ticket

Workflow Overview

@@ -76,12 +76,26 @@

Workflow Overview

- + + placeholder="https://github.com/openshift/enhancements/pull/1234">
The enhancement proposal that describes the feature
+
+ + +
GitHub Gist with detailed API specifications
+
+ +
+ + +
Jira ticket with requirements and acceptance criteria
+
+ @@ -137,10 +151,16 @@

Workflow Overview

form.addEventListener('submit', async (e) => { e.preventDefault(); const epUrl = document.getElementById('ep_url').value.trim(); + const designDocUrl = document.getElementById('design_doc_url').value.trim(); + const jiraTicket = document.getElementById('jira_ticket').value.trim(); const repo = document.getElementById('repo').value; const baseBranch = document.getElementById('base_branch').value.trim(); - if (!epUrl || !repo || !baseBranch) return; + if (!repo || !baseBranch) return; + if (!epUrl && !designDocUrl && !jiraTicket) { + statusEl.innerHTML = 'At least one input source is required (EP URL, Design Document, or Jira Ticket)'; + return; + } btn.disabled = true; logEl.style.display = 'none'; @@ -150,7 +170,11 @@

Workflow Overview

statusEl.innerHTML = ' Submitting workflow job\u2026'; try { - const body = JSON.stringify({ep_url: epUrl, repo_url: repo, base_branch: baseBranch}); + const payload = {repo_url: repo, base_branch: baseBranch}; + if (epUrl) payload.ep_url = epUrl; + if (designDocUrl) payload.design_doc_url = designDocUrl; + if (jiraTicket) payload.jira_ticket = jiraTicket; + const body = JSON.stringify(payload); const res = await fetch('/api/v1/workflows', { method: 'POST', headers: {'Content-Type': 'application/json'}, diff --git a/go.work.sum b/go.work.sum index 02d75e2..16f8384 100644 --- a/go.work.sum +++ b/go.work.sum @@ -1,19 +1,44 @@ +cloud.google.com/go/compute/metadata v0.3.0 h1:Tz+eQXMEqDIKRsmY3cHTL6FVaynIjX2QxYC4trgAKZc= cloud.google.com/go/compute/metadata v0.3.0/go.mod h1:zFmK7XCadkQkj6TtorcaGlCW1hT1fIilQDwofLpJ20k= +github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46 h1:lsxEuwrXEAokXB9qhlbKWPpo3KMLZQ5WB5WLQRW1uq0= github.com/NYTimes/gziphandler v0.0.0-20170623195520-56545f4a5d46/go.mod h1:3wb06e3pkSAbeQ52E9H9iFoQsEEwGN64994WTCIhntQ= +github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5 h1:0CwZNZbxp69SHPdPJAN/hZIm0C4OItdklCFmMRWYpio= github.com/armon/go-socks5 v0.0.0-20160902184237-e75332964ef5/go.mod h1:wHh0iHkYZB8zMSxRWpUBQtwG5a7fFgvEO+odwuTv2gs= +github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a h1:idn718Q4B6AGu/h5Sxe66HYVdqdGu2l9Iebqhi/AEoA= github.com/asaskevich/govalidator v0.0.0-20190424111038-f61b66f89f4a/go.mod h1:lB+ZfQJz7igIIfQNfa7Ml4HSf2uFQQRzpGGRXenZAgY= +github.com/creack/pty v1.1.9 h1:uDmaGzcdjhF4i/plgjmEsriH11Y0o7RKapEf/LDaM3w= +github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da h1:oI5xCqsCo564l8iNU+DwB5epxmsaqB+rhGL0m5jtYqE= github.com/golang/groupcache v0.0.0-20210331224755-41bb18bfe9da/go.mod h1:cIg4eruTrX1D+g88fzRXU5OdNfaM+9IcxsU14FzY7Hc= +github.com/google/btree v1.0.1 h1:gK4Kx5IaGY9CD5sPJ36FHiBJ6ZXl0kilRiiCj+jdYp4= github.com/google/btree v1.0.1/go.mod h1:xXMiIv4Fb/0kKde4SpL7qlzvu5cMJDRkFDxJfI9uaxA= +github.com/gorilla/websocket v1.5.0 h1:PPwGk2jz7EePpoHN/+ClbZu8SPxiqlu12wZP/3sWmnc= github.com/gorilla/websocket v1.5.0/go.mod h1:YR8l580nyteQvAITg2hZ9XVh4b55+EU/adAjf1fMHhE= +github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7 h1:pdN6V1QBWetyv/0+wjACpqVH+eVULgEjkurDLq3goeM= github.com/gregjones/httpcache v0.0.0-20180305231024-9cad4c3443a7/go.mod h1:FecbI9+v66THATjSRHfNgh1IVFe/9kFxbXtjV0ctIMA= +github.com/kisielk/errcheck v1.5.0 h1:e8esj/e4R+SAOwFwN+n3zr0nYeCyeweozKfO23MvHzY= +github.com/kisielk/gotool v1.0.0 h1:AV2c/EiW3KqPNT9ZKl07ehoAGi4C5/01Cfbblndcapg= +github.com/kr/pty v1.1.1 h1:VkoXIwSboBpnk99O/KFauAEILuNHv5DVFKZMBN/gUgw= +github.com/moby/spdystream v0.4.0 h1:Vy79D6mHeJJjiPdFEL2yku1kl0chZpJfZcPpb16BRl8= github.com/moby/spdystream v0.4.0/go.mod h1:xBAYlnt/ay+11ShkdFKNAG7LsyK/tmNBVvVOwrfMgdI= +github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f h1:y5//uYreIhSUg3J1GEMiLbxo1LJaP8RfCpH6pymGZus= github.com/mxk/go-flowrate v0.0.0-20140419014527-cca7078d478f/go.mod h1:ZdcZmHo+o7JKHSa8/e818NopupXU1YMK5fe1lsApnBw= +github.com/peterbourgon/diskv v2.0.1+incompatible h1:UBdAOUP5p4RWqPBg048CAvpKN+vxiaj6gdUUzhl4XmI= github.com/peterbourgon/diskv v2.0.1+incompatible/go.mod h1:uqqh8zWWbv1HBMNONnaR/tNboyR3/BZd58JJSHlUSCU= +github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4= github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= +github.com/stretchr/objx v0.5.0 h1:1zr/of2m5FGMsad5YfcqgdqdWrIhu+EBEJRhR1U7z/c= +github.com/yuin/goldmark v1.2.1 h1:ruQGxdhGHe7FWOJPT0mKs5+pD2Xs1Bm/kdGlHO04FmM= +go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto= go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE= +golang.org/x/crypto v0.24.0 h1:mnl8DM0o513X8fdIkmyFE/5hTYxbwYOjDS/+rK6qpRI= golang.org/x/crypto v0.24.0/go.mod h1:Z1PMYSOR5nyMcyAVAIQSKCDwalqy85Aqn1x3Ws4L5DM= +golang.org/x/mod v0.17.0 h1:zY54UmvipHiNd+pm+m0x9KhZ9hl1/7QNMyxXbc6ICqA= golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c= +golang.org/x/sync v0.7.0 h1:YsImfSBoP9QPYL0xyKJPq0gcaJdG3rInoqxTWbfQu9M= golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk= +golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2 h1:H2TDz8ibqkAF6YGhCdN3jS9O0/s90v0rJh3X/OLHEUk= golang.org/x/xerrors v0.0.0-20220907171357-04be3eba64a2/go.mod h1:K8+ghG5WaK9qNqU5K3HdILfMLy1f3aNYFI/wnl100a8= +gopkg.in/evanphx/json-patch.v4 v4.12.0 h1:n6jtcsulIzXPJaxegRbvFNNrZDjbij7ny3gmSPG+6V4= gopkg.in/evanphx/json-patch.v4 v4.12.0/go.mod h1:p8EYWUEYMpynmqDbY58zCKCFZw8pRWMG4EsWvDvM72M= +k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70 h1:NGrVE502P0s0/1hudf8zjgwki1X/TByhmAoILTarmzo= k8s.io/gengo/v2 v2.0.0-20240228010128-51d4e06bde70/go.mod h1:VH3AT8AaQOqiGjMF9p0/IM1Dj+82ZwjfxUP1IxaHE+8= diff --git a/plugins/oape/README.md b/plugins/oape/README.md index 1268457..303a818 100644 --- a/plugins/oape/README.md +++ b/plugins/oape/README.md @@ -26,17 +26,20 @@ Clones an OpenShift operator git repository by URL into the current directory an ### `/oape:api-generate` -Reads an OpenShift enhancement proposal PR, extracts the required API changes, and generates compliant Go type definitions in the correct paths of the current OpenShift operator repository. +Reads an OpenShift enhancement proposal PR, design document (gist), and/or Jira ticket, extracts the required API changes, and generates compliant Go type definitions in the correct paths of the current OpenShift operator repository. **Usage:** ```shell /oape:api-generate https://github.com/openshift/enhancements/pull/1234 +/oape:api-generate --jira OCPBUGS-12345 +/oape:api-generate https://github.com/openshift/enhancements/pull/1234 --jira OCPBUGS-12345 +/oape:api-generate https://github.com/openshift/enhancements/pull/1234 --design-doc https://gist.github.com/user/abc123 --jira OCPBUGS-12345 ``` **What it does:** -1. **Prechecks** -- Validates the PR URL, required tools (`gh`, `go`, `git`), GitHub authentication, repository type (must be an OpenShift operator repo with `openshift/api` dependency), and PR accessibility. Fails immediately if any precheck fails. +1. **Prechecks** -- Validates the PR URL/Jira ticket key, required tools (`gh`, `go`, `git`), GitHub authentication, repository type (must be an OpenShift operator repo with `openshift/api` dependency), and input source accessibility. Fails immediately if any precheck fails. 2. **Knowledge Refresh** -- Fetches and internalizes the latest OpenShift and Kubernetes API conventions before generating any code. -3. **Enhancement Analysis** -- Reads the enhancement proposal to extract API group, version, kinds, fields, validation requirements, feature gate info, and whether it is a configuration or workload API. +3. **Input Analysis** -- Reads the enhancement proposal, Jira ticket, and/or design document to extract API group, version, kinds, fields, validation requirements, feature gate info, and whether it is a configuration or workload API. 4. **Code Generation** -- Generates or modifies Go type definitions following conventions derived from the authoritative documents and patterns from the existing codebase. 5. **FeatureGate Registration** -- Adds FeatureGate to `features.go` when applicable. @@ -57,17 +60,19 @@ Generates `.testsuite.yaml` integration test files for OpenShift API type defini ### `/oape:api-implement` -Reads an OpenShift enhancement proposal PR, extracts the required implementation logic, and generates complete controller/reconciler code following controller-runtime and operator-sdk conventions. +Reads an OpenShift enhancement proposal PR, design document (gist), and/or Jira ticket, extracts the required implementation logic, and generates complete controller/reconciler code following controller-runtime and operator-sdk conventions. **Usage:** ```shell /oape:api-implement https://github.com/openshift/enhancements/pull/1234 +/oape:api-implement --jira OCPBUGS-12345 +/oape:api-implement https://github.com/openshift/enhancements/pull/1234 --jira OCPBUGS-12345 ``` **What it does:** -1. **Prechecks** -- Validates the PR URL, required tools (`gh`, `go`, `git`, `make`), GitHub authentication, repository type (controller-runtime or library-go), and PR accessibility. +1. **Prechecks** -- Validates the PR URL/Jira ticket key, required tools (`gh`, `go`, `git`, `make`), GitHub authentication, repository type (controller-runtime or library-go), and input source accessibility. 2. **Knowledge Refresh** -- Fetches and internalizes the latest controller-runtime patterns and operator best practices. -3. **Enhancement Analysis** -- Reads the enhancement proposal to extract business logic requirements, reconciliation workflow, conditions, events, and error handling. +3. **Input Analysis** -- Reads the enhancement proposal, Jira ticket, and/or design document to extract business logic requirements, reconciliation workflow, conditions, events, and error handling. 4. **Pattern Detection** -- Identifies the controller layout pattern used in the repository. 5. **Code Generation** -- Generates complete Reconcile() logic, SetupWithManager, finalizer handling, status updates, and event recording. 6. **Controller Registration** -- Adds the new controller to the manager. @@ -95,8 +100,9 @@ Analyzes a Jira Request for Enhancement (RFE) and generates a structured breakdo # Clone the operator repository (if not already cloned) /oape:init cert-manager-operator -# Generate the API types +# Generate the API types (from EP, Jira ticket, or both) /oape:api-generate https://github.com/openshift/enhancements/pull/1234 +/oape:api-generate --jira OCPBUGS-12345 # Generate integration tests for the new types /oape:api-generate-tests api/v1alpha1/myresource_types.go @@ -200,7 +206,7 @@ See [e2e-test-generator/](e2e-test-generator/) for fixture templates and pattern - **gh** (GitHub CLI) -- installed and authenticated (for api-generate, api-implement, review) - **make** -- Make (for api-implement) - **curl** -- For fetching Jira issues (for review, analyze-rfe) -- **JIRA_PERSONAL_TOKEN** -- For analyze-rfe (Jira REST API) +- **JIRA_PERSONAL_TOKEN** -- For api-generate/api-implement with `--jira` flag, and for analyze-rfe (Jira REST API) - **oc** -- OpenShift CLI (recommended, for running generated execution steps) - Must be run from within an OpenShift operator repository diff --git a/plugins/oape/commands/api-generate.md b/plugins/oape/commands/api-generate.md index ad2bf51..b20d5ee 100644 --- a/plugins/oape/commands/api-generate.md +++ b/plugins/oape/commands/api-generate.md @@ -1,6 +1,6 @@ --- -description: Generate OpenShift API type definitions from an enhancement proposal PR and/or design document, following OpenShift and Kubernetes API conventions -argument-hint: [--design-doc ] +description: Generate OpenShift API type definitions from an enhancement proposal PR, design document, and/or Jira ticket, following OpenShift and Kubernetes API conventions +argument-hint: [--design-doc ] [--jira ] --- ## Name @@ -16,16 +16,29 @@ oape:api-generate # Design document only /oape:api-generate --design-doc + +# Jira ticket only +/oape:api-generate --jira OCPBUGS-12345 + +# Jira ticket with full URL +/oape:api-generate --jira https://issues.redhat.com/browse/OCPBUGS-12345 + +# EP + Jira ticket +/oape:api-generate --jira OCPBUGS-12345 + +# All three sources +/oape:api-generate --design-doc --jira OCPBUGS-12345 ``` ## Description -The `oape:api-generate` command reads an OpenShift enhancement proposal PR and/or a design document (GitHub Gist), extracts the required API changes, and generates compliant Go type definitions in the correct paths of the current OpenShift operator repository. +The `oape:api-generate` command reads an OpenShift enhancement proposal PR, a design document (GitHub Gist), and/or a Jira ticket, extracts the required API changes, and generates compliant Go type definitions in the correct paths of the current OpenShift operator repository. **Input Sources:** - **Enhancement Proposal (EP)**: High-level requirements, constraints, and context from an openshift/enhancements PR - **Design Document (Gist)**: Detailed implementation specifications including exact field definitions, validation rules, and code structure +- **Jira Ticket**: Specific implementation requirements, acceptance criteria, and component context from a Jira issue -When both sources are provided, the design document takes precedence for implementation details while the EP provides high-level context. +When multiple sources are provided, precedence is: design document > Jira ticket > EP. The design document takes precedence for exact implementation details, the Jira ticket provides specific requirements and acceptance criteria, and the EP provides high-level context. It refreshes its knowledge of API conventions from the authoritative sources on every run, analyzes the input sources, and generates or modifies Go types that strictly follow both OpenShift and Kubernetes API conventions. @@ -39,34 +52,52 @@ All prechecks must pass before proceeding. If ANY precheck fails, STOP immediate #### Precheck 1 — Parse and Validate Input Arguments -The command accepts an Enhancement Proposal URL and/or a design document (gist) URL. At least one must be provided. +The command accepts an Enhancement Proposal URL, a design document (gist) URL, and/or a Jira ticket key/URL. At least one must be provided. ```bash ARGS="$ARGUMENTS" ENHANCEMENT_PR="" DESIGN_DOC_URL="" +JIRA_INPUT="" +JIRA_TICKET_KEY="" ENHANCEMENT_PR_NUMBER="" +# Extract --jira argument if present +if echo "$ARGS" | grep -q '\-\-jira'; then + JIRA_INPUT=$(echo "$ARGS" | sed -n 's/.*--jira[[:space:]]\+\([^[:space:]]\+\).*/\1/p') + ARGS=$(echo "$ARGS" | sed 's/--jira[[:space:]]\+[^[:space:]]\+//') + # Guard against accidental flag-as-value (e.g., --jira --design-doc) + if echo "$JIRA_INPUT" | grep -qE '^--'; then + echo "PRECHECK FAILED: --jira value looks like a flag: $JIRA_INPUT" + echo "Provide a ticket key (e.g., OCPBUGS-12345) or URL after --jira." + exit 1 + fi +fi + # Extract --design-doc argument if present if echo "$ARGS" | grep -q '\-\-design-doc'; then DESIGN_DOC_URL=$(echo "$ARGS" | sed -n 's/.*--design-doc[[:space:]]\+\([^[:space:]]\+\).*/\1/p') # Remove --design-doc and its value from ARGS to get EP URL ENHANCEMENT_PR=$(echo "$ARGS" | sed 's/--design-doc[[:space:]]\+[^[:space:]]\+//' | xargs) else - ENHANCEMENT_PR="$ARGS" + ENHANCEMENT_PR=$(echo "$ARGS" | xargs) fi # Validate at least one input is provided -if [ -z "$ENHANCEMENT_PR" ] && [ -z "$DESIGN_DOC_URL" ]; then +if [ -z "$ENHANCEMENT_PR" ] && [ -z "$DESIGN_DOC_URL" ] && [ -z "$JIRA_INPUT" ]; then echo "PRECHECK FAILED: No input provided." echo "Usage:" - echo " /oape:api-generate [--design-doc ]" + echo " /oape:api-generate [--design-doc ] [--jira ]" echo " /oape:api-generate --design-doc " + echo " /oape:api-generate --jira OCPBUGS-12345" + echo " /oape:api-generate --jira https://issues.redhat.com/browse/OCPBUGS-12345" echo "" echo "Examples:" echo " /oape:api-generate https://github.com/openshift/enhancements/pull/1234" echo " /oape:api-generate https://github.com/openshift/enhancements/pull/1234 --design-doc https://gist.github.com/user/abc123" echo " /oape:api-generate --design-doc https://gist.github.com/user/abc123" + echo " /oape:api-generate --jira OCPBUGS-12345" + echo " /oape:api-generate https://github.com/openshift/enhancements/pull/1234 --jira OCPBUGS-12345" exit 1 fi @@ -81,7 +112,7 @@ if [ -n "$ENHANCEMENT_PR" ]; then ENHANCEMENT_PR_NUMBER=$(echo "$ENHANCEMENT_PR" | grep -oE '[0-9]+$') echo "Enhancement PR #$ENHANCEMENT_PR_NUMBER validated." else - echo "No Enhancement PR provided. Using design document only." + echo "No Enhancement PR provided." fi # Validate Design Document URL if provided @@ -98,13 +129,41 @@ if [ -n "$DESIGN_DOC_URL" ]; then fi echo "Design document URL validated: $DESIGN_DOC_URL" else - echo "No design document provided. Using Enhancement PR only." + echo "No design document provided." +fi + +# Validate and parse Jira ticket if provided +if [ -n "$JIRA_INPUT" ]; then + # If input is a URL, extract the ticket key + if echo "$JIRA_INPUT" | grep -qE '^https?://'; then + JIRA_TICKET_KEY=$(echo "$JIRA_INPUT" | sed -n 's|.*/browse/\([A-Z][A-Z0-9]*-[0-9][0-9]*\).*|\1|p') + if [ -z "$JIRA_TICKET_KEY" ]; then + echo "PRECHECK FAILED: Could not extract ticket key from Jira URL." + echo "Expected format: https://issues.redhat.com/browse/PROJECT-12345" + echo "Got: $JIRA_INPUT" + exit 1 + fi + else + JIRA_TICKET_KEY="$JIRA_INPUT" + fi + + # Validate ticket key format (PROJECT-NUMBER) + if ! echo "$JIRA_TICKET_KEY" | grep -qE '^[A-Z][A-Z0-9]+-[0-9]+$'; then + echo "PRECHECK FAILED: Invalid Jira ticket key format." + echo "Expected format: PROJECT-12345 (e.g., OCPBUGS-12345, RFE-1234)" + echo "Got: $JIRA_TICKET_KEY" + exit 1 + fi + echo "Jira ticket key validated: $JIRA_TICKET_KEY" +else + echo "No Jira ticket provided." fi echo "" echo "=== Input Sources ===" [ -n "$ENHANCEMENT_PR" ] && echo " Enhancement PR: $ENHANCEMENT_PR" [ -n "$DESIGN_DOC_URL" ] && echo " Design Document: $DESIGN_DOC_URL" +[ -n "$JIRA_TICKET_KEY" ] && echo " Jira Ticket: $JIRA_TICKET_KEY" echo "=====================" ``` @@ -245,7 +304,60 @@ else fi ``` -#### Precheck 6 — Verify Clean Working Tree (Warning) +#### Precheck 6 — Verify Jira Ticket is Accessible (if provided) + +```bash +JIRA_SUMMARY="" + +if [ -n "$JIRA_TICKET_KEY" ]; then + echo "Verifying Jira ticket $JIRA_TICKET_KEY is accessible..." + + JIRA_URL="${JIRA_URL:-https://issues.redhat.com}" + + # Check if JIRA_PERSONAL_TOKEN is set + if [ -z "$JIRA_PERSONAL_TOKEN" ]; then + echo "PRECHECK FAILED: JIRA_PERSONAL_TOKEN environment variable is not set." + echo "" + echo "Setup instructions:" + echo " 1. Visit: https://issues.redhat.com/secure/ViewProfile.jspa?selectedTab=com.atlassian.pats.pats-plugin:jira-user-personal-access-tokens" + echo " 2. Create a personal access token" + echo " 3. Export it: export JIRA_PERSONAL_TOKEN=\"your_token_here\"" + exit 1 + fi + + # Fetch issue summary to verify accessibility + JIRA_RESPONSE=$(curl -sS -w "\n%{http_code}" \ + -H "Authorization: Bearer $JIRA_PERSONAL_TOKEN" \ + -H "Accept: application/json" \ + "$JIRA_URL/rest/api/2/issue/$JIRA_TICKET_KEY?fields=summary") + + JIRA_HTTP_CODE=$(echo "$JIRA_RESPONSE" | tail -1) + JIRA_BODY=$(echo "$JIRA_RESPONSE" | sed '$d') + + if [ "$JIRA_HTTP_CODE" = "200" ]; then + JIRA_SUMMARY=$(echo "$JIRA_BODY" | jq -r '.fields.summary // "Untitled"') + echo "Jira ticket verified: $JIRA_TICKET_KEY - $JIRA_SUMMARY" + elif [ "$JIRA_HTTP_CODE" = "401" ]; then + echo "PRECHECK FAILED: Jira authentication failed (HTTP 401)." + echo "Your JIRA_PERSONAL_TOKEN may be invalid or expired." + exit 1 + elif [ "$JIRA_HTTP_CODE" = "403" ]; then + echo "PRECHECK FAILED: Access denied to Jira ticket $JIRA_TICKET_KEY (HTTP 403)." + exit 1 + elif [ "$JIRA_HTTP_CODE" = "404" ]; then + echo "PRECHECK FAILED: Jira ticket $JIRA_TICKET_KEY not found (HTTP 404)." + echo "Verify the ticket key and check: $JIRA_URL/browse/$JIRA_TICKET_KEY" + exit 1 + else + echo "PRECHECK FAILED: Unable to access Jira ticket $JIRA_TICKET_KEY (HTTP $JIRA_HTTP_CODE)." + exit 1 + fi +else + echo "Skipping Jira ticket validation (not provided)." +fi +``` + +#### Precheck 7 — Verify Clean Working Tree (Warning) ```bash if ! git diff --quiet || ! git diff --cached --quiet; then @@ -283,7 +395,7 @@ generation steps. ### Phase 2: Fetch and Analyze Input Sources -Fetch content from all provided input sources (Enhancement Proposal and/or Design Document). +Fetch content from all provided input sources (Enhancement Proposal, Design Document, and/or Jira Ticket). #### 2.1 Fetch Enhancement Proposal (if provided) @@ -331,17 +443,58 @@ If the `gh api` command fails, try fetching via curl: curl -sL "https://api.github.com/gists/$GIST_ID" | jq -r '.files | to_entries[] | "=== FILE: \(.key) ===\n\(.value.content)\n"' ``` -#### 2.3 Analyze and Merge Requirements +#### 2.3 Fetch Jira Ticket (if provided) + +```bash +if [ -n "$JIRA_TICKET_KEY" ]; then + echo "Fetching Jira ticket $JIRA_TICKET_KEY..." + + JIRA_URL="${JIRA_URL:-https://issues.redhat.com}" + + # Fetch full issue details including description, acceptance criteria, components, and linked issues + # customfield_12316840 = Target Release, customfield_12319940 = Story Points (Red Hat Jira) + curl -sS \ + -H "Authorization: Bearer $JIRA_PERSONAL_TOKEN" \ + -H "Accept: application/json" \ + "$JIRA_URL/rest/api/2/issue/$JIRA_TICKET_KEY?fields=summary,description,components,labels,status,issuetype,issuelinks,fixVersions,customfield_12316840,customfield_12319940" +fi +``` + +From the Jira response, extract: +- **Summary**: High-level description of the requirement +- **Description**: Full requirement text (strip Jira wiki markup) +- **Components**: Affected OpenShift components +- **Labels**: Relevant labels (e.g., TechPreview, API-change) +- **Acceptance Criteria**: From description or custom fields +- **Linked Issues**: Related EPs, bugs, or other stories that provide context +- **Fix Versions**: Target OpenShift version + +#### 2.4 Analyze and Merge Requirements ```thinking I need to analyze the input source(s) and extract API requirements. The approach depends on what was provided: -**If BOTH Enhancement Proposal AND Design Document are provided:** +**Precedence rules (highest to lowest):** +1. Design Document — exact implementation details +2. Jira Ticket — specific requirements and acceptance criteria +3. Enhancement Proposal — high-level context and constraints + +**If Design Document, Jira Ticket, AND Enhancement Proposal are all provided:** +- EP provides high-level context: motivation, constraints, affected components +- Jira ticket adds specific requirements: acceptance criteria, component details, fix versions +- Design Document provides implementation details: exact field definitions, types, validation +- For conflicts: Design Document > Jira Ticket > EP + +**If BOTH Enhancement Proposal AND Design Document are provided (no Jira):** - The EP provides high-level context: motivation, constraints, affected components - The Design Document provides implementation details: exact field definitions, types, validation - When both specify the same information, the Design Document takes precedence -- Extract from EP: operator/component context, FeatureGate requirements, general constraints -- Extract from Design Document: exact API fields, types, validation rules, code structure + +**If Jira Ticket is provided (alone or with EP, without Design Document):** +- Extract from Jira: summary, description, acceptance criteria, components, linked issues +- Look for API field specifications, validation rules, or schema descriptions in the description +- Check linked issues for additional context (linked EPs, related bugs, stories) +- Extract from EP (if provided): broader motivation, architectural constraints **If only Enhancement Proposal is provided:** - Extract all requirements from the EP (original behavior) @@ -350,6 +503,15 @@ I need to analyze the input source(s) and extract API requirements. The approach - The Design Document must be comprehensive enough to generate API types - It should specify: API group, version, kind, all fields with types and validation +**If only Jira Ticket is provided:** +- The ticket must contain enough detail to determine API group, version, kind, and fields +- If the ticket is sparse (missing description, no acceptance criteria, or lacks API details), + WARN the user and ask clarifying questions such as: + - "What API group/version should be used?" + - "What fields are needed in the spec?" + - "Should this be TechPreview-gated?" + Then continue with the combined information + From the combined sources, I must extract: a. Which OpenShift operator/component is being modified b. The API group and version (e.g., config.openshift.io/v1, operator.openshift.io/v1) @@ -365,9 +527,10 @@ From the combined sources, I must extract: l. The FeatureGate name to use If there are conflicts between sources, I will: -1. Prefer Design Document specifics over EP generalizations -2. Document any conflicts in my analysis -3. Ask the user for clarification if conflicts are ambiguous +1. Prefer Design Document specifics over all others +2. Prefer Jira ticket specifics over EP generalizations +3. Document any conflicts in my analysis +4. Ask the user for clarification if conflicts are ambiguous ``` ### Phase 3: Identify Target API Paths in Current Repository @@ -487,6 +650,7 @@ After generating all files, provide a summary: Input Sources: Enhancement PR: (if provided) Design Document: (if provided) + Jira Ticket: - (if provided) Enhancement Title: (if EP provided) Generated/Modified Files: @@ -512,8 +676,9 @@ Modified Fields/Types: Validation Rules: - <field>: <rule description> -Source Conflicts Resolved: (if both EP and design doc provided) - - <field>: Used design doc specification (<reason>) +Source Conflicts Resolved: (if multiple sources provided) + - <field>: Used design doc specification over Jira ticket (<reason>) + - <field>: Used Jira acceptance criteria over EP (<reason>) Next Steps: 1. Review the generated code for correctness @@ -529,14 +694,16 @@ Next Steps: The command MUST FAIL and STOP immediately if ANY of the following are true: -1. **No input provided**: Neither an enhancement PR URL nor a design document URL was provided +1. **No input provided**: Neither an enhancement PR URL, design document URL, nor Jira ticket key was provided 2. **Invalid PR URL**: The provided EP URL is not a valid `openshift/enhancements` PR 3. **Invalid gist URL**: The provided design document URL is not a valid GitHub Gist -4. **Missing tools**: `gh`, `go`, or `git` are not installed or `gh` is not authenticated -5. **Not an operator repo**: The current directory is not a Git repository with a Go module that references `openshift/api` -6. **Input not accessible**: The enhancement PR or design document cannot be fetched (permissions, doesn't exist, etc.) -7. **No API changes found**: The input source(s) do not describe any API changes -8. **Ambiguous API target**: Cannot determine the target API group, version, or kind from the input sources +4. **Invalid Jira ticket**: The provided Jira ticket key does not match PROJECT-NUMBER format +5. **Missing tools**: `gh`, `go`, or `git` are not installed or `gh` is not authenticated +6. **Not an operator repo**: The current directory is not a Git repository with a Go module that references `openshift/api` +7. **Input not accessible**: The enhancement PR, design document, or Jira ticket cannot be fetched (permissions, doesn't exist, etc.) +8. **Jira token missing**: `--jira` was provided but `JIRA_PERSONAL_TOKEN` is not set +9. **No API changes found**: The input source(s) do not describe any API changes +10. **Ambiguous API target**: Cannot determine the target API group, version, or kind from the input sources When failing, provide a clear error message explaining: - Which precheck failed @@ -546,7 +713,7 @@ When failing, provide a clear error message explaining: ## Behavioral Rules 1. **Never guess**: If the input sources are ambiguous about API details, STOP and ask the user for clarification rather than guessing. -2. **Design document precedence**: When both EP and design document are provided, the design document takes precedence for implementation details. +2. **Source precedence**: When multiple sources are provided, precedence is: design document > Jira ticket > EP for implementation details. 3. **Convention over proposal**: If the input sources suggest an API design that violates conventions (e.g., using a Boolean), generate the convention-compliant alternative and document the deviation. 4. **TechPreview when specified**: If the input sources indicate TechPreview gating, generate the appropriate FeatureGate markers. Follow whatever is specified regarding API maturity level. 5. **Idempotent**: Running this command multiple times with the same inputs should produce the same result (though it should warn if files already exist). @@ -555,16 +722,22 @@ When failing, provide a clear error message explaining: ## Arguments -- `<enhancement-pr-url>` (optional if design-doc provided): GitHub PR URL to the OpenShift enhancement proposal +- `<enhancement-pr-url>` (optional if another source provided): GitHub PR URL to the OpenShift enhancement proposal - Format: `https://github.com/openshift/enhancements/pull/<number>` -- `--design-doc <gist-url>` (optional if EP provided): GitHub Gist URL containing detailed API specifications +- `--design-doc <gist-url>` (optional if another source provided): GitHub Gist URL containing detailed API specifications - Supported formats: - `https://gist.github.com/username/gist_id` - `https://gist.github.com/gist_id` - `https://gist.githubusercontent.com/username/gist_id/raw/...` -**At least one input source (EP or design document) must be provided.** +- `--jira <ticket-key-or-url>` (optional if another source provided): Jira ticket providing specific requirements and acceptance criteria + - Supported formats: + - Ticket key: `OCPBUGS-12345`, `RFE-1234`, `HOSTEDCP-567` + - Full URL: `https://issues.redhat.com/browse/OCPBUGS-12345` + - Requires `JIRA_PERSONAL_TOKEN` environment variable + +**At least one input source (EP, design document, or Jira ticket) must be provided.** ## Design Document Expected Format @@ -599,15 +772,18 @@ When using a design document, it should contain structured implementation detail - **gh** (GitHub CLI) — installed and authenticated (`gh auth login`) - **go** — Go toolchain installed - **git** — Git installed +- **jq** — JSON processor (for parsing Jira API responses) +- **JIRA_PERSONAL_TOKEN** — Personal access token for Jira REST API (required when using `--jira`) - Must be run from within an OpenShift operator repository (Go module that references `github.com/openshift/api`) ## Exit Conditions - **Success**: API type definitions generated/modified with a summary of all changes - **Failure Scenarios**: - - No input provided (neither EP nor design document) - - Invalid enhancement PR URL or gist URL + - No input provided (neither EP, design document, nor Jira ticket) + - Invalid enhancement PR URL, gist URL, or Jira ticket key - Missing required tools or unauthenticated GitHub CLI + - Missing `JIRA_PERSONAL_TOKEN` when `--jira` is used - Not inside a valid OpenShift operator repository - Input source(s) inaccessible - No API changes found in the input sources diff --git a/plugins/oape/commands/api-implement.md b/plugins/oape/commands/api-implement.md index 42c90ba..ad67d23 100644 --- a/plugins/oape/commands/api-implement.md +++ b/plugins/oape/commands/api-implement.md @@ -1,6 +1,6 @@ --- -description: Generate OpenShift controller/reconciler implementation code from an enhancement proposal PR and/or design document, following controller-runtime and operator-sdk conventions -argument-hint: <enhancement-pr-url> [--design-doc <gist-url>] +description: Generate OpenShift controller/reconciler implementation code from an enhancement proposal PR, design document, and/or Jira ticket, following controller-runtime and operator-sdk conventions +argument-hint: <enhancement-pr-url> [--design-doc <gist-url>] [--jira <ticket-key-or-url>] --- ## Name @@ -16,16 +16,29 @@ oape:api-implement # Design document only /oape:api-implement --design-doc <https://gist.github.com/user/gist_id> + +# Jira ticket only +/oape:api-implement --jira OCPBUGS-12345 + +# Jira ticket with full URL +/oape:api-implement --jira https://issues.redhat.com/browse/OCPBUGS-12345 + +# EP + Jira ticket +/oape:api-implement <https://github.com/openshift/enhancements/pull/NNNN> --jira OCPBUGS-12345 + +# All three sources +/oape:api-implement <https://github.com/openshift/enhancements/pull/NNNN> --design-doc <https://gist.github.com/user/gist_id> --jira OCPBUGS-12345 ``` ## Description -The `oape:api-implement` command reads an OpenShift enhancement proposal PR and/or a design document (GitHub Gist), extracts the required implementation logic, and generates complete controller/reconciler code in the correct paths of the current OpenShift operator repository. +The `oape:api-implement` command reads an OpenShift enhancement proposal PR, a design document (GitHub Gist), and/or a Jira ticket, extracts the required implementation logic, and generates complete controller/reconciler code in the correct paths of the current OpenShift operator repository. **Input Sources:** - **Enhancement Proposal (EP)**: High-level requirements, constraints, and context from an openshift/enhancements PR - **Design Document (Gist)**: Detailed implementation specifications including reconciliation workflow, dependent resources, and controller behavior +- **Jira Ticket**: Specific implementation requirements, acceptance criteria, and component context from a Jira issue -When both sources are provided, the design document takes precedence for implementation details while the EP provides high-level context. +When multiple sources are provided, precedence is: design document > Jira ticket > EP. The design document takes precedence for exact implementation details, the Jira ticket provides specific requirements and acceptance criteria, and the EP provides high-level context. This command generates **production-ready code with zero TODOs** by: 1. Parsing the input sources for explicit business logic requirements @@ -47,34 +60,52 @@ All prechecks must pass before proceeding. If ANY precheck fails, STOP immediate #### Precheck 1 — Parse and Validate Input Arguments -The command accepts an Enhancement Proposal URL and/or a design document (gist) URL. At least one must be provided. +The command accepts an Enhancement Proposal URL, a design document (gist) URL, and/or a Jira ticket key/URL. At least one must be provided. ```bash ARGS="$ARGUMENTS" ENHANCEMENT_PR="" DESIGN_DOC_URL="" +JIRA_INPUT="" +JIRA_TICKET_KEY="" ENHANCEMENT_PR_NUMBER="" +# Extract --jira argument if present +if echo "$ARGS" | grep -q '\-\-jira'; then + JIRA_INPUT=$(echo "$ARGS" | sed -n 's/.*--jira[[:space:]]\+\([^[:space:]]\+\).*/\1/p') + ARGS=$(echo "$ARGS" | sed 's/--jira[[:space:]]\+[^[:space:]]\+//') + # Guard against accidental flag-as-value (e.g., --jira --design-doc) + if echo "$JIRA_INPUT" | grep -qE '^--'; then + echo "PRECHECK FAILED: --jira value looks like a flag: $JIRA_INPUT" + echo "Provide a ticket key (e.g., OCPBUGS-12345) or URL after --jira." + exit 1 + fi +fi + # Extract --design-doc argument if present if echo "$ARGS" | grep -q '\-\-design-doc'; then DESIGN_DOC_URL=$(echo "$ARGS" | sed -n 's/.*--design-doc[[:space:]]\+\([^[:space:]]\+\).*/\1/p') # Remove --design-doc and its value from ARGS to get EP URL ENHANCEMENT_PR=$(echo "$ARGS" | sed 's/--design-doc[[:space:]]\+[^[:space:]]\+//' | xargs) else - ENHANCEMENT_PR="$ARGS" + ENHANCEMENT_PR=$(echo "$ARGS" | xargs) fi # Validate at least one input is provided -if [ -z "$ENHANCEMENT_PR" ] && [ -z "$DESIGN_DOC_URL" ]; then +if [ -z "$ENHANCEMENT_PR" ] && [ -z "$DESIGN_DOC_URL" ] && [ -z "$JIRA_INPUT" ]; then echo "PRECHECK FAILED: No input provided." echo "Usage:" - echo " /oape:api-implement <EP_URL> [--design-doc <GIST_URL>]" + echo " /oape:api-implement <EP_URL> [--design-doc <GIST_URL>] [--jira <TICKET_KEY_OR_URL>]" echo " /oape:api-implement --design-doc <GIST_URL>" + echo " /oape:api-implement --jira OCPBUGS-12345" + echo " /oape:api-implement --jira https://issues.redhat.com/browse/OCPBUGS-12345" echo "" echo "Examples:" echo " /oape:api-implement https://github.com/openshift/enhancements/pull/1234" echo " /oape:api-implement https://github.com/openshift/enhancements/pull/1234 --design-doc https://gist.github.com/user/abc123" echo " /oape:api-implement --design-doc https://gist.github.com/user/abc123" + echo " /oape:api-implement --jira OCPBUGS-12345" + echo " /oape:api-implement https://github.com/openshift/enhancements/pull/1234 --jira OCPBUGS-12345" exit 1 fi @@ -89,7 +120,7 @@ if [ -n "$ENHANCEMENT_PR" ]; then ENHANCEMENT_PR_NUMBER=$(echo "$ENHANCEMENT_PR" | grep -oE '[0-9]+$') echo "Enhancement PR #$ENHANCEMENT_PR_NUMBER validated." else - echo "No Enhancement PR provided. Using design document only." + echo "No Enhancement PR provided." fi # Validate Design Document URL if provided @@ -106,13 +137,41 @@ if [ -n "$DESIGN_DOC_URL" ]; then fi echo "Design document URL validated: $DESIGN_DOC_URL" else - echo "No design document provided. Using Enhancement PR only." + echo "No design document provided." +fi + +# Validate and parse Jira ticket if provided +if [ -n "$JIRA_INPUT" ]; then + # If input is a URL, extract the ticket key + if echo "$JIRA_INPUT" | grep -qE '^https?://'; then + JIRA_TICKET_KEY=$(echo "$JIRA_INPUT" | sed -n 's|.*/browse/\([A-Z][A-Z0-9]*-[0-9][0-9]*\).*|\1|p') + if [ -z "$JIRA_TICKET_KEY" ]; then + echo "PRECHECK FAILED: Could not extract ticket key from Jira URL." + echo "Expected format: https://issues.redhat.com/browse/PROJECT-12345" + echo "Got: $JIRA_INPUT" + exit 1 + fi + else + JIRA_TICKET_KEY="$JIRA_INPUT" + fi + + # Validate ticket key format (PROJECT-NUMBER) + if ! echo "$JIRA_TICKET_KEY" | grep -qE '^[A-Z][A-Z0-9]+-[0-9]+$'; then + echo "PRECHECK FAILED: Invalid Jira ticket key format." + echo "Expected format: PROJECT-12345 (e.g., OCPBUGS-12345, RFE-1234)" + echo "Got: $JIRA_TICKET_KEY" + exit 1 + fi + echo "Jira ticket key validated: $JIRA_TICKET_KEY" +else + echo "No Jira ticket provided." fi echo "" echo "=== Input Sources ===" [ -n "$ENHANCEMENT_PR" ] && echo " Enhancement PR: $ENHANCEMENT_PR" [ -n "$DESIGN_DOC_URL" ] && echo " Design Document: $DESIGN_DOC_URL" +[ -n "$JIRA_TICKET_KEY" ] && echo " Jira Ticket: $JIRA_TICKET_KEY" echo "=====================" ``` @@ -247,7 +306,60 @@ else fi ``` -#### Precheck 6 — Verify API Types Exist +#### Precheck 6 — Verify Jira Ticket is Accessible (if provided) + +```bash +JIRA_SUMMARY="" + +if [ -n "$JIRA_TICKET_KEY" ]; then + echo "Verifying Jira ticket $JIRA_TICKET_KEY is accessible..." + + JIRA_URL="${JIRA_URL:-https://issues.redhat.com}" + + # Check if JIRA_PERSONAL_TOKEN is set + if [ -z "$JIRA_PERSONAL_TOKEN" ]; then + echo "PRECHECK FAILED: JIRA_PERSONAL_TOKEN environment variable is not set." + echo "" + echo "Setup instructions:" + echo " 1. Visit: https://issues.redhat.com/secure/ViewProfile.jspa?selectedTab=com.atlassian.pats.pats-plugin:jira-user-personal-access-tokens" + echo " 2. Create a personal access token" + echo " 3. Export it: export JIRA_PERSONAL_TOKEN=\"your_token_here\"" + exit 1 + fi + + # Fetch issue summary to verify accessibility + JIRA_RESPONSE=$(curl -sS -w "\n%{http_code}" \ + -H "Authorization: Bearer $JIRA_PERSONAL_TOKEN" \ + -H "Accept: application/json" \ + "$JIRA_URL/rest/api/2/issue/$JIRA_TICKET_KEY?fields=summary") + + JIRA_HTTP_CODE=$(echo "$JIRA_RESPONSE" | tail -1) + JIRA_BODY=$(echo "$JIRA_RESPONSE" | sed '$d') + + if [ "$JIRA_HTTP_CODE" = "200" ]; then + JIRA_SUMMARY=$(echo "$JIRA_BODY" | jq -r '.fields.summary // "Untitled"') + echo "Jira ticket verified: $JIRA_TICKET_KEY - $JIRA_SUMMARY" + elif [ "$JIRA_HTTP_CODE" = "401" ]; then + echo "PRECHECK FAILED: Jira authentication failed (HTTP 401)." + echo "Your JIRA_PERSONAL_TOKEN may be invalid or expired." + exit 1 + elif [ "$JIRA_HTTP_CODE" = "403" ]; then + echo "PRECHECK FAILED: Access denied to Jira ticket $JIRA_TICKET_KEY (HTTP 403)." + exit 1 + elif [ "$JIRA_HTTP_CODE" = "404" ]; then + echo "PRECHECK FAILED: Jira ticket $JIRA_TICKET_KEY not found (HTTP 404)." + echo "Verify the ticket key and check: $JIRA_URL/browse/$JIRA_TICKET_KEY" + exit 1 + else + echo "PRECHECK FAILED: Unable to access Jira ticket $JIRA_TICKET_KEY (HTTP $JIRA_HTTP_CODE)." + exit 1 + fi +else + echo "Skipping Jira ticket validation (not provided)." +fi +``` + +#### Precheck 7 — Verify API Types Exist ```bash echo "Checking if API types exist in the repository..." @@ -265,7 +377,7 @@ echo "Found API types:" echo "$API_TYPES" | head -10 ``` -#### Precheck 7 — Verify Clean Working Tree (Warning) +#### Precheck 8 — Verify Clean Working Tree (Warning) ```bash if ! git diff --quiet || ! git diff --cached --quiet; then @@ -395,7 +507,7 @@ I will apply the correct patterns based on detected type. ### Phase 3: Fetch and Parse Input Sources -Fetch content from all provided input sources (Enhancement Proposal and/or Design Document). +Fetch content from all provided input sources (Enhancement Proposal, Design Document, and/or Jira Ticket). #### 3.1 Fetch Enhancement Proposal (if provided) @@ -440,18 +552,61 @@ If the `gh api` command fails, try fetching via curl: curl -sL "https://api.github.com/gists/$GIST_ID" | jq -r '.files | to_entries[] | "=== FILE: \(.key) ===\n\(.value.content)\n"' ``` -#### 3.3 Extract Structured Requirements +#### 3.3 Fetch Jira Ticket (if provided) + +```bash +if [ -n "$JIRA_TICKET_KEY" ]; then + echo "Fetching Jira ticket $JIRA_TICKET_KEY..." + + JIRA_URL="${JIRA_URL:-https://issues.redhat.com}" + + # Fetch full issue details including description, acceptance criteria, components, and linked issues + # customfield_12316840 = Target Release, customfield_12319940 = Story Points (Red Hat Jira) + curl -sS \ + -H "Authorization: Bearer $JIRA_PERSONAL_TOKEN" \ + -H "Accept: application/json" \ + "$JIRA_URL/rest/api/2/issue/$JIRA_TICKET_KEY?fields=summary,description,components,labels,status,issuetype,issuelinks,fixVersions,customfield_12316840,customfield_12319940" +fi +``` + +From the Jira response, extract: +- **Summary**: High-level description of the requirement +- **Description**: Full requirement text (strip Jira wiki markup) +- **Components**: Affected OpenShift components +- **Labels**: Relevant labels (e.g., TechPreview, API-change) +- **Acceptance Criteria**: From description or custom fields — these map to specific controller behaviors +- **Linked Issues**: Related EPs, bugs (bugs reveal edge cases the controller must handle), or other stories +- **Fix Versions**: Target OpenShift version — affects feature gating + +#### 3.4 Extract Structured Requirements ```thinking I MUST extract structured information from the input source(s). The approach depends on what was provided: -**If BOTH Enhancement Proposal AND Design Document are provided:** +**Precedence rules (highest to lowest):** +1. Design Document — exact implementation details +2. Jira Ticket — specific requirements and acceptance criteria +3. Enhancement Proposal — high-level context and constraints + +**If Design Document, Jira Ticket, AND Enhancement Proposal are all provided:** +- EP provides high-level context: motivation, constraints, affected components +- Jira ticket adds specific requirements: acceptance criteria mapping to controller behaviors, component details, fix versions +- Design Document provides implementation details: reconciliation workflow, dependent resources, controller behavior +- For conflicts: Design Document > Jira Ticket > EP + +**If BOTH Enhancement Proposal AND Design Document are provided (no Jira):** - The EP provides high-level context: motivation, constraints, affected components - The Design Document provides implementation details: reconciliation workflow, dependent resources, controller behavior - When both specify the same information, the Design Document takes precedence - Extract from EP: component context, FeatureGate requirements, general constraints - Extract from Design Document: exact reconciliation steps, dependent resources, status updates, events +**If Jira Ticket is provided (alone or with EP, without Design Document):** +- Extract from Jira: acceptance criteria that define controller behaviors, component scope, linked bugs revealing edge cases +- Jira tickets often have specific implementation requirements closer to code-level detail than EPs +- Check linked issues for additional context (linked EPs, related bugs, stories) +- Extract from EP (if provided): broader motivation, architectural constraints + **If only Enhancement Proposal is provided:** - Extract all requirements from the EP (original behavior) @@ -459,6 +614,15 @@ I MUST extract structured information from the input source(s). The approach dep - The Design Document must be comprehensive enough to generate controller code - It should specify: API details, reconciliation workflow, dependent resources, status conditions +**If only Jira Ticket is provided:** +- The ticket must contain enough detail to determine controller behavior +- If the ticket is sparse (missing description, no acceptance criteria, or lacks implementation details), + WARN the user and ask clarifying questions such as: + - "What reconciliation steps should the controller perform?" + - "What dependent resources should it manage?" + - "What status conditions should it report?" + Then continue with the combined information + From the combined sources, I will extract the following. For each item, I will search for specific sections, keywords, and patterns. ## EXTRACTION CHECKLIST @@ -575,11 +739,12 @@ Based on all above, compute: - [ ] Events: create, patch **Source Merging Rules:** -When both EP and Design Document are provided: +When multiple sources are provided, precedence is: Design Document > Jira Ticket > EP. 1. Design Document takes precedence for implementation-specific details -2. EP provides context and constraints -3. If there are conflicts, prefer Design Document specifics -4. Document any significant conflicts in the output summary +2. Jira Ticket provides specific requirements, acceptance criteria, and edge cases from linked bugs +3. EP provides context and constraints +4. If there are conflicts, prefer higher-precedence source specifics +5. Document any significant conflicts in the output summary If ANY required section (A, B, C, F, I) is missing or ambiguous across ALL provided sources, I MUST stop and ask the user for clarification. I will NOT guess. ``` @@ -1285,6 +1450,7 @@ After generating all files, provide a comprehensive summary: Input Sources: Enhancement PR: <url> (if provided) Design Document: <gist-url> (if provided) + Jira Ticket: <key> - <summary> (if provided) Enhancement Title: <title> (if EP provided) Operator Type: <controller-runtime | library-go> @@ -1344,8 +1510,9 @@ Cleanup on Deletion: Feature Gate: <FeatureGateName> (if applicable) -Source Conflicts Resolved: (if both EP and design doc provided) - - <item>: Used design doc specification (<reason>) +Source Conflicts Resolved: (if multiple sources provided) + - <item>: Used design doc specification over Jira ticket (<reason>) + - <item>: Used Jira acceptance criteria over EP (<reason>) Next Steps: 1. Review the generated controller code @@ -1362,22 +1529,24 @@ Next Steps: The command MUST FAIL and STOP immediately if ANY of the following are true: -1. **No input provided**: Neither an enhancement PR URL nor a design document URL was provided +1. **No input provided**: Neither an enhancement PR URL, design document URL, nor Jira ticket key was provided 2. **Invalid PR URL**: The provided EP URL is not a valid `openshift/enhancements` PR 3. **Invalid gist URL**: The provided design document URL is not a valid GitHub Gist -4. **Missing tools**: `gh`, `go`, `git`, or `make` not installed -5. **Not authenticated**: `gh` not authenticated -6. **Not an operator repo**: No go.mod or not a recognized operator type -7. **No API types**: API types don't exist (run `/oape:api-generate` first) -8. **Input not accessible**: Enhancement PR or design document cannot be fetched -9. **No implementation requirements**: Input sources don't describe controller behavior -10. **Ambiguous requirements**: Cannot determine reconciliation workflow from input sources -11. **Unsupported framework**: Repository does not use controller-runtime or library-go +4. **Invalid Jira ticket**: The provided Jira ticket key does not match PROJECT-NUMBER format +5. **Missing tools**: `gh`, `go`, `git`, or `make` not installed +6. **Not authenticated**: `gh` not authenticated +7. **Not an operator repo**: No go.mod or not a recognized operator type +8. **No API types**: API types don't exist (run `/oape:api-generate` first) +9. **Input not accessible**: Enhancement PR, design document, or Jira ticket cannot be fetched +10. **Jira token missing**: `--jira` was provided but `JIRA_PERSONAL_TOKEN` is not set +11. **No implementation requirements**: Input sources don't describe controller behavior +12. **Ambiguous requirements**: Cannot determine reconciliation workflow from input sources +13. **Unsupported framework**: Repository does not use controller-runtime or library-go ## Behavioral Rules 1. **Never guess**: If input sources are ambiguous, STOP and ask the user for clarification -2. **Design document precedence**: When both EP and design document are provided, the design document takes precedence for implementation details +2. **Source precedence**: When multiple sources are provided, precedence is: design document > Jira ticket > EP for implementation details 3. **Zero TODOs**: Generate actual implementation code, not placeholders 4. **Convention over proposal**: Apply framework best practices even if input sources differ 5. **Match existing patterns**: Replicate patterns from existing controllers in the repo @@ -1390,16 +1559,22 @@ The command MUST FAIL and STOP immediately if ANY of the following are true: ## Arguments -- `<enhancement-pr-url>` (optional if design-doc provided): GitHub PR URL to the OpenShift enhancement proposal +- `<enhancement-pr-url>` (optional if another source provided): GitHub PR URL to the OpenShift enhancement proposal - Format: `https://github.com/openshift/enhancements/pull/<number>` -- `--design-doc <gist-url>` (optional if EP provided): GitHub Gist URL containing detailed implementation specifications +- `--design-doc <gist-url>` (optional if another source provided): GitHub Gist URL containing detailed implementation specifications - Supported formats: - `https://gist.github.com/username/gist_id` - `https://gist.github.com/gist_id` - `https://gist.githubusercontent.com/username/gist_id/raw/...` -**At least one input source (EP or design document) must be provided.** +- `--jira <ticket-key-or-url>` (optional if another source provided): Jira ticket providing specific requirements and acceptance criteria + - Supported formats: + - Ticket key: `OCPBUGS-12345`, `RFE-1234`, `HOSTEDCP-567` + - Full URL: `https://issues.redhat.com/browse/OCPBUGS-12345` + - Requires `JIRA_PERSONAL_TOKEN` environment variable + +**At least one input source (EP, design document, or Jira ticket) must be provided.** ## Design Document Expected Format @@ -1446,6 +1621,8 @@ When using a design document for controller implementation, it should contain: - **go** — Go toolchain installed - **git** — Git installed - **make** — Make installed +- **jq** — JSON processor (for parsing Jira API responses) +- **JIRA_PERSONAL_TOKEN** — Personal access token for Jira REST API (required when using `--jira`) - Must be run from within an OpenShift operator repository - API types MUST exist (run `/oape:api-generate` first)