Skip to content

feat(cloudbees): Operations Center + Feature Management enterprise support - #468

Merged
beng360 merged 37 commits into
mainfrom
feat/cloudbees-enterprise
Jun 10, 2026
Merged

beng360 merged 37 commits into
mainfrom
feat/cloudbees-enterprise

Conversation

@beng360

@beng360 beng360 commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Summary

Enhances the CloudBees connector from a simple Jenkins reskin to full enterprise support with Operations Center multi-controller management and Feature Management flag correlation.

What's new

Operations Center Client

  • Discovers all managed Jenkins controllers from a central OC instance
  • Queries builds across multiple controllers in parallel (capped at 20 controllers, 5 builds each)
  • SSRF protection: validates controller URLs match OC domain before sending credentials

Feature Management Client

  • Connects to CloudBees Feature Management API (Bearer token auth)
  • Queries recent flag changes for incident correlation ("was a flag toggled before this broke?")
  • 1 req/sec rate limiting with single retry on 429
  • URL-encoded path parameters (prevents path injection)

Enhanced RCA Tool — 3 new actions

  • flag_changes — "show me recent feature flag toggles that might correlate with this incident"
  • cross_controller_deployments — "what deployed across all controllers in the last 4 hours?"
  • controller_list — "what controllers are managed by this Operations Center?"

New routes

  • POST /connect-platform — connect OC + FM credentials
  • GET /platform-status — check what's connected
  • GET /controllers — list discovered controllers
  • POST /disconnect-platform — remove platform credentials

Architecture

Existing (unchanged):
  /connect → single Jenkins controller → cloudbees_rca actions

New (additive):
  /connect-platform → OC credentials (cloudbees_oc provider)
                     → FM credentials (cloudbees_fm provider)
  
  RCA agent → cloudbees_rca(action="flag_changes")
            → cloudbees_rca(action="cross_controller_deployments")  
            → cloudbees_rca(action="controller_list")

Security hardening (from review)

  • Path injection: app_id/flag_name URL-encoded via urllib.parse.quote
  • SSRF: controller URLs validated against OC domain before credential forwarding
  • Resource leaks: HTTP clients support context managers + explicit close()
  • Error sanitization: no raw exceptions returned to callers
  • URL scheme validation: only http/https accepted
  • Rate limit: FM client respects 1 req/sec + retries on 429

Backwards compatibility

  • Existing single-controller flow (/connect, all existing RCA actions) completely unchanged
  • New actions gracefully return "not_configured" message when OC/FM credentials don't exist
  • No database schema changes (uses existing user_tokens table with new provider names)

Test plan

  • Connect single CloudBees controller (existing flow still works)
  • Connect Operations Center — verify controller discovery
  • Connect Feature Management — verify token validation
  • Trigger RCA with flag_changes action — verify flag data returned
  • Trigger RCA with cross_controller_deployments — verify cross-controller aggregation
  • Test without OC/FM credentials — verify graceful "not_configured" messages
  • Test with invalid OC URL — verify scheme validation rejects non-http

🤖 Generated with Claude Code

Summary by CodeRabbit

  • New Features

    • Added CloudBees Operations Center integration for multi-controller visibility and build discovery
    • Added Feature Management integration for feature flag change tracking
    • Enhanced RCA with enterprise actions: controller discovery, cross-controller deployments, and flag changes
    • New connection flow supporting Operations Center and Feature Management configuration
  • Documentation

    • Added CloudBees integration guide with setup instructions and webhook configuration
    • Updated skill documentation with enterprise action guidance

@beng360
beng360 requested a review from a team as a code owner June 2, 2026 21:06
@coderabbitai

coderabbitai Bot commented Jun 2, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Note

Reviews paused

It looks like this branch is under active development. To avoid overwhelming you with review comments due to an influx of new commits, CodeRabbit has automatically paused this review. You can configure this behavior by changing the reviews.auto_review.auto_pause_after_reviewed_commits setting.

Use the following commands to manage reviews:

  • @coderabbitai resume to resume automatic reviews.
  • @coderabbitai review to trigger a single review.

Use the checkboxes below for quick actions:

  • ▶️ Resume reviews
  • 🔍 Trigger review

Walkthrough

This PR adds CloudBees Operations Center and Feature Management platform support to Aurora's CloudBees CI integration. New API clients enable discovery of managed controllers and querying of flag changes across deployments. Extended RCA actions support cross-controller incident investigation and optional feature-flag correlation. A new multi-step auth flow and platform credential routes support enterprise deployments.

Changes

CloudBees Enterprise Platform Integration

