One list for Jenkins, GitLab, GitHub Actions, Azure DevOps and Argo CD
How OpsNexus pulls pipelines and builds from five CI/CD tools into one list, why every tool's status is mapped to the same five values, and why write actions are off until you turn them on.
Ask a platform team "is anything failing right now?" and the honest answer is often "let me check four places". The legacy services build on Jenkins. New repositories use GitHub Actions. One group lives in GitLab. Deployments to Kubernetes go through Argo CD. And there's always an Azure DevOps project nobody wants to migrate.
OpsNexus doesn't ask you to move. It connects to the tools you already run and shows their pipelines and builds in one list, next to pipelines that run on OpsNexus's own build agents. This post explains how that works.
One small contract per tool
Each tool is a connector: one Python file that implements the same short interface.
class Connector:
kind: str # "jenkins", "gitlab", "github_actions", "azure_devops", "argocd"
label: str
fields: list[Field] # what the "Connect a tool" form asks for
def test_connection(self) -> TestResult: ...
def list_pipelines(self) -> list[PipelineData]: ...
def list_builds(self, pipeline, limit=20) -> list[BuildData]: ...
# optional write actions
def trigger(self, pipeline, branch=None) -> str: ...
def cancel(self, pipeline, build_external_id) -> None: ...The form you fill in under Integrations → Connect a tool is generated from fields. A field marked secret, such as an API token, goes to the secret store (HashiCorp Vault by default) and never to the database or an API response. Adding a new tool means writing one file and registering it. No route, page or database change is needed.
Here is what each tool asks for:
| Tool | Connection | Token scope |
|---|---|---|
| Jenkins | URL, username, API token | The user's API token |
| GitLab CI/CD | URL, personal access token, optional project IDs | read_api (api to trigger or cancel) |
| GitHub Actions | Repository, access token, optional API URL | Fine-grained token with Actions: read (write to run or cancel) |
| Azure DevOps | Organization URL, project, personal access token | Build: Read (Read & execute to trigger) |
| Argo CD | Server URL, API token, optional TLS check | An account token, for example from argocd account generate-token |
Five statuses, not fifty
The hard part of "one list" isn't fetching data. It's making the data comparable. Every tool has its own vocabulary:
- Jenkins reports
SUCCESS,FAILURE,UNSTABLE,ABORTEDandNOT_BUILT, plus a separatebuildingflag. - GitLab has
created,waiting_for_resource,preparing,pending,scheduled,manual,running,success,failed,canceledandskipped. - GitHub Actions splits state into a
statusand aconclusion, with values such astimed_out,startup_failure,neutralandaction_required. - Azure DevOps has a
statusand, once completed, aresultthat can bepartiallySucceeded. - Argo CD has sync operations with phases, and a history of successful deployments.
Each connector maps its tool's values onto five: queued, running, succeeded, failed and cancelled (plus unknown for anything it doesn't recognise). Some choices are judgement calls, and we made them on purpose:
- Jenkins
UNSTABLEand Azure DevOpspartiallySucceededcount as failed. If tests failed, the build isn't green, whatever the tool calls it. - A GitLab
manualjob counts as queued. It's waiting for someone. - GitHub
timed_outandstartup_failurecount as failed, andskippedcounts as cancelled.
With one vocabulary, filters, success rates, dashboards and alert rules work the same way across every tool. GET /api/builds?status=failed means the same thing for a Jenkins job and an Argo CD application.
Syncing without hammering your servers
The worker syncs each integration every INTEGRATION_SYNC_MINUTES (5 by default). It lists pipelines, then fetches the most recent builds of each one (20 by default) and updates them in place. You can also press Sync now, and every sync keeps a short log you can read on the integration page.
All connectors share one HTTP client with sensible manners:
- 15-second timeouts, 5 seconds to connect;
- up to three attempts with backoff on network errors,
429and5xx, honouringRetry-After; - clear errors: a
401or403becomes "Authentication failed. Check the username and token." instead of a stack trace.
If one pipeline's builds can't be read, the sync skips that pipeline and carries on. If the whole sync fails, the integration shows the error. When a working integration starts failing, a notification goes out once, not on every retry.
Notifications that mean something
Because builds are normalized, notifications are too. When a sync sees that a build started, succeeded or failed since the last sync, it sends build.started, build.succeeded or build.failed. For Argo CD, the same transitions are deployments and send deployment.started, deployment.completed or deployment.failed. Each message carries the pipeline, run number, branch, short commit, who triggered it, duration, source tool and environment.
Two guards keep this quiet:
- Nothing is sent on an integration's first import. Connecting Jenkins shouldn't page you about three years of history.
- Only builds that started in the last 24 hours count.
Route these events to Slack, Teams, email or webhooks under Notifications.
Read-only until you say otherwise
All five connectors can trigger and cancel builds. For Argo CD, "trigger" starts a sync and "cancel" stops the running operation. But every integration starts read-only. Until an admin turns on write actions for that integration, trigger and cancel return:
Write actions are turned off for <name>. An admin can allow them on the integration page.There are two reasons for this default. First, the token you paste may have more power than you meant to hand over. A read-only start lets you connect, look around and decide. Second, a button that starts a production deployment shouldn't appear just because someone connected a tool. When write actions are on, triggering still needs the pipelines permission with the trigger action, and every trigger is recorded in the audit log.
Your own pipelines in the same list
OpsNexus also runs pipelines itself, on build agents you install. Those pipelines appear in the same list as everything else, under their own platform. So the answer to "is anything failing right now?" is one page: CI/CD → Overview, filtered by status.
To get started, see Integrations. For OpsNexus's own pipelines, start with Pipelines and Build agents.