How to Route Jenkins Builds Through Chainsaw

Intermediate 20 minutes DevOps / Platform Engineers Advanced Configuration

Configure a Jenkins declarative pipeline to install packages through Chainsaw using a Username-with-password credential and job-local package-manager configuration.

Overview

Jenkins agents are typically long-lived with persistent workspaces, so it is especially important to generate package-manager configuration inside each stage, pull credentials from Jenkins Credentials with withCredentials, and clear local caches on the first Chainsaw run. This page is the Jenkins-specific slice of How to Integrate Chainsaw with CI/CD Pipelines; see that guide for GitHub Actions and GitLab CI and for the cross-cutting cache-invalidation and policy guidance.

Prerequisites

  • A running Chainsaw instance reachable from your Jenkins agents
  • A Chainsaw client credential with Client Type set to Service Token
  • Repository scope set to only the ecosystems the pipeline needs
  • The service token stored in Jenkins Credentials as a Username with password credential named chainsaw-service-token (Client ID as username, Client Secret as password)
Persistent Jenkins workspaces hide bypasses. A workspace or agent that already has node_modules, ~/.m2/repository, or ~/.gradle/caches populated will reuse those packages without ever calling Chainsaw. Clear them on the first Chainsaw run (the snippets below do this), and rotate the workspace if you are unsure.

Declarative Pipeline

Each stage wraps its work in withCredentials so the Client ID and Secret are injected as CHAINSAW_CLIENT_ID / CHAINSAW_CLIENT_SECRET only for that step.

pipeline {
  agent any

  stages {
    stage('npm install') {
      steps {
        withCredentials([usernamePassword(credentialsId: 'chainsaw-service-token',
          usernameVariable: 'CHAINSAW_CLIENT_ID', passwordVariable: 'CHAINSAW_CLIENT_SECRET')]) {
          sh '''
            rm -rf node_modules package-lock.json
            CHAINSAW_NPM_AUTH="$(printf '%s' "$CHAINSAW_CLIENT_ID:$CHAINSAW_CLIENT_SECRET" | base64)"
            cat > .npmrc <<EOF
registry=https://chain305.com/chainproxy/repository/@default/npmjs/
//chain305.com/chainproxy/repository/@default/npmjs/:_auth=${CHAINSAW_NPM_AUTH}
//chain305.com/chainproxy/repository/@default/npmjs/:always-auth=true
EOF
            npm ci
          '''
        }
      }
    }

    stage('maven build') {
      steps {
        withCredentials([usernamePassword(credentialsId: 'chainsaw-service-token',
          usernameVariable: 'CHAINSAW_CLIENT_ID', passwordVariable: 'CHAINSAW_CLIENT_SECRET')]) {
          sh '''
            rm -rf ~/.m2/repository
            mkdir -p ~/.m2
            cat > ~/.m2/settings.xml <<'XML'
<settings>
  <mirrors>
    <mirror>
      <id>chainsaw</id>
      <url>https://chain305.com/chainproxy/repository/@default/maven-central/</url>
      <mirrorOf>*</mirrorOf>
    </mirror>
  </mirrors>
  <servers>
    <server>
      <id>chainsaw</id>
      <username>${env.CHAINSAW_CLIENT_ID}</username>
      <password>${env.CHAINSAW_CLIENT_SECRET}</password>
    </server>
  </servers>
</settings>
XML
            mvn -B verify
          '''
        }
      }
    }

    stage('docker pull') {
      steps {
        withCredentials([usernamePassword(credentialsId: 'chainsaw-service-token',
          usernameVariable: 'CHAINSAW_CLIENT_ID', passwordVariable: 'CHAINSAW_CLIENT_SECRET')]) {
          sh '''
            echo "$CHAINSAW_CLIENT_SECRET" | docker login chain305.com --username "$CHAINSAW_CLIENT_ID" --password-stdin
            docker pull chain305.com/library/alpine:3.21
          '''
        }
      }
    }

    stage('gradle build') {
      steps {
        withCredentials([usernamePassword(credentialsId: 'chainsaw-service-token',
          usernameVariable: 'CHAINSAW_CLIENT_ID', passwordVariable: 'CHAINSAW_CLIENT_SECRET')]) {
          sh '''
            rm -rf ~/.gradle/caches
            mkdir -p ~/.gradle
            cat > ~/.gradle/init.gradle <<'GRADLE'
allprojects {
    repositories {
        clear()
        maven {
            url "https://chain305.com/chainproxy/repository/@default/gradle-central/"
            credentials {
                username = System.getenv("CHAINSAW_CLIENT_ID")
                password = System.getenv("CHAINSAW_CLIENT_SECRET")
            }
        }
        maven {
            url "https://chain305.com/chainproxy/repository/@default/google-maven/"
            credentials {
                username = System.getenv("CHAINSAW_CLIENT_ID")
                password = System.getenv("CHAINSAW_CLIENT_SECRET")
            }
        }
    }
}
GRADLE
            ./gradlew build
          '''
        }
      }
    }
  }
}

A policy block returns HTTP 403, the install command exits non-zero, the stage fails, and the build is marked failed — the dependency never lands. Gate downstream promotion or deploy stages on this build’s success.

Use a dedicated PR/branch-build service token so failing-build noise and audit trails stay separate from production builds. See How to Create and Manage Client Credentials.

Verify Pipeline Activity

After the first run:

  1. Open Traffic in the Chainsaw dashboard.
  2. Filter by the service token’s Client ID.
  3. Confirm requests use the expected repository and outcome.
  4. Check the cache hit ratio after a warm run.

Next Steps