Layer / File(s) Summary
Feature Management API Client
server/connectors/cloudbees_connector/fm_client.py
CloudBeesFMClient provides bearer-token authentication, rate-limited HTTP requests, token validation, application listing, and filtering of flag changes by modification timestamp to support feature-flag correlation in RCA.
Operations Center API Client
server/connectors/cloudbees_connector/oc_client.py
CloudBeesOCClient handles URL validation, auth-mode selection, multi-endpoint server discovery, controller discovery with fallback strategies, controller URL domain validation, and cross-controller build queries with job and build limits.
Enterprise Platform Credential Routes
server/routes/cloudbees/cloudbees_routes.py
New endpoints for credential lifecycle: POST /connect-platform validates and persists OC/FM credentials and discovers controllers; GET /platform-status checks credential presence; GET /controllers runs discovery; POST|DELETE /disconnect-platform deletes stored credentials. Improved /connect URL validation and failure logging.
RCA Tool Enterprise Actions
server/chat/backend/agent/tools/cloudbees_rca_tool.py, server/chat/background/prediscovery_task.py, server/chat/backend/agent/tools/cloud_tools.py
Extended cloudbees_rca with three enterprise actions: flag_changes (requires FM+app_id), cross_controller_deployments (OC-based), and controller_list (OC-based). Added helper functions to instantiate per-user FM/OC clients. Removed deprecated trace_context action. Updated prediscovery agent instructions and tool descriptions.
Client-Side API Route Handlers
client/src/app/api/cloudbees/connect-platform/route.ts, client/src/app/api/cloudbees/controllers/route.ts, client/src/app/api/cloudbees/platform-status/route.ts
Three new Next.js API routes configured via createCIPostHandler and createCIGetHandler factories to proxy platform credential and controller discovery endpoints.
CloudBees Multi-Step Auth Component
client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx
New React component implementing three connection modes (Single Controller, Operations Center, PAT) with multi-step flow: mode selection (step 1), credential input with URL validation (step 2), webhook snippet display (step 3), and connected view showing metadata, summary stats, RCA toggle, webhook URLs, recent deployments, and managed controllers.
Auth Page Wrapper and Feature Flag Gating
client/src/app/cloudbees/auth/page.tsx, client/src/lib/feature-flags.ts, client/src/components/connectors/ConnectorRegistry.ts, .env.example
Wrapped CloudBeesAuthPage in ConnectorAuthGuard; added isCloudBeesEnabled feature flag helper; gated connector registration behind the NEXT_PUBLIC_ENABLE_CLOUDBEES flag.
Documentation and Skills
server/chat/backend/agent/skills/integrations/cloudbees/SKILL.md, server/chat/backend/agent/skills/integrations/cloudbees_oc/SKILL.md, website/docs/integrations/cloudbees.md
Updated CloudBees skill to v2.0 and removed trace_context. Added new cloudbees_oc skill documenting enterprise OC/FM actions. Added comprehensive website integration guide covering connection modes, webhook setup, standard vs enterprise RCA actions, optional feature-flag correlation, and auto-trigger RCA configuration.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • Arvo-AI/aurora#437: Adds is_cloudbees_connected() connectivity helper to cloudbees_rca_tool.py, which is foundational to this PR's extension of the same module with enterprise actions.
  • Arvo-AI/aurora#173: Introduces prediscovery background agent and build_prediscovery_prompt(...) logic; this PR extends prediscovery instructions to call new CloudBees OC enterprise RCA actions.

Suggested reviewers

  • isiddharthsingh
  • OlivierTrudeau
  • damianloch

Poem

🐰 A rabbit hops through controller clusters so wide,
Flags and builds cascade on the OC tide,
Three connection paths converge, no need to hide—
Enterprise RCA, with Aurora as guide!
🚀✨

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 72.09% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately describes the main addition: enterprise support through Operations Center and Feature Management integration, which represents the primary focus of the changeset.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feat/cloudbees-enterprise

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@beng360
beng360 marked this pull request as draft June 2, 2026 21:08
Comment thread server/chat/backend/agent/tools/cloudbees_rca_tool.py Fixed
Comment thread server/chat/backend/agent/tools/cloudbees_rca_tool.py Fixed
Comment thread server/chat/backend/agent/tools/cloudbees_rca_tool.py Fixed
Comment thread server/connectors/cloudbees_connector/oc_client.py Fixed

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 5

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@server/chat/backend/agent/tools/cloudbees_rca_tool.py`:
- Around line 157-171: The code returns a field named "warnings" mapped from the
variable error after calling oc_client.query_recent_builds_across_controllers,
which is confusing; update this by either changing the client's return name or
mapping the returned error value to a clearer local name before building the
JSON. Specifically, modify the call/response handling around
query_recent_builds_across_controllers (the tuple currently unpacked as success,
builds, error) so that the third element is named warnings (or warnings_list)
and use that when returning {"builds": builds, "count": len(builds),
"time_window_hours": time_window_hours, "warnings": warnings}, or alternatively
change the client function signature to return (success, builds, warnings)
consistently; keep oc_client.close() in the finally block unchanged.

In `@server/connectors/cloudbees_connector/fm_client.py`:
- Around line 81-89: The retry branch after receiving a 429 does not update the
rate limiter state, so update _last_request_time (or call _rate_limit_wait())
after the successful retry to reflect the retry's timestamp; specifically, in
the block around the second client.request (the retry using
client.request(method=method, url=url, params=params)), ensure you update the
module/class attribute _last_request_time (or invoke _rate_limit_wait())
immediately after the retry response is obtained and before returning or
proceeding, and preserve existing 429 handling (i.e., still return the same
error if the retry is 429).

In `@server/connectors/cloudbees_connector/oc_client.py`:
- Around line 175-231: The time_window_hours parameter in
query_recent_builds_across_controllers is unused; filter builds by timestamp
before returning them. After collecting builds into all_builds (and before
sorting), compute a cutoff = current_time_ms - time_window_hours * 3600 * 1000
and keep only builds where build.get("timestamp", 0) >= cutoff; ensure you
handle None/missing timestamps by excluding them, preserve existing
controller/job annotations (_controller, _job), and then sort and return as
before (using MAX_CONTROLLERS and MAX_BUILDS_PER_CONTROLLER unchanged).

In `@server/routes/cloudbees/cloudbees_routes.py`:
- Around line 366-368: The code returns the raw error from
oc_client.discover_controllers() directly in the HTTP response (the error
variable used in jsonify), which can leak sensitive info; change the response to
sanitize or replace the error with a safe message (e.g., "Failed to discover
controllers") and log the original error internally using the existing logger
before returning the generic message. Update the block that checks success from
oc_client.discover_controllers() to: log the original error
(logger.error/exception with the error variable), set a sanitized_error
string/fallback, and return jsonify({"connected": True, "controllers": [],
"error": sanitized_error}), 502 instead of returning the raw error.
- Around line 268-270: The handler currently returns the raw error from
oc_client.get_server_info() (variables success, error, user_id); change it to
use the same safe_errors whitelist logic used in the /connect route: if error is
in safe_errors return that message to the client, otherwise return a generic
"Failed to validate Operations Center credentials" message while keeping the
full error in the server log via logger.warning; ensure you reference the
safe_errors set and apply the check before building the jsonify response.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: 6bf9f2d1-257b-425e-8746-5503dea60749

📥 Commits

Reviewing files that changed from the base of the PR and between ffd1e30 and 43e4f46.

📒 Files selected for processing (6)
  • server/chat/backend/agent/tools/cloudbees_rca_tool.py
  • server/connectors/cloudbees_connector/__init__.py
  • server/connectors/cloudbees_connector/fm_client.py
  • server/connectors/cloudbees_connector/oc_client.py
  • server/connectors/cloudbees_connector/platform_client.py
  • server/routes/cloudbees/cloudbees_routes.py

Comment thread server/chat/backend/agent/tools/cloudbees_rca_tool.py Outdated
Comment thread server/connectors/cloudbees_connector/fm_client.py
Comment thread server/connectors/cloudbees_connector/oc_client.py Outdated
Comment thread server/routes/cloudbees/cloudbees_routes.py Outdated
Comment thread server/routes/cloudbees/cloudbees_routes.py Outdated
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Fixed
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Fixed
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Fixed
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Fixed
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Fixed
@beng360
beng360 marked this pull request as ready for review June 4, 2026 20:22

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (3)
client/src/hooks/use-github-status.ts (1)

120-120: ⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

Remove dead cleanup code for non-existent focus event listener.

Line 120 removes a focus event listener that is never added in this effect (lines 114-116 show only providerStateChanged, message, and visibilitychange are registered). This cleanup is dead code left from incomplete refactoring.

🧹 Proposed fix
     return () => {
       window.removeEventListener('providerStateChanged', handleProviderChange);
       window.removeEventListener('message', handleAuthMessage);
-      window.removeEventListener('focus', checkStatus);
       document.removeEventListener('visibilitychange', handleVisibility);
     };
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/hooks/use-github-status.ts` at line 120, The cleanup in the effect
contains a stale call removing a 'focus' listener that is never added; remove
the dead window.removeEventListener('focus', checkStatus) line and ensure the
effect's cleanup only unregisters the actual listeners that were registered
(e.g., providerStateChanged, the message handler, and 'visibilitychange') so
that removeEventListener targets match the corresponding addEventListener calls
in use-github-status (look for the providerStateChanged and checkStatus/message
handler registrations).
client/src/components/github-provider-integration.tsx (1)

