Jenkins Pipelines skill for Codex

Declarative Jenkinsfiles that stay boring. Codex keeps Jenkinsfiles declarative and thin, with shell scripts doing the work and Groovy only wiring stages. Every build gets a timeout and abort-previous concurrency, credentials go through single-quoted shell expansion so they never land in logs, shared code moves into a tag-pinned library, and migration off Jenkins starts with an inventory and parallel runs.

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- jenkins-pipelines

Installs to ~/.agents/skills/jenkins-pipelines/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $jenkins-pipelines, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Writes and maintains declarative Jenkinsfiles - agents per stage, options for timeouts and concurrency, when conditions, parallel and matrix stages, withCredentials without Groovy interpolation leaks, post blocks for JUnit and cleanup, input gates, versioned shared libraries in vars/ and src/, Jenkinsfile linting, and planned migrations to GitHub Actions or GitLab CI. Use when editing a Jenkinsfile, extracting duplicated pipeline code into a shared library, debugging CPS or sandbox errors, hardening credentials handling, or scoping a move off Jenkins.

Use it when

  • A repository builds on Jenkins and its Jenkinsfile needs changes
  • Pipeline code is copied across many repos
  • Builds fail with NotSerializableException or sandbox rejections
  • The team is planning to move off Jenkins

Use something else when

How to check the result

The pipeline-model-converter validate endpoint accepts the Jenkinsfile, and a branch build shows expected stages, published test results and masked credentials with no interpolation warning.

The mistake it prevents

Using double-quoted Groovy strings around a credential variable, which interpolates the secret into the logged command line.

~/.agents/skills/jenkins-pipelines/SKILL.md

--- name: jenkins-pipelines description: Writes and maintains declarative Jenkinsfiles - agents per stage, options for timeouts and concurrency, when conditions, parallel and matrix stages,… license: MIT ---

A maintainable Jenkinsfile is declarative, short, and boring: shell scripts do the work, Groovy only wires stages together, every build has a timeout, credentials never pass through Groovy string interpolation, and shared logic lives in a versioned library pinned by tag. If the Jenkinsfile reads like a program, move the logic into scripts the developer can run locally.

Inspect first

  1. Read AGENTS.md and the Makefile or package.json scripts; stages should call those.
  2. Read the Jenkinsfile and find @Library lines; open the library repo at the pinned ref.
  3. Identify the controller version and installed plugins if possible (Manage Jenkins > Plugins, or jenkins-plugin-cli --list in a JCasC image). Steps like junit, withCredentials, dockerContainer or podTemplate come from plugins.
  4. Ask whether the controller has executors. Builds should run on agents, never the built-in node (set its executors to 0).
  5. Lint before committing (network to the controller, authenticated):
curl -s -X POST --user "$JENKINS_USER:$JENKINS_TOKEN" \
  -F "jenkinsfile=<Jenkinsfile" "$JENKINS_URL/pipeline-model-converter/validate"

Baseline Jenkinsfile

pipeline {
  agent none
  options {
    timeout(time: 30, unit: 'MINUTES')
    disableConcurrentBuilds(abortPrevious: true)
    buildDiscarder(logRotator(numToKeepStr: '30', artifactNumToKeepStr: '5'))
    timestamps()
  }
  environment {
    CI = 'true'
  }
  stages {
    stage('Checks') {
      parallel {
        stage('Lint') {
          agent { docker { image 'node:22-bookworm-slim' } }
          steps { sh 'npm ci && npm run lint' }
        }
        stage('Test') {
          agent { docker { image 'node:22-bookworm-slim' } }
          steps { sh 'npm ci && npm test -- --reporter=junit --outputFile=reports/junit.xml' }
          post { always { junit allowEmptyResults: false, testResults: 'reports/*.xml' } }
        }
      }
    }
    stage('Deploy production') {
      when { branch 'main'; beforeAgent true }
      input {
        message 'Deploy to production?'
        submitter 'release-managers'
      }
      agent { label 'deploy' }
      steps {
        withCredentials([string(credentialsId: 'prod-deploy-token', variable: 'DEPLOY_TOKEN')]) {
          sh './scripts/deploy.sh production'   // script reads $DEPLOY_TOKEN from env
        }
      }
    }
  }
  post {
    failure { echo "Build failed: ${env.BUILD_URL}" }
  }
}

Notes on the choices:

  • agent none at the top plus per-stage agents means an input wait holds no executor.
  • beforeAgent true evaluates when before allocating an agent.
  • timestamps() needs the Timestamper plugin; drop it if missing.
  • timeout inside options covers the whole run, including input; give long approvals their own timeout around the input stage if needed.

Credentials without leaks

