# Jenkins

> Inspect Jenkins builds, pipeline stages, console logs, and the build queue from the command line via `af jenkins`. Use when a CI build failed and you need to know why, when checking whether a build or branch is green, when reading console output or a specific pipeline stage's log, when a push hasn't produced a build yet (queue), or when polling a running build to completion.

- Skill: `avantmedialtd/jenkins` (Agent Skill)
- Install (CLI): `npx skillmds@latest add avantmedialtd/jenkins`
- Raw SKILL.md: https://api.skillmd.com/api/skills/avantmedialtd/jenkins/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: DevOps & Infra
- Author: avantmedialtd (https://skillmd.com/u/avantmedialtd)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/avantmedialtd/jenkins

---


# Jenkins CLI

Read-only build visibility for Jenkins via `af jenkins`. Eight subcommands — `jobs`, `job`, `branches`, `build`, `log`, `queue`, `stages`, `stage-log`. Nothing here triggers, cancels, or mutates a build.

## Setup

Jenkins uses its own credentials — Atlassian, Bitbucket, and Sonar tokens are **not** interchangeable. All three variables are **required**; there are no fallbacks, no CLI flags, and no `af.json` block for Jenkins.

Add to your project's `.env`:

- `JENKINS_BASE_URL` — Jenkins instance URL (e.g. `https://jenkins.example.com`; a trailing slash is stripped)
- `JENKINS_USER` — Your Jenkins username
- `JENKINS_API_TOKEN` — API token from `<jenkins>/user/<you>/configure`

They are combined as HTTP Basic auth. Config is read **lazily on the first API call**, so a missing variable surfaces as a runtime error (`JENKINS_USER is not set. Create a .env file in your project directory with: ...`, exit 1), not as a startup error.

**Run `af jenkins` from the repo root that holds the `.env`.** `af` auto-loads `.env` from the *current working directory* only, and it never overwrites variables already set in the environment.

## Job Paths

**This is the single most error-prone part of the command — read it before writing any job path.**

Job paths use `/` to separate folder / pipeline / branch segments. **Every segment becomes its own Jenkins `/job/` level**, URL-encoded individually:

```
my-folder/my-pipeline/feature-branch  →  /job/my-folder/job/my-pipeline/job/feature-branch
my-pipeline/feature/auth              →  /job/my-pipeline/job/feature/job/auth
```

Consequences:

- **For a multibranch pipeline, the branch is just the last segment**: `af jenkins build my-app/main` targets the `main` branch of the `my-app` pipeline.
- **A branch name that itself contains a slash is split into multiple `/job/` levels.** `af jenkins build my-app/feature/AB-123` becomes `/job/my-app/job/feature/job/AB-123`. af has no branch-name special-casing (`request.ts` just splits on `/`), so this only resolves if `feature` really is a folder in Jenkins. For a multibranch branch literally named `feature/AB-123`, Jenkins stores it as one item named `feature%2FAB-123` — pass it as a single segment: `af jenkins build "my-app/feature%2FAB-123"`.
- **Do not pre-encode ordinary segments.** Each segment is `encodeURIComponent`-ed for you, so spaces and other specials inside a segment are handled. The one exception is a branch whose *name* contains a slash: pass it as a single `%2F`-escaped segment (`my-app/feature%2FAB-123`), which af re-encodes to `feature%252FAB-123` — Jenkins' own form for that item.
- Quote paths containing spaces so the shell keeps them as one argument.

## Build Number Resolution

Shared by `build`, `log`, `stages`, and `stage-log`:

- **Omitted** → Jenkins `lastBuild`
- The literal string **`latest`** → Jenkins `lastBuild`
- **Anything else** → used verbatim as the build number (e.g. `142`)

So `af jenkins build my-app/main` and `af jenkins build my-app/main latest` are exactly equivalent.

## Quick Reference

Run bare `af jenkins` (no subcommand) for the full command reference; `af jenkins --help` is intercepted by af's router and prints only a short stub. `--json` is the only meaningful option — it works on every subcommand.

- `af jenkins jobs [folder]` — List jobs; with a folder path, list that folder's children
- `af jenkins job <name>` — Job detail + the 10 most recent builds (+ a Branches table for a multibranch pipeline)
- `af jenkins branches <pipeline>` — Per-branch build status for a multibranch pipeline
- `af jenkins build <name> [number|latest]` — Build detail: status, duration, when, changeset
- `af jenkins log <name> [number|latest]` — Full console output, raw to stdout
- `af jenkins queue` — Pending build queue (takes no arguments)
- `af jenkins stages <name> [number|latest]` — Pipeline stage breakdown
- `af jenkins stage-log <name> <stage> [number|latest]` — Log for one pipeline stage

Note `stage-log`'s argument order: the **stage name is the second positional, the build number the third**.

### Required arguments

`job`, `branches`, `build`, `log`, and `stages` all require `<name>`; `stage-log` requires both `<name>` and `<stage>`. Omitting a required argument prints a usage error and exits 1. `jobs` and `queue` take no required arguments.

## Status Vocabularies

Two different vocabularies — do not conflate them.

**Build result** (`build`, `job`, `branches`) — `SUCCESS` | `FAILURE` | `UNSTABLE` | `ABORTED` | `NOT_BUILT` | `null`. In markdown output a build that is still running renders as `RUNNING`, and a missing result as `UNKNOWN`. In `--json`, a running build has `building: true` and `result: null`.

**Pipeline stage status** (`stages`, from the Stage View plugin) — `SUCCESS`, `FAILED`, `IN_PROGRESS`, `NOT_EXECUTED`, etc. Note `FAILED`, not `FAILURE`.

`af jenkins jobs` and the Status row of `af jenkins job` show Jenkins' raw `color` field instead (`blue`, `red`, `yellow`, `blue (building)`, …).

## Output Formats

- Default: Markdown tables — except `log` and `stage-log`, which write **raw text** straight to stdout (no wrapping, no trailing newline) so they pipe cleanly.
- JSON: Add `--json` for the raw Jenkins API response.

## Exit Codes

**A failed build still exits 0.** Only transport errors, usage errors, and unknown subcommands exit 1. The exit code tells you whether the *query* succeeded, never whether the *build* succeeded.

```bash
# WRONG — this is always true, even for a red build
if af jenkins build my-app/main; then echo "green"; fi

# RIGHT — read the result field
result=$(af jenkins build my-app/main --json | jq -r '.result')
[ "$result" = "SUCCESS" ] || exit 1
```

- `0` — Command completed (regardless of build health)
- `1` — Usage error, auth/config error, HTTP error, unknown subcommand, or `branches` finding no branches

## Common Workflows

### Diagnose a failing CI build

The core loop. Narrow from build → stage → stage log rather than reading the whole console.

```bash
# 1. Confirm it actually failed (and that it isn't still running)
af jenkins build my-app/main --json | jq '{number, building, result}'

# 2. Find which stage failed
af jenkins stages my-app/main
# | Stage    | Status  | Duration |
# | Build    | SUCCESS | 1m 12s   |
# | Test     | FAILED  | 3m 4s    |

# 3. Read only that stage's log
af jenkins stage-log my-app/main "Test" | tail -100
```

If the stage breakdown isn't available (not a pipeline, or the Stage View plugin is missing), fall back to the console:

```bash
af jenkins log my-app/main | tail -100
```

### Poll a running build to completion

`result` is `null` while a build is in progress — always gate on `building` first.

```bash
while true; do
  status=$(af jenkins build my-app/main --json | jq -r 'if .building then "RUNNING" else (.result // "UNKNOWN") end')
  [ "$status" = "RUNNING" ] || break
  sleep 30
done
echo "Finished: $status"
```

### "My push didn't trigger a build"

Before assuming the webhook is broken, check the queue — the build is probably queued, not missing.

```bash
# Still showing the old build number?
af jenkins build my-app/main --json | jq '.number'

# Then look at the queue
af jenkins queue
# | Job     | Queued Since           | Reason                              |
# | my-app  | Jul 14, 2026, 10:04 AM | Waiting for next available executor |
```

`Build queue is empty.` (exit 0) means nothing is pending — at that point suspect the webhook or branch indexing.

### Is the branch green?

```bash
# One branch
af jenkins build my-app/main

# Every branch of a multibranch pipeline at once
af jenkins branches my-app
```

### Find the job path

```bash
# Top-level jobs
af jenkins jobs

# Drill into a folder
af jenkins jobs my-folder

# Job health + last 10 builds, and (for a multibranch pipeline) its branches
af jenkins job my-folder/my-app
```

`af jenkins job` is the best single "is this healthy" command — job status, last success/failure, the last 10 builds, and (for a multibranch pipeline) every branch. For *what changed* in a given build, you still need `af jenkins build <name> [number]`, which is the only command that prints the changeset.

### Search a console log

The log is fetched in full; there is no tail/head/limit flag. Pipe it.

```bash
af jenkins log my-app/main | tail -50
af jenkins log my-app/main | grep -i -A5 'error\|exception'
af jenkins log my-app/main 142 | grep -c 'FAILED'
```

### Discover stage names

The "not found" error lists every available stage — use it deliberately.

```bash
af jenkins stage-log my-app/main x
# Error: Stage "x" not found. Available stages: Checkout, Build, Test, Deploy
```

Stage matching is **case-insensitive**, so `"test"` finds `Test`. Stage names with spaces must be quoted: `af jenkins stage-log my-app/main "Unit Tests"`.

## Tips

- **Branch is a path segment, not a flag** — `af jenkins build my-app/main`, never `af jenkins build my-app --branch main`.
- **`stages` → `stage-log` beats reading the whole console** — go straight to the failing stage instead of grepping thousands of lines.
- **Use `--json` for any decision logic** — the markdown is for humans; `result`/`building` are what a script should branch on.
- **Don't `--json` a big console log** — `af jenkins log --json` wraps the entire log in a single-line `{"output": "..."}`. Prefer the raw form plus `grep`/`tail`.
- **The recent-build count is fixed at 10** in `af jenkins job` and cannot be changed.
- **There is no unknown-flag error.** A typo'd `--foo bar` is silently swallowed (and eats `bar`, which then isn't treated as a positional argument); a trailing `--foo` with no value errors with `Option --foo requires a value`. Stick to `--json`.