314-321: ⚠️ Potential issue | 🟠 Major | 🏗️ Heavy lift

Fix GitHub metadata regeneration/pending UX (no-op polling still called)

In client/src/components/github-provider-integration.tsx (lines 314-321), startMetadataPolling immediately returns after clearing pollingRef (disabled polling). It’s still invoked:

  • from loadSavedRepos (around line 329) after fetching selections
  • from handleRegenerate (around line 551) after setting metadata_status: 'generating'

Since the /repo-metadata/generate endpoint enqueues async work and only updates connected_repos.metadata_status in the DB, the client never re-fetches repo-selections to transition repo metadata_status from pending/generating to ready—so users will likely see “Generating description…” until they manually refresh/reconnect.

Either re-enable a lightweight revalidation/polling for GitHub (like the GitLab connect page does), or remove startMetadataPolling and its call sites and update the UI to reflect that only manual refresh will show completion. Also keep the issue #471`` rationale scoped to dev-only if that’s truly the intent.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@client/src/components/github-provider-integration.tsx` around lines 314 -
321, startMetadataPolling currently clears pollingRef then returns, so callers
(startMetadataPolling invoked from loadSavedRepos and handleRegenerate) never
revalidate repo selections after triggering repo-metadata/generate, leaving
connected_repos.metadata_status stuck in pending/generating; either re-enable a
lightweight revalidation loop or remove the no-op and update callers/UI. Fix:
modify startMetadataPolling to implement a lightweight polling/revalidation
(e.g., setInterval that fetches repo selections every N seconds and clears
itself when no repos are pending) or delete startMetadataPolling and remove its
invocations in loadSavedRepos and handleRegenerate and ensure the UI for
metadata_status explicitly indicates manual refresh is required; reference
startMetadataPolling, loadSavedRepos, handleRegenerate, and
connected_repos.metadata_status to locate changes and keep the issue `#471`
dev-only rationale if you retain a disabled path.
server/routes/cloudbees/cloudbees_routes.py (1)

344-350: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

PAT/Bearer connections are misreported as disconnected in /platform-status.

Line 345 requires username, but PAT mode intentionally stores OC credentials with an empty username. Valid bearer connections will show connected: false.