// Wrong: Groovy interpolates the secret into the command string, which is logged and
// visible in process listings. Jenkins warns "insecure interpolation of sensitive variables".
sh "curl -H 'Authorization: Bearer ${DEPLOY_TOKEN}' https://api.example.com"

// Right: single quotes, the shell expands the environment variable.
sh 'curl -H "Authorization: Bearer $DEPLOY_TOKEN" https://api.example.com'
Credential typeBinding
Secret textstring(credentialsId: 'id', variable: 'TOKEN')
Username and passwordusernamePassword(credentialsId: 'id', usernameVariable: 'U', passwordVariable: 'P')
SSH keysshUserPrivateKey(credentialsId: 'id', keyFileVariable: 'KEY') or the ssh-agent plugin
Secret file (kubeconfig)file(credentialsId: 'id', variable: 'KUBECONFIG')
  • Scope credentials to the folder that needs them, not global.
  • For cloud access prefer workload identity on the agent (IAM role on the EC2 or Kubernetes service account) over stored keys.

Shared libraries

jenkins-lib/
├── vars/
│   └── nodePipeline.groovy     # global step: nodePipeline(nodeVersion: '22')
├── src/org/acme/ci/Semver.groovy
└── resources/org/acme/ci/deploy.sh
// vars/nodePipeline.groovy
def call(Map cfg = [:]) {
  def image = "node:${cfg.get('nodeVersion', '22')}-bookworm-slim"
  pipeline {
    agent { docker { image image } }
    options { timeout(time: cfg.get('timeoutMinutes', 30), unit: 'MINUTES') }
    stages {
      stage('Install') { steps { sh 'npm ci' } }
      stage('Test')    { steps { sh 'npm test' } }
    }
  }
}
// Jenkinsfile in a consuming repo
@Library('[email protected]') _
nodePipeline(nodeVersion: '22')
  • Pin by tag, never @main, so a library change cannot break every repo at once. Roll out library changes by bumping the tag in one repo first.
  • Load non-Groovy files with libraryResource('org/acme/ci/deploy.sh'), write them with writeFile, then run them.
  • Untrusted (folder-level) libraries run in the Groovy sandbox; trusted global libraries do not. Keep trusted libraries small and code-reviewed.

Groovy and CPS traps

  • Pipeline Groovy is CPS-transformed: closures inside .each {} on non-serializable objects, JsonSlurper results, or matchers held across steps raise NotSerializableException. Use readJSON (Pipeline Utility Steps) or move logic into a @NonCPS method that does not call pipeline steps.
  • script { } blocks in declarative pipelines are an escape hatch; more than a few lines means the logic belongs in a shell script or library.
  • Heavy Groovy runs on the controller, not the agent. Parsing large files in Groovy loads the controller; do it in sh on the agent.
  • “Scripts not permitted to use method” means a sandbox rejection. Rewrite with allowed steps instead of asking an admin to approve broad signatures like java.io.File.

Speed and hygiene

  • stash/unstash for small build outputs between agents; use an artifact store for anything over a few hundred MB.
  • skipDefaultCheckout() on stages that do not need source.
  • Clean workspaces with cleanWs() (Workspace Cleanup plugin) in post { cleanup { } } on long-lived agents.
  • Use matrix { axes { axis { name 'NODE'; values '20', '22' } } stages { ... } } for version fan-out instead of copy-pasted stages.

Migrating off Jenkins

  1. Inventory: jobs, plugins they rely on, credentials, agents and triggers. GitHub’s gh actions-importer audit jenkins (extension, needs network and a Jenkins token) produces an inventory and estimated conversion rate.
  2. Classify jobs: simple (build/test), deploy (credentials, approvals), exotic (plugin specific). Migrate simple ones first and run both systems in parallel until results match.
  3. Map concepts: stage -> job, parallel -> independent jobs, input -> protected environment approval, withCredentials -> OIDC or environment secrets, shared library -> reusable workflow or CI/CD component.
  4. Switch the required status check to the new system, then disable (do not delete) the Jenkins job. Deleting jobs and their history needs explicit user approval.

Hand off to github-actions-workflows or gitlab-ci for the target pipeline.

Verification

  • The validate endpoint returns “Jenkinsfile successfully validated.”
  • Run on a branch (multibranch job) and confirm: stages appear as expected, test results show on the build page, the build times out rather than hangs, and the console shows **** where credentials are used and no interpolation warning.
  • For library changes, use Replay on one consuming job before tagging.

Output

Report the stages and their agents, credentials used and how they are bound, the library version pinned, plugins the pipeline depends on, and any controller-side changes (plugin installs, script approvals) the user must make.

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Jenkins Pipelines"
  short_description: "Declarative Jenkinsfiles that stay boring"
  default_prompt: "$jenkins-pipelines Clean up this 400-line scripted Jenkinsfile into a declarative pipeline"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem