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.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- jenkins-pipelines Installs to ~/.agents/skills/jenkins-pipelines/ for every project on this machine.
curl -fsSL codexskills.dev/cli.mjs -o codex-skills.mjsnode codex-skills.mjs install jenkins-pipelines The CLI also searches, updates and removes skills. Run it with no arguments to pick from a list. Needs Node 20.12+.
curl -fsSL codexskills.dev/install.sh \ | sh -s -- --project jenkins-pipelines Installs to .agents/skills/jenkins-pipelines/ in the current repository. Commit it and everyone who runs Codex there gets it.
curl -fsSLO codexskills.dev/downloads/jenkins-pipelines.zipunzip -o jenkins-pipelines.zip -d ~/.agents/skills/ The archive holds the skill folder with its generated agents/openai.yaml. Unzip into .agents/skills/ to scope it to a repo.
& ([scriptblock]::Create((irm codexskills.dev/install.ps1))) jenkins-pipelines PowerShell 5.1 or 7. Installs to %USERPROFILE%\.agents\skills\. Add -Project for the current repo.
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
- You are writing the replacement pipeline on GitHub: use github-actions-workflows
- You are writing the replacement pipeline on GitLab: use gitlab-ci
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.
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
- Read
AGENTS.mdand theMakefileorpackage.jsonscripts; stages should call those. - Read the
Jenkinsfileand find@Librarylines; open the library repo at the pinned ref. - Identify the controller version and installed plugins if possible (Manage Jenkins >
Plugins, or
jenkins-plugin-cli --listin a JCasC image). Steps likejunit,withCredentials,dockerContainerorpodTemplatecome from plugins. - Ask whether the controller has executors. Builds should run on agents, never the built-in node (set its executors to 0).
- 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 noneat the top plus per-stage agents means aninputwait holds no executor.beforeAgent trueevaluateswhenbefore allocating an agent.timestamps()needs the Timestamper plugin; drop it if missing.timeoutinsideoptionscovers the whole run, includinginput; give long approvals their owntimeoutaround 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 type | Binding |
|---|---|
| Secret text | string(credentialsId: 'id', variable: 'TOKEN') |
| Username and password | usernamePassword(credentialsId: 'id', usernameVariable: 'U', passwordVariable: 'P') |
| SSH key | sshUserPrivateKey(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 withwriteFile, 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,JsonSlurperresults, or matchers held across steps raiseNotSerializableException. UsereadJSON(Pipeline Utility Steps) or move logic into a@NonCPSmethod 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
shon 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/unstashfor 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) inpost { 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
- 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. - Classify jobs: simple (build/test), deploy (credentials, approvals), exotic (plugin specific). Migrate simple ones first and run both systems in parallel until results match.
- Map concepts:
stage-> job,parallel-> independent jobs,input-> protected environment approval,withCredentials-> OIDC or environment secrets, shared library -> reusable workflow or CI/CD component. - 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.yaml
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.