Suggested fix
-    if oc_creds and oc_creds.get("base_url") and oc_creds.get("username") and oc_creds.get("api_token"):
+    if oc_creds and oc_creds.get("base_url") and oc_creds.get("api_token"):
+        auth_mode = (oc_creds.get("auth_mode") or "basic").lower()
+        has_required_identity = auth_mode == "bearer" or bool(oc_creds.get("username"))
+        if not has_required_identity:
+            return jsonify({
+                "operations_center": {"connected": False},
+                "feature_management": fm_status,
+            })
         oc_status = {
             "connected": True,
             "url": oc_creds["base_url"],
             "username": oc_creds["username"],
         }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/routes/cloudbees/cloudbees_routes.py` around lines 344 - 350, The code
is incorrectly requiring a non-empty username for OC PAT/bearer auth, causing
valid bearer connections to appear disconnected; update the condition that
builds oc_status (where oc_creds is returned from
get_token_data(CLOUDBEES_OC_PROVIDER)) to consider the credentials "connected"
when base_url and api_token are present even if username is empty, and populate
oc_status["username"] using oc_creds.get("username") (which may be None or
empty) rather than gate connection on it; keep using oc_creds["base_url"] and
oc_creds["api_token"] for validation so bearer/PAT users are reported connected.
♻️ Duplicate comments (1)
server/connectors/cloudbees_connector/oc_client.py (1)

208-264: ⚠️ Potential issue | 🟠 Major | ⚡ Quick win

time_window_hours is still ignored in cross-controller queries.

Line 209 accepts time_window_hours, but returned builds are never filtered by cutoff before sorting/return. This causes stale build data in enterprise RCA results.

Suggested fix
+from datetime import datetime, timedelta, timezone
...
         # Sort by timestamp descending
+        if time_window_hours > 0:
+            cutoff_ms = int(
+                (datetime.now(timezone.utc) - timedelta(hours=time_window_hours)).timestamp() * 1000
+            )
+            all_builds = [
+                b for b in all_builds
+                if isinstance(b.get("timestamp"), (int, float)) and b.get("timestamp", 0) >= cutoff_ms
+            ]
+
         all_builds.sort(key=lambda b: b.get("timestamp", 0), reverse=True)
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@server/connectors/cloudbees_connector/oc_client.py` around lines 208 - 264,
query_recent_builds_across_controllers currently accepts time_window_hours but
never applies it; compute a cutoff (e.g., cutoff_ms = int(time.time() * 1000) -
time_window_hours * 3600 * 1000) and filter all_builds to only include builds
with build.get("timestamp", 0) >= cutoff_ms before sorting/returning; ensure
time is imported (or use time.time()) and keep the filtering step just prior to
the existing sort to avoid changing job/build-fetch logic inside
get_controller_client/list_builds.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx`:
- Around line 109-111: The fetch response mapping in the CloudBeesAuthPage
component is using the wrong path; update the handler for
fetch("/api/cloudbees/status?full=true") so it checks and uses d?.summary (not
d?.status?.summary") when calling setSummary, i.e. replace the nested status
access with the top-level summary in the .then callback that currently invokes
setSummary.
- Around line 336-343: The success UI is shown even when the PUT to
/api/cloudbees/rca-settings fails because non-2xx responses aren't checked;
update the fetch handling in the CloudBeesAuthPage component to inspect the
fetch response (e.g., response.ok) after the await
fetch("/api/cloudbees/rca-settings", ...) call and only call
setRcaEnabled(checked) and toast(...) when the response is OK; if not OK, throw
or handle the error and show an error toast instead so the toggle UI doesn't
reflect an unpersisted change.
- Around line 144-145: Replace the generic CustomEvent("providerStateChanged")
dispatches with the CloudBees-specific event required by the connector contract:
dispatch new Event("cloudBeesStateChanged") (use Event, not CustomEvent). Update
each dispatch site in CloudBeesAuthPage.tsx where window.dispatchEvent(new
CustomEvent("providerStateChanged")) is called so they instead call
window.dispatchEvent(new Event("cloudBeesStateChanged")), ensuring disconnect
and status revalidation emit the provider-specific event name.

In `@client/src/hooks/use-github-status.ts`:
- Around line 111-116: Remove the no-op visibility handler and its registration:
delete the empty handleVisibility function and the
document.addEventListener('visibilitychange', handleVisibility) line so no
unused listener is attached; ensure only the remaining listeners
(window.addEventListener('providerStateChanged', handleProviderChange') and
window.addEventListener('message', handleAuthMessage') remain unchanged to
preserve behavior.

In `@server/connectors/cloudbees_connector/oc_client.py`:
- Around line 73-97: The current host-comparison in the controller validation
(variables oc_host, ctrl_host, oc_domain, ctrl_domain) is insecure because it
simply joins trailing labels and can be bypassed (e.g., co.uk or IP octet
suffixes); replace this heuristic with a robust check: parse both hosts, treat
IP literals separately by using ipaddress.ip_address and require exact IP
equality for IPs, and for domain names use a public suffix list-based extraction
(e.g., tldextract or publicsuffix2) to get the registrable/registered domain for
self.base_url and controller_url and compare those registered domains for
equality; ensure you raise the same ValueError when they do not match and keep
the invalid-URL guard for empty ctrl_host.

In `@server/routes/cloudbees/cloudbees_routes.py`:
- Around line 270-274: The CloudBeesOCClient created in the route handlers
(instances in the CloudBeesOCClient creation blocks and used to call
get_server_info and similar methods around lines shown) are never closed; update
the handlers to ensure the client is always closed after use by wrapping usage
in a try/finally (or using the client's context manager if it implements
__enter__/__exit__) and call the client's close method (e.g., oc_client.close())
in the finally block; apply the same pattern to the other route path(s) where
CloudBeesOCClient is constructed (also the block around the other occurrence
noted) so connections/file descriptors are released under error and normal
execution.

---

Outside diff comments:
In `@client/src/components/github-provider-integration.tsx`:
- Around line 314-321: startMetadataPolling currently clears pollingRef then
returns, so callers (startMetadataPolling invoked from loadSavedRepos and
handleRegenerate) never revalidate repo selections after triggering
repo-metadata/generate, leaving connected_repos.metadata_status stuck in
pending/generating; either re-enable a lightweight revalidation loop or remove
the no-op and update callers/UI. Fix: modify startMetadataPolling to implement a
lightweight polling/revalidation (e.g., setInterval that fetches repo selections
every N seconds and clears itself when no repos are pending) or delete
startMetadataPolling and remove its invocations in loadSavedRepos and
handleRegenerate and ensure the UI for metadata_status explicitly indicates
manual refresh is required; reference startMetadataPolling, loadSavedRepos,
handleRegenerate, and connected_repos.metadata_status to locate changes and keep
the issue `#471` dev-only rationale if you retain a disabled path.

In `@client/src/hooks/use-github-status.ts`:
- Line 120: The cleanup in the effect contains a stale call removing a 'focus'
listener that is never added; remove the dead
window.removeEventListener('focus', checkStatus) line and ensure the effect's
cleanup only unregisters the actual listeners that were registered (e.g.,
providerStateChanged, the message handler, and 'visibilitychange') so that
removeEventListener targets match the corresponding addEventListener calls in
use-github-status (look for the providerStateChanged and checkStatus/message
handler registrations).

