From d84cb15c9415922f604c131bc5a0beb48624f0be Mon Sep 17 00:00:00 2001 From: Andy Pickering Date: Mon, 6 Jul 2026 16:02:15 +0900 Subject: [PATCH 1/3] Update specs to document Konflux CI pipeline and .tekton/ directory Co-Authored-By: Claude Opus 4.6 --- .ai/spec/README.md | 2 +- .ai/spec/how/e2e-testing.md | 18 ++++++++++++++++-- .ai/spec/how/project-structure.md | 3 +++ 3 files changed, 20 insertions(+), 3 deletions(-) diff --git a/.ai/spec/README.md b/.ai/spec/README.md index 6f8c7ef..f49b6dc 100644 --- a/.ai/spec/README.md +++ b/.ai/spec/README.md @@ -28,7 +28,7 @@ AI agents. Content is optimized for precision and machine consumption. | Navigate the codebase | `how/project-structure.md` | | Understand K8s data flow | `how/k8s-data-layer.md` | | Understand the plugin system | `how/console-plugin-system.md` | -| Understand e2e testing | `how/e2e-testing.md` | +| Understand CI and e2e testing | `how/e2e-testing.md` | ## Cross-Reference diff --git a/.ai/spec/how/e2e-testing.md b/.ai/spec/how/e2e-testing.md index acec7b3..f97863a 100644 --- a/.ai/spec/how/e2e-testing.md +++ b/.ai/spec/how/e2e-testing.md @@ -1,6 +1,20 @@ -# E2E Testing +# Testing -## Framework +## Konflux CI Pipeline + +`.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml` defines a Tekton +Pipeline that runs as a Konflux integration test on every PR. It checks out the PR commit +and runs three checks sequentially: + +1. `npm run lint` — ESLint, Prettier, and Stylelint +2. `npm run test` — unit tests +3. `npm run i18n` — verifies locale files are up to date + +The pipeline uses the Playwright base image (`mcr.microsoft.com/playwright`) for its Node.js +toolchain and runs with a 4Gi memory limit. It extracts the commit SHA from the Konflux +SNAPSHOT parameter to check out the exact PR revision. + +## E2E Framework Playwright with `@playwright/test`. Mirrors the setup used by the sibling `lightspeed-console` repo. diff --git a/.ai/spec/how/project-structure.md b/.ai/spec/how/project-structure.md index 44fad8e..9cd4feb 100644 --- a/.ai/spec/how/project-structure.md +++ b/.ai/spec/how/project-structure.md @@ -29,6 +29,9 @@ | `integration-tests/support/fixtures.ts` | `test`, `oc`, `gatherClusterArtifacts` | Custom test fixture, cluster CLI helper, artifact collection | | `integration-tests/support/global-setup.ts` | `globalSetup` | Operator readiness, browser login, storageState | | `integration-tests/support/global-teardown.ts` | `globalTeardown` | Cluster cleanup and artifact gathering | +| `.tekton/lightspeed-agentic-console-pull-request.yaml` | — | Konflux PipelineRun for PR builds | +| `.tekton/lightspeed-agentic-console-push.yaml` | — | Konflux PipelineRun for push builds | +| `.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml` | — | Konflux integration test Pipeline running lint, unit tests, and i18n checks | ## Key Entry Points From ac531f8a04a66622a4133fac239495062762c130 Mon Sep 17 00:00:00 2001 From: Andy Pickering Date: Thu, 25 Jun 2026 14:44:18 +0900 Subject: [PATCH 2/3] Add lint integration test pipeline for Konflux pre-commit checks Runs lint, unit tests, and i18n validation on PRs via a Tekton integration test pipeline, following the same pattern used in lightspeed-console. Co-Authored-By: Claude Opus 4.6 --- ...lightspeed-agentic-console-pre-commit.yaml | 85 +++++++++++++++++++ 1 file changed, 85 insertions(+) create mode 100644 .tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml diff --git a/.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml b/.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml new file mode 100644 index 0000000..9e96280 --- /dev/null +++ b/.tekton/integration-tests/lightspeed-agentic-console-pre-commit.yaml @@ -0,0 +1,85 @@ +--- +apiVersion: tekton.dev/v1beta1 +kind: Pipeline +metadata: + name: lightspeed-agentic-console-pre-commits +spec: + description: | + This pipeline runs pre-commit checks for OpenShift Lightspeed Agentic Console. + params: + - name: SNAPSHOT + description: 'The JSON string representing the snapshot of the application under test.' + default: '{"components": [{"name":"test-app", "containerImage": "quay.io/example/repo:latest", "source": {"git": {"revision": "main"}}}]}' + - name: test-name + description: 'The name of the test corresponding to a defined Konflux integration test.' + default: 'lightspeed-agentic-console-pre-commits' + tasks: + - name: extract-snapshot-metadata + description: Extract console image and commit from SNAPSHOT input + params: + - name: SNAPSHOT + value: $(params.SNAPSHOT) + taskSpec: + results: + - name: console-image + value: "$(steps.get-snapshot-images.results.console-image)" + - name: commit + value: "$(steps.get-snapshot-images.results.commit)" + params: + - name: SNAPSHOT + steps: + - name: get-snapshot-images + image: registry.redhat.io/openshift4/ose-cli:latest + env: + - name: SNAPSHOT + value: $(params.SNAPSHOT) + results: + - name: console-image + type: string + description: "console image from snapshot" + - name: commit + type: string + description: "commit sha to be used in console tests" + script: | + dnf -y install jq + echo -n "$(jq -r --arg component_name "lightspeed-agentic-console" '.components[] | select(.name == $component_name) | .containerImage' <<< "$SNAPSHOT")" > $(step.results.console-image.path) + echo -n "$(jq -r --arg component_name "lightspeed-agentic-console" '.components[] | select(.name == $component_name) | .source.git.revision' <<< "$SNAPSHOT")" > $(step.results.commit.path) + - name: lint + description: Run static checks + runAfter: + - extract-snapshot-metadata + params: + - name: commit + value: $(tasks.extract-snapshot-metadata.results.commit) + taskSpec: + params: + - name: commit + steps: + - name: run-lint + env: + - name: COMMIT_SHA + value: $(params.commit) + image: mcr.microsoft.com/playwright:v1.60.0-noble + resources: + limits: + memory: 4Gi + script: | + #!/bin/bash + set -euo pipefail + cd /home + git clone https://github.com/openshift/lightspeed-agentic-console.git + cd lightspeed-agentic-console + git fetch origin "${COMMIT_SHA}" + git config advice.detachedHead false + git checkout "${COMMIT_SHA}" + echo "---------------------------------------------" + echo "node version: $(node -v)" + echo "npm version: $(npm -v)" + echo "---------------------------------------------" + NODE_OPTIONS=--max-old-space-size=4096 npm ci --no-fund + echo "---------------------------------------------" + npm run lint + echo "---------------------------------------------" + npm run test + echo "---------------------------------------------" + npm run i18n From 4dc4b2c4daa7649f9c08899af3463f9dbdc66155 Mon Sep 17 00:00:00 2001 From: Andy Pickering Date: Thu, 25 Jun 2026 14:54:18 +0900 Subject: [PATCH 3/3] Fix linter errors and warnings --- locales/en/plugin__lightspeed-agentic-console-plugin.json | 3 ++- src/components/configuration/AgentForm.tsx | 1 + src/components/configuration/ApprovalPolicyTab.tsx | 2 ++ src/components/proposals/ProposalDetailPage.tsx | 7 ++++++- src/components/proposals/SandboxLogViewer.tsx | 2 ++ 5 files changed, 13 insertions(+), 2 deletions(-) diff --git a/locales/en/plugin__lightspeed-agentic-console-plugin.json b/locales/en/plugin__lightspeed-agentic-console-plugin.json index 8af40b8..afb60dc 100644 --- a/locales/en/plugin__lightspeed-agentic-console-plugin.json +++ b/locales/en/plugin__lightspeed-agentic-console-plugin.json @@ -8,10 +8,12 @@ "After execution, a separate verification agent with read-only cluster access independently checks whether the fix worked. It inspects actual cluster state, not the execution agent's self-reported results. The checks shown below are the analysis agent's recommendations. The verification agent uses its own judgment based on what it observes, and every check it runs is reported transparently in the Verification tab. If verification fails, and retries were selected at approval, the Lightspeed operator will automatically retry execution with the failure reasons included as context for the execution agent.": "After execution, a separate verification agent with read-only cluster access independently checks whether the fix worked. It inspects actual cluster state, not the execution agent's self-reported results. The checks shown below are the analysis agent's recommendations. The verification agent uses its own judgment based on what it observes, and every check it runs is reported transparently in the Verification tab. If verification fails, and retries were selected at approval, the Lightspeed operator will automatically retry execution with the failure reasons included as context for the execution agent.", "Age": "Age", "Agent tiers define the model and settings used at each stage of a proposal workflow.": "Agent tiers define the model and settings used at each stage of a proposal workflow.", + "Agentic system is suspended": "Agentic system is suspended", "Agents": "Agents", "AI Hub": "AI Hub", "Alert Diagnosis: {{name}}": "Alert Diagnosis: {{name}}", "Alert rule created successfully": "Alert rule created successfully", + "All operations are halted and new proposals will be terminated. Remove or update the AgenticOLSConfig to resume.": "All operations are halted and new proposals will be terminated. Remove or update the AgenticOLSConfig to resume.", "Analysis (seconds)": "Analysis (seconds)", "Analysis did not complete — no proposal was generated.": "Analysis did not complete — no proposal was generated.", "Analysis Sandbox": "Analysis Sandbox", @@ -27,7 +29,6 @@ "Approve the escalation step for proposal {{name}}. The agent will research the issue, draft a support case, and file it.": "Approve the escalation step for proposal {{name}}. The agent will research the issue, draft a support case, and file it.", "Approve Verification": "Approve Verification", "Approve with {{num}} retries": "Approve with {{num}} retries", - "Approve with retries": "Approve with retries", "Auto-scroll": "Auto-scroll", "Automatic": "Automatic", "Before": "Before", diff --git a/src/components/configuration/AgentForm.tsx b/src/components/configuration/AgentForm.tsx index 6e75d4b..f4aa2ef 100644 --- a/src/components/configuration/AgentForm.tsx +++ b/src/components/configuration/AgentForm.tsx @@ -28,6 +28,7 @@ const AgentForm: React.FC = ({ providers, onSubmit, onCancel }) React.useEffect(() => { if (!providerName && providers.length > 0) { + // eslint-disable-next-line react-hooks/set-state-in-effect setProviderName(providers[0].metadata.name); } }, [providers, providerName]); diff --git a/src/components/configuration/ApprovalPolicyTab.tsx b/src/components/configuration/ApprovalPolicyTab.tsx index a4fae4e..f1cd559 100644 --- a/src/components/configuration/ApprovalPolicyTab.tsx +++ b/src/components/configuration/ApprovalPolicyTab.tsx @@ -54,6 +54,7 @@ const ApprovalPolicyTab: React.FC = () => { React.useEffect(() => { if (policyExists) { const stages = policy.spec?.stages; + // eslint-disable-next-line react-hooks/set-state-in-effect setStageValues({ Analysis: getStageApproval(stages, 'Analysis'), Execution: getStageApproval(stages, 'Execution'), @@ -62,6 +63,7 @@ const ApprovalPolicyTab: React.FC = () => { }); setMaxAttempts(policy.spec?.maxAttempts ?? 1); } + // eslint-disable-next-line react-hooks/exhaustive-deps }, [policyResourceVersion]); const handleSave = async () => { diff --git a/src/components/proposals/ProposalDetailPage.tsx b/src/components/proposals/ProposalDetailPage.tsx index cc7fd1f..7516b91 100644 --- a/src/components/proposals/ProposalDetailPage.tsx +++ b/src/components/proposals/ProposalDetailPage.tsx @@ -969,6 +969,7 @@ const ProposalTab: React.FC = ({ ); React.useEffect(() => { if (options.length === 1) { + // eslint-disable-next-line react-hooks/set-state-in-effect setLocalSelectedOption(0); } }, [options.length]); @@ -984,6 +985,7 @@ const ProposalTab: React.FC = ({ const [execAgent, setExecAgent] = React.useState(proposal.spec.execution?.agent ?? ''); React.useEffect(() => { + // eslint-disable-next-line react-hooks/set-state-in-effect setExecAgent(proposal.spec.execution?.agent ?? ''); }, [proposal.spec.execution?.agent]); @@ -1885,6 +1887,8 @@ const ProposalDetailPage: React.FC = () => { executionApproval.error || verificationApproval.error || escalationApproval.error; + + /* eslint-disable react-hooks/exhaustive-deps */ const clearError = React.useCallback(() => { analysisApproval.clearError(); executionApproval.clearError(); @@ -1896,6 +1900,7 @@ const ProposalDetailPage: React.FC = () => { verificationApproval.clearError, escalationApproval.clearError, ]); + /* eslint-enable react-hooks/exhaustive-deps */ const [escalateOpen, setEscalateOpen] = React.useState(false); if (!loaded) { @@ -2099,10 +2104,10 @@ const ProposalDetailPage: React.FC = () => { > {effectiveTab === 'overview' && ( )} {effectiveTab === 'proposal' && ( diff --git a/src/components/proposals/SandboxLogViewer.tsx b/src/components/proposals/SandboxLogViewer.tsx index 35334da..e9c505f 100644 --- a/src/components/proposals/SandboxLogViewer.tsx +++ b/src/components/proposals/SandboxLogViewer.tsx @@ -80,6 +80,7 @@ const SandboxLogViewer: React.FC<{ React.useEffect(() => { if (!podExists) { + // eslint-disable-next-line react-hooks/set-state-in-effect setStatus((prev) => (prev === 'streaming' || prev === 'ended' ? 'ended' : 'searching')); } else if (podPhase === 'Pending') { setStatus('waiting'); @@ -91,6 +92,7 @@ const SandboxLogViewer: React.FC<{ return; } + // eslint-disable-next-line react-hooks/set-state-in-effect setStatus('streaming'); setLogs(''); const abortController = new AbortController();