## Out of Scope

`af jenkins` is read-only by design. It cannot:

- Trigger, re-run, cancel, or abort a build
- Update job configuration, or create/delete jobs
- Manage credentials, nodes, or plugins

## Error Handling

- Errors print to stderr. With `--json`, only errors raised by the API layer (missing `JENKINS_*` config, HTTP errors, `Stage "x" not found`, `Pipeline stages not available`) are re-emitted to **stdout** as `{"error": "message"}`. Usage errors, `Option --x requires a value`, `Unknown jenkins command`, and `No branches found` stay plain-text on **stderr** even with `--json` — always check the exit code, not just stdout.
- Exit codes: `0` success, `1` error — **never a build-health signal**

Distinctive errors worth recognising:

- `Pipeline stages not available. The Pipeline Stage View plugin may not be installed, or this job may not be a pipeline.` — from `stages`/`stage-log` on **any** HTTP 404. af rewrites every 404 into this one message, so it may equally mean a wrong job path or a nonexistent build number. Confirm the path with `af jenkins job <name>` first; if the path and build are right, it really is "not a pipeline / plugin missing" — fall back to `af jenkins log`.
- `No branches found. Is this a multibranch pipeline?` — from `branches` against a job with no children (e.g. a freestyle job). This is a hard exit 1, not an empty table.
- `Stage "<x>" not found. Available stages: ...` — from `stage-log`; the list is your discovery mechanism.
- `HTTP 404: ...` — usually a wrong job path. Re-read the Job Paths section and confirm with `af jenkins jobs`.
- `JENKINS_* is not set. ...` — missing credential, or you are not in the directory holding the `.env`.