In `@server/routes/cloudbees/cloudbees_routes.py`:
- Around line 344-350: The code is incorrectly requiring a non-empty username
for OC PAT/bearer auth, causing valid bearer connections to appear disconnected;
update the condition that builds oc_status (where oc_creds is returned from
get_token_data(CLOUDBEES_OC_PROVIDER)) to consider the credentials "connected"
when base_url and api_token are present even if username is empty, and populate
oc_status["username"] using oc_creds.get("username") (which may be None or
empty) rather than gate connection on it; keep using oc_creds["base_url"] and
oc_creds["api_token"] for validation so bearer/PAT users are reported connected.

---

Duplicate comments:
In `@server/connectors/cloudbees_connector/oc_client.py`:
- Around line 208-264: query_recent_builds_across_controllers currently accepts
time_window_hours but never applies it; compute a cutoff (e.g., cutoff_ms =
int(time.time() * 1000) - time_window_hours * 3600 * 1000) and filter all_builds
to only include builds with build.get("timestamp", 0) >= cutoff_ms before
sorting/returning; ensure time is imported (or use time.time()) and keep the
filtering step just prior to the existing sort to avoid changing job/build-fetch
logic inside get_controller_client/list_builds.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: ASSERTIVE

Plan: Pro

Run ID: dbdd80f0-6a98-4e52-9a20-a7f066b715b6

📥 Commits

Reviewing files that changed from the base of the PR and between 43e4f46 and f9abf72.

⛔ Files ignored due to path filters (1)
  • client/public/cloudbees.svg is excluded by !**/*.svg
📒 Files selected for processing (14)
  • client/src/app/api/cloudbees/connect-platform/route.ts
  • client/src/app/api/cloudbees/controllers/route.ts
  • client/src/app/api/cloudbees/platform-status/route.ts
  • client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx
  • client/src/app/cloudbees/auth/page.tsx
  • client/src/components/github-provider-integration.tsx
  • client/src/hooks/use-github-status.ts
  • server/chat/backend/agent/skills/integrations/cloudbees/SKILL.md
  • server/chat/backend/agent/skills/integrations/cloudbees_oc/SKILL.md
  • server/chat/backend/agent/tools/cloud_tools.py
  • server/chat/background/prediscovery_task.py
  • server/connectors/cloudbees_connector/oc_client.py
  • server/routes/cloudbees/cloudbees_routes.py
  • website/docs/integrations/cloudbees.md

Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Outdated
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Outdated
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Outdated
Comment thread client/src/hooks/use-github-status.ts Outdated
Comment thread server/connectors/cloudbees_connector/oc_client.py Outdated
Comment thread server/routes/cloudbees/cloudbees_routes.py Outdated
beng360 added a commit that referenced this pull request Jun 4, 2026
Backend:
- Use 'with' for OC/FM client context managers in RCA tool
- Remove unused 'time' import
- Update rate limit tracking after 429 retry
- Use time_window_hours parameter in cross-controller query
- Sanitize error messages in routes (no internal hostnames)
- Close OC clients properly in routes
- Strengthen domain matching (prevent evil-company.com bypass)

Frontend:
- Remove unused imports (Button, Badge, router)
- Fix status summary mapping
- Dispatch providerStateChanged after connect
- Only toast RCA toggle success on actual success
- Remove empty visibility handler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread server/routes/cloudbees/cloudbees_routes.py Fixed
beng360 and others added 17 commits June 4, 2026 17:00
…e support

- CloudBeesOCClient: discovers managed controllers, queries across them
- CloudBeesFMClient: queries feature flag changes for incident correlation
- CloudBeesPlatformClient: orchestration layer combining OC + FM
- Enhanced RCA tool: new actions (flag_changes, cross_controller_deployments, controller_list)
- New routes: /connect-platform, /platform-status, /controllers, /disconnect-platform
- Graceful degradation: missing credentials return helpful messages, not errors
- Additive: existing single-controller flow completely unchanged

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Path injection: URL-encode app_id/flag_name in FM client
- SSRF: validate controller URLs match OC domain before sending creds
- Resource leak: add context manager support + close() in action handlers
- Remove trace_context delegation to wrong provider
- Add single retry on FM 429 responses
- Cache platform_status instead of hitting FM API every call
- Sanitize error messages from OC (no raw exceptions to caller)
- Validate URL schemes (require http/https)
- Guard against empty app_id in flag_changes action

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Three connection modes in one clean flow:
- Single Controller: direct Jenkins/CloudBees CI connection (existing)
- Operations Center: multi-controller management with controller discovery
- Personal Access Token: platform-level auth for organizations using PATs

Optional Feature Management section under OC mode for flag correlation.
Displays discovered controllers after successful OC connection.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Critical fixes:
- Field name alignment: frontend now sends oc_url/api_token/fm_api_token (matches backend)
- PAT mode: backend now supports Bearer token auth when username is empty
- OC client: supports both Basic Auth and Bearer token modes
- Disconnect: removes both single-controller AND platform credentials
- Controllers: fetched on page load from /controllers endpoint
- Connect response: includes discovered controllers list

Other fixes:
- Client-side URL scheme validation (http/https only)
- 0 controllers: shows guidance message instead of dead-end
- Clear all form fields on disconnect
- Remove trace_context from tool description (action doesn't exist)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Remove all Card wrappers and bordered containers
- Mode picker: divider-separated list with hover arrows
- Forms: transparent inputs, rounded-xl, generous spacing
- Connected: minimal data display with status dots
- Feature management: collapsible <details> instead of card section
- Matches sign-in page typography and spacing philosophy
- No decorative elements, trusts the content

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- SKILL.md: document enterprise actions (flag_changes, cross_controller_deployments, controller_list)
- Add cloudbees_oc skill entry for SkillRegistry detection
- SSRF: relax validation to allow sibling subdomains (same registrable domain)
- Prediscovery: add OC exploration instructions to prompt
- MCP: verify cloudbees_rca is available via MCP tool registry

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Covers all three connection modes (Single Controller, Operations Center, PAT),
webhook setup, RCA actions (standard + enterprise), feature flag correlation,
and auto-trigger configuration.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Complete redesign as a multi-step wizard:
- Step 1: Choose connection mode (OC, Single, PAT) with descriptive cards
- Step 2: Enter credentials with inline instruction box
- Step 3: Webhook setup with copy-able URL and Jenkinsfile snippet
- Connected: Controller list + 'what happens next' section
- Progress bar tracks position through the flow
- Full max-w-2xl width, generous spacing, no cramped forms

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
… webhook, deployments)

Connected state is now a full dashboard:
- 4-column stats grid (jobs, nodes, executors, queue)
- Job health bar with colored segments + legend
- RCA auto-trigger toggle switch
- Webhook URL with copy + Jenkinsfile snippet (collapsible)
- Recent deployments list with status dots
- Controllers list (OC mode)
- All in the same dark minimal aesthetic

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Refs #471

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ctions

- Removed all trace_context references (action no longer exists)
- Flag_changes: explicitly states 'only call if FM is connected + have app_id'
- cross_controller_deployments: only if OC is connected
- Prevents agent from calling actions that will always error

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…DBEES

Hidden by default (.env.example=false). Set NEXT_PUBLIC_ENABLE_CLOUDBEES=true to show.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Backend:
- Use 'with' for OC/FM client context managers in RCA tool
- Remove unused 'time' import
- Update rate limit tracking after 429 retry
- Use time_window_hours parameter in cross-controller query
- Sanitize error messages in routes (no internal hostnames)
- Close OC clients properly in routes
- Strengthen domain matching (prevent evil-company.com bypass)

Frontend:
- Remove unused imports (Button, Badge, router)
- Fix status summary mapping
- Dispatch providerStateChanged after connect
- Only toast RCA toggle success on actual success
- Remove empty visibility handler

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@beng360
beng360 force-pushed the feat/cloudbees-enterprise branch from 9823bad to e5e5617 Compare June 4, 2026 21:01
@beng360

beng360 commented Jun 8, 2026

Copy link
Copy Markdown
Contributor Author

All 6 fixed in 51075be. To test locally: set NEXT_PUBLIC_ENABLE_CLOUDBEES=true in .env and recreate frontend.

Comment thread server/routes/cloudbees/cloudbees_routes.py Fixed
Was missing from docker-compose.yaml (make dev), not just
docker-compose.prod-local.yml. Without this, the flag never reaches
the frontend container regardless of .env settings.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@isiddharthsingh

Copy link
Copy Markdown
Contributor

Code review (follow-up)

Re-reviewed after the two fix commits. The PAT-mode username gate, the missing disconnect-platform proxy route, the deployments #undefined field mismatch, and the fm_client leak are all correctly resolved. Four issues remain — two are regressions/gaps in the fixes, two are related defects in the same OC code path.

  1. flag_changes crashes when called without app_id. The previous fix removed the guard, but the action still can't run without an app_id: the connect UI sends fm_api_token but never fm_app_id (so the stored FM app_id is always empty), the tool schema marks app_id optional, and cloudbees_oc/SKILL.md instructs the agent to call flag_changes with no app_id. That None flows into get_recent_flag_changesquote(None, safe=''), which raises TypeError: quote_from_bytes() expected bytes (verified in-container). The call site has no try/except, so it propagates uncaught. Fix: fan out over list_applications() when app_id is None, or capture/send fm_app_id and inject it, or restore a graceful guard and mark app_id required.

})
with fm_client:
success, changes, error = fm_client.get_recent_flag_changes(
app_id, since_hours=time_window_hours
)
if not success:
return json.dumps({"error": error or "Failed to query Feature Management."})
return json.dumps({"flag_changes": changes, "count": len(changes), "time_window_hours": time_window_hours})

def get_recent_flag_changes(
self, app_id: str, since_hours: int = 24
) -> Tuple[bool, List[Dict], Optional[str]]:
"""Get flags that were modified within the given time window.
The API may not have a direct "changes" endpoint, so we fetch all flags
and filter by updatedAt timestamps.
"""
success, data, error = self._request(
"GET", f"/applications/{quote(app_id, safe='')}/flags"
)

  1. OC/PAT connection status is lost on page reload. loadStatus drives the whole UI off cloudbeesService.getStatus(), which hits /api/cloudbees/status — and the backend status route only checks legacy cloudbees credentials (_get_stored_cloudbees_credentials). A user connected only via Operations Center / PAT (stored under cloudbees_oc) gets {connected: false}, so a refresh drops them back to the setup wizard instead of the connected dashboard. The reload path should also consider cloudbees_oc (e.g. fall back to platform-status).

const result = await cloudbeesService.getStatus();
if (result) {
setStatus(result);
localStorage.setItem(CACHE_KEY, JSON.stringify(result));
if (result.connected) {
localStorage.setItem(CONNECTED_KEY, "true");
setStep("connected");

def status(user_id):
"""Check whether CloudBees CI is connected and return summary dashboard data."""
creds = _get_stored_cloudbees_credentials(user_id)
if not creds:
return jsonify({"connected": False})

  1. OC-only users get the skill but not the tool. The registry loads the cloudbees_oc skill via its connection_check on the cloudbees_oc provider, and the skill advertises cloudbees_rca — but cloud_tools.py only registers cloudbees_rca when is_cloudbees_connected() is true, which checks legacy cloudbees credentials only. An OC-only user thus gets skill instructions referencing a tool that was never registered. Fix: make is_cloudbees_connected() also return true when cloudbees_oc credentials exist. (Same root cause as Update README.md #2: legacy-only provider checks ignore cloudbees_oc.)

def is_cloudbees_connected(user_id: str) -> bool:
"""Check if CloudBees CI is connected for a user."""
from utils.auth.token_management import get_token_data
creds = get_token_data(user_id, "cloudbees")
return bool(
creds
and creds.get("base_url")
and creds.get("username")
and creds.get("api_token")
)

category: cicd
connection_check:
method: get_token_data
provider_key: cloudbees_oc
required_field: base_url
tools:
- cloudbees_rca
index: "CI/CD -- CloudBees Operations Center: cross-controller deployments, managed controller inventory, feature flag changes"

  1. Env-var sync fix only covers the build-time path. The compose-file additions fix build-from-source deployments, but the runtime injector client/docker-entrypoint.sh (which generates window.__ENV) still omits NEXT_PUBLIC_ENABLE_CLOUDBEES. With prebuilt images (make prod-prebuilt), the flag is inlined false at build and the only runtime override path doesn't carry the key, so setting it in .env has no effect and CloudBees stays hidden. Add the flag to docker-entrypoint.sh alongside the others.

window.__ENV = {
NEXT_PUBLIC_BACKEND_URL: "$(sanitize "${NEXT_PUBLIC_BACKEND_URL:-}")",
NEXT_PUBLIC_WEBSOCKET_URL: "$(sanitize "${NEXT_PUBLIC_WEBSOCKET_URL:-}")",
NEXT_PUBLIC_ENABLE_OVH: "$(sanitize "${NEXT_PUBLIC_ENABLE_OVH:-}")",
NEXT_PUBLIC_ENABLE_PAGERDUTY_OAUTH: "$(sanitize "${NEXT_PUBLIC_ENABLE_PAGERDUTY_OAUTH:-}")",
NEXT_PUBLIC_ENABLE_SHAREPOINT: "$(sanitize "${NEXT_PUBLIC_ENABLE_SHAREPOINT:-}")",
NEXT_PUBLIC_ENABLE_JIRA: "$(sanitize "${NEXT_PUBLIC_ENABLE_JIRA:-}")",
NEXT_PUBLIC_ENABLE_NOTION: "$(sanitize "${NEXT_PUBLIC_ENABLE_NOTION:-}")",
NEXT_PUBLIC_ENABLE_SPINNAKER: "$(sanitize "${NEXT_PUBLIC_ENABLE_SPINNAKER:-}")",
};
JSEOF

🤖 Generated with Claude Code

- If this code review was useful, please react with 👍. Otherwise, react with 👎.

beng360 and others added 3 commits June 9, 2026 15:06
- is_cloudbees_connected now checks OC/PAT creds (not just legacy single-controller)
- _get_oc_client_for_user passes auth_mode, fixing PAT users unable to use RCA
- Add debug logging for incomplete credentials in RCA tool
- Add NEXT_PUBLIC_ENABLE_CLOUDBEES to env.ts EnvKey type
- Replace magic number 5 with MAX_BUILDS_PER_CONTROLLER constant
- Use context managers (with) for OC and FM clients in connect-platform route
- Validate response.ok before setting webhook info state
- Replace presentational <label> elements with <p> for accessibility
- Document PAT mode capabilities in integration docs

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Break the 1012-line monolith into focused presentational components:
- ModeSelector (step 1): connection mode selection
- CredentialForms (step 2): OC, single controller, and PAT forms
- WebhookSetup (step 3): webhook URL display and Jenkinsfile snippet
- ConnectedDashboard: stats, job health, RCA toggle, deployments, controllers

Parent retains all state, effects, and handler logic.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Without this, the CloudBees integration doesn't appear in prod/docker
deployments because env-config.js never gets the variable injected at
container startup.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx
Comment thread client/src/app/cloudbees/auth/components/CloudBeesAuthPage.tsx Outdated
Comment thread server/connectors/cloudbees_connector/oc_client.py Outdated
Comment thread server/connectors/cloudbees_connector/oc_client.py
Comment thread server/chat/backend/agent/tools/cloudbees_rca_tool.py
Comment thread server/connectors/cloudbees_connector/fm_client.py Outdated
beng360 and others added 5 commits June 9, 2026 16:26
- Strip username from localStorage cache (only store connected + baseUrl)
- Replace all raw fetch() calls with apiRequest for consistent auth/error handling
- Replace brittle domain heuristic with tldextract for controller URL validation
- Add separate MAX_JOBS_PER_CONTROLLER constant (50) for job iteration limit
- Validate app_id is present before calling flag_changes action
- Fix naive datetime comparison in fm_client (ensure tz-aware, catch TypeError)
- Add tldextract to requirements.txt

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
TypeScript:
- Number.isNaN instead of isNaN
- Mark all component props as Readonly
- Replace array index keys with stable identifiers
- Extract nested ternary into variable
- Use globalThis instead of window
- Use optional chaining (result?.controllers?.length)
- Extract nested template literal

Python:
- Fix NOSONAR comment syntax (remove trailing colon)
- Replace unused variables with _
- Use logger.exception instead of logger.error in except blocks
- Reduce cognitive complexity in connect_platform (extract _validate_and_store_fm)
- Reduce cognitive complexity in discover_controllers (extract _fetch_controller_data, _parse_controller_response)
- Reduce cognitive complexity in query_recent_builds (extract _query_single_controller)
- Reduce cognitive complexity in get_recent_flag_changes (extract _is_recently_modified, _parse_timestamp)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Use NEXT_PUBLIC_BACKEND_URL + NGROK_URL fallback pattern (same as
Dynatrace) instead of the non-standard BACKEND_URL. Setting NGROK_URL
once now works for all connectors in local OSS dev.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- Make tldextract import lazy with fallback to simple suffix matching
  so the app doesn't crash if the package isn't installed yet
- Merge the two status calls into one: use /status?full=true directly
  instead of calling getStatus() then status?full=true separately,
  cutting Jenkins round trips in half on dashboard load

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
- _verify_webhook_user: check both cloudbees and cloudbees_oc providers
  (was returning 403 for OC-only users)
- connect-platform: generate webhook_secret for OC credentials
  (was storing empty secret, making webhook URL snippet useless)
- _get_stored_cloudbees_credentials: fall through to cloudbees_oc
  when legacy cloudbees provider not found
- _build_client: handle bearer/PAT auth mode, instantiate OC client
  when username is empty
- cloudbees_rca_tool: fall through to OC client when legacy client
  unavailable for single-controller actions
- CSS: change styled-jsx to global scope so animate-step-in reaches
  child components (ModeSelector, CredentialForms, etc.)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@isiddharthsingh

Copy link
Copy Markdown
Contributor

CloudBees OC/PAT parity check (00520c5)

Verified the "make OC/PAT a first-class citizen" change. The connection / webhook / deployment / auto-RCA / tool-registration layer is now genuinely at parity for OC/PAT. One real gap remains.

Deep build-introspection breaks for pure OC/PAT users. The single-controller action fallthrough hands a CloudBeesOCClient to the shared JenkinsClient-shaped helpers:

client = _get_client_for_cloudbees_user(user_id) or _get_oc_client_for_user(user_id)
if not client:
return json.dumps({"error": "CloudBees CI is not connected. Configure credentials in Settings > Connectors > CloudBees CI."})

Those helpers call JenkinsClient-only methods on it — build_detail -> get_build_detail(), plus pipeline_stages / stage_log / build_logs / test_results / blue_ocean_*:

if not job_path or not build_number:
return json.dumps({"error": "job_path and build_number are required"})
success, data, error = client.get_build_detail(job_path, build_number)
if not success:
return json.dumps({"error": error or "Failed to fetch build detail"})

CloudBeesOCClient implements none of these — only get_server_info, discover_controllers, get_controller_client, query_recent_builds_across_controllers:

def get_server_info(self) -> Tuple[bool, Optional[Dict], Optional[str]]:
"""Validate OC connection by fetching server info."""
# Try /cjoc path first, then root
success, data, error = self._request(
"GET", "/cjoc/api/json", params={"tree": "mode,nodeDescription,numExecutors,useSecurity"}
)
if success:
return success, data, error
# Fallback: OC might be at the root
return self._request(
"GET", "/api/json", params={"tree": "mode,nodeDescription,numExecutors,useSecurity"}
)
def discover_controllers(self) -> Tuple[bool, List[Dict], Optional[str]]:
"""Discover managed controllers from Operations Center."""
success, data, error = self._fetch_controller_data()
if not success:
return False, [], error
controllers = _parse_controller_response(data) if data else []
return True, controllers, None

For a user with no legacy cloudbees row, each of these actions raises AttributeError (no try/except around the call), so the agent gets a raw error instead of build data. OC RCA can list deployments but can't introspect the failing build — the core "drill into why it failed" actions. In OC mode the build lives on a controller, so these actions should route through get_controller_client(controller_url) to a real per-controller JenkinsClient.

Minor (non-blocking):

  1. /status swallows the same AttributeError for list_jobs / get_queue / list_nodes, so OC users see "Connected" with zeroed job-health / queue / node cards (the OC root legitimately has no jobs).

job_health = {"healthy": 0, "unstable": 0, "failing": 0, "disabled": 0, "other": 0}
try:
j_ok, j_data, _ = client.list_jobs()
if j_ok:
jobs = j_data
job_count = len(jobs)
for job in jobs:
color = (job.get("color") or "").lower().replace("_anime", "")
if color == "blue":
job_health["healthy"] += 1
elif color == "yellow":
job_health["unstable"] += 1
elif color == "red":
job_health["failing"] += 1
elif color in ("disabled", "notbuilt"):
job_health["disabled"] += 1
else:
job_health["other"] += 1
except Exception:
logger.exception("[CLOUDBEES] Failed to fetch job list for user %s", user_id)

  1. Only new OC connects generate a webhook_secret; pre-upgrade OC rows have none, so those webhooks run unsigned (signature check is skipped when no secret is stored).

if webhook_secret:
if not signature:
logger.warning("[CLOUDBEES] Webhook rejected: missing %s for user %s", SIGNATURE_HEADER, sanitize(user_id)[:50])
return jsonify({"error": f"Missing {SIGNATURE_HEADER} header"}), 401
if not verify_webhook_signature(request.get_data(), signature, webhook_secret):
logger.warning("[CLOUDBEES] Webhook rejected: invalid signature for user %s", sanitize(user_id)[:50])
return jsonify({"error": "Invalid webhook signature"}), 401

🤖 Generated with Claude Code

beng360 and others added 5 commits June 10, 2026 12:51
For OC-only users, build_detail/pipeline_stages/stage_log/build_logs/
test_results/blue_ocean actions now require a controller_url parameter.
The agent must first call controller_list or cross_controller_deployments
to discover controller URLs, then pass the relevant one.

Previously these actions passed a CloudBeesOCClient to JenkinsClient-shaped
helpers, causing AttributeError on every call.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…ars)

Both branches added feature flags to the same locations — keep both
NEXT_PUBLIC_ENABLE_CLOUDBEES and NEXT_PUBLIC_ENABLE_BITBUCKET_OAUTH.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The connected-accounts endpoint returns provider keys exactly as stored
in user_tokens (cloudbees_oc, cloudbees_fm). The frontend connector card
checks for "cloudbees". Added a UI alias map so these sub-providers
register under the unified "cloudbees" key in the response.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@sonarqubecloud

Copy link
Copy Markdown

@beng360
beng360 merged commit 2ef55b8 into main Jun 10, 2026
16 checks passed
@beng360
beng360 deleted the feat/cloudbees-enterprise branch June 10, 2026 19:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants