2026年9月17日
2026年9月17日
JenkinsのPipelineスクリプトの書き方(Jenkinsfileの基本構文と実例)
Jenkins の Pipeline は、ビルド・テスト・デプロイの手順を Jenkinsfile というテキストファイルに書き、リポジトリで一緒に管理する仕組みです。結論から言うと、これから書くなら **Declarative Pipeline(宣言型)を基本にし、複雑な分岐だけ script ブロックで補う** のが扱いやすい方法です。この記事では、基本構文、PHP/WordPress 案件を想定した実例、認証情報の安全な渡し方、書いたスクリプトの検証方法までを順に説明します。
Declarative と Scripted の違い
Pipeline の書き方には2種類あります。
- Declarative Pipeline:
pipeline { ... }で始まり、agent・stages・postなどの決まったブロックに沿って書く形式。構文チェックが効きやすく、Blue Ocean や Stage View での表示も整います。 - Scripted Pipeline:
node { ... }で始まり、Groovy のコードとして自由に書く形式。柔軟ですが、書き手によって構造がばらつき、レビューしにくくなりがちです。
チームで長く運用するなら Declarative を選び、ループや動的な分岐が必要な箇所だけ script { } の中に Groovy を書く、という分担が無難です。以降の例も Declarative を中心にしています。
Jenkinsfile の最小構成
まずは骨格を押さえます。pipeline の中に「どこで動かすか(agent)」と「何をするか(stages)」を書くのが最低限です。
pipeline {
agent any
stages {
stage('Build') {
steps {
echo 'ビルドを開始します'
sh 'php -v'
}
}
}
}
agent any は「空いているどのノードでも実行してよい」という意味です。ビルド用ノードを分けている場合は agent { label 'php' } のようにラベルで指定します。Docker Pipeline プラグインを入れていれば agent { docker { image 'composer:2' } } のようにコンテナ内で実行することもできます。
sh は Linux/macOS ノード用のステップで、Windows ノードでは bat や powershell を使います。ノードの OS とステップが合っていないと「コマンドが見つからない」系のエラーになるので、最初につまずきやすい点です。
PHP / WordPress 案件を想定した実例
テーマやプラグインのリポジトリで、Composer で依存を入れ、PHP_CodeSniffer と PHPUnit を回し、main ブランチだけデプロイする例です。
pipeline {
agent { label 'php' }
options {
timeout(time: 30, unit: 'MINUTES')
buildDiscarder(logRotator(numToKeepStr: '20'))
disableConcurrentBuilds()
}
environment {
COMPOSER_NO_INTERACTION = '1'
}
stages {
stage('Install') {
steps {
sh 'composer install --no-progress --prefer-dist'
}
}
stage('Checks') {
parallel {
stage('PHPCS') {
steps {
sh 'vendor/bin/phpcs --standard=phpcs.xml.dist'
}
}
stage('PHPUnit') {
steps {
sh 'vendor/bin/phpunit --log-junit build/junit.xml'
}
}
}
}
stage('Deploy') {
when {
branch 'main'
}
steps {
sshagent(credentials: ['deploy-ssh-key']) {
sh './scripts/deploy.sh'
}
}
}
}
post {
always {
junit allowEmptyResults: true, testResults: 'build/junit.xml'
}
failure {
echo 'ビルドが失敗しました。コンソール出力を確認してください'
}
}
}
各ブロックの役割と注意点は次のとおりです。
- options:
timeoutはハングしたビルドを打ち切る設定です。外部 API を叩くテストやcomposer installが止まったままエージェントを占有するのを防げます。buildDiscarderは古いビルド履歴を自動で消し、ディスクの圧迫を防ぎます。disableConcurrentBuildsは同じジョブの同時実行を止め、デプロイの競合を避けます。 - environment: ステージ全体で使う環境変数を定義します。秘密情報はここに直接書かず、後述の Credentials を使います。
- parallel: 互いに依存しないチェックを並列に流し、待ち時間を短くします。どちらかが失敗すると全体が失敗になります。
- when { branch 'main' }: ブランチ条件は Multibranch Pipeline のジョブで評価されるものです。通常の Pipeline ジョブでは期待どおりに判定されないことがあるので、ジョブの種類を確認してください。
- post: 成否にかかわらず実行したい処理(テスト結果の集計、通知など)を書きます。
always・success・failure・unstableなどの条件が使えます。
sshagent は SSH Agent プラグイン、junit は JUnit プラグイン、parallel などの Declarative 構文自体は Pipeline 系プラグインが提供します。プラグインが入っていないステップは「No such DSL method」というエラーになるので、エラー文に出たステップ名から必要なプラグインを探すのが近道です。
認証情報(Credentials)を安全に渡す
API トークンやデプロイ用の SSH 鍵は、Jenkins の Credentials に登録し、ID で参照します。Jenkinsfile はリポジトリに入るので、値そのものを書くのは避けてください。
stage('Notify') {
steps {
withCredentials([string(credentialsId: 'slack-webhook-url', variable: 'WEBHOOK_URL')]) {
sh '''
curl -fsS -X POST -H 'Content-Type: application/json' \
-d '{"text":"build finished"}' "$WEBHOOK_URL"
'''
}
}
}
ここで大事なのは **sh の引数をシングルクォートで囲む** ことです。ダブルクォートで "${WEBHOOK_URL}" と書くと、Groovy 側で文字列が展開されてからシェルに渡るため、値がプロセス一覧などに残るおそれがあります。最近の Jenkins はこの書き方を検出すると警告を出します。シングルクォートなら展開はシェルの環境変数として行われ、ログ上の値もマスクされます。
withCredentials は Credentials Binding プラグインの機能で、ユーザー名とパスワードの組なら usernamePassword(credentialsId: '...', usernameVariable: 'API_USER', passwordVariable: 'API_PASS') を使います。マスクはあくまで補助なので、set -x で値を表示したり、ファイルに書き出したりしないよう注意してください。
条件分岐やループは script ブロックで
Declarative の中で Groovy のロジックが必要になったら script { } を使います。
stage('Deploy targets') {
steps {
script {
def targets = ['staging']
if (env.BRANCH_NAME == 'main') {
targets << 'production'
}
for (t in targets) {
sh "./scripts/deploy.sh ${t}"
}
}
}
}
この例では t は秘密情報ではない固定値なので、ダブルクォートで展開しても問題ありません。秘密情報かどうかでクォートを使い分ける、と覚えておくと安全です。
script ブロックの Groovy はサンドボックスで実行され、許可されていないメソッドを呼ぶと「Scripts not permitted to use method ...」というエラーになります。管理画面の In-process Script Approval で承認できますが、内容を理解せずに承認すると任意コード実行の入口になり得ます。多くの場合は readJSON(Pipeline Utility Steps プラグイン)のような既存のステップで置き換えられるので、まずはそちらを検討してください。
Scripted Pipeline で書く場合
参考として、同じ考え方を Scripted で書くと次のようになります。
node('php') {
stage('Checkout') {
checkout scm
}
stage('Test') {
try {
sh 'composer install --no-progress'
sh 'vendor/bin/phpunit --log-junit build/junit.xml'
} finally {
junit allowEmptyResults: true, testResults: 'build/junit.xml'
}
}
}
post の代わりに try/finally で後処理を書く必要があり、timeout なども自分でラップすることになります。既存のジョブが Scripted で書かれている場合を除き、新規に書くなら Declarative の方が保守しやすいはずです。
書いたスクリプトを検証する方法
Jenkinsfile はコミットして実行するまで間違いに気づきにくいので、次の手段を組み合わせます。
- Pipeline Syntax(スニペットジェネレーター): ジョブ画面から開ける補助ツールで、ステップの引数をフォームで選ぶとコード片を生成してくれます。プラグインごとの正しい引数名を確認するのに便利です。画面上の表記はバージョンによって変わることがあります。
- Replay: 実行済みビルドの Jenkinsfile をその場で書き換えて再実行できる機能です。コミットせずに小さな修正を試せますが、確定した内容はリポジトリに反映してください。
- Declarative Linter: Jenkins の CLI や HTTP エンドポイントに Jenkinsfile を送ると、Declarative の構文チェックだけを行えます。エンドポイントや認証方式は環境によって異なるので、お使いの Jenkins のドキュメントを確認してください。
# Jenkins CLI(SSH 経由)で構文チェックする例。ホスト名・ポート・ユーザーは環境に合わせて変更
ssh -p 2222 your-user@jenkins.example.com declarative-linter < Jenkinsfile
構文は正しくても、ノードに composer が入っていない、作業ディレクトリが想定と違う、といった実行時の問題は残ります。最初は sh 'pwd && ls -la' のような確認用ステップを入れてログを読み、環境を把握してから本処理を書くと遠回りになりません。
よくあるつまずき
- **
when { branch }が効かない**: 通常の Pipeline ジョブで使っている可能性があります。Multibranch Pipeline にするか、env.GIT_BRANCHなどをexpressionで判定します。 - ワークスペースに前回の成果物が残る: Workspace Cleanup プラグインの
cleanWs()をpost { always { ... } }で呼ぶと、ビルドごとに作業領域を掃除できます。 - デプロイが途中で止まった: 本番デプロイのステージ前に
inputステップで承認を挟む、デプロイスクリプト側でバックアップを取ってから反映する、といった安全策を入れておくと復旧が楽になります。
Jenkins 本体と各プラグインは更新が頻繁で、ステップの引数や管理画面の表記は変わることがあります(2026年10月現在)。新しい書き方を採用する前に、公式の Pipeline Syntax リファレンスとプラグインのページで現在の仕様を確かめてください。
まとめ
- 新規に書くなら Declarative Pipeline を基本にし、複雑なロジックだけ
scriptブロックに閉じ込める。 optionsでタイムアウトと履歴の上限を決め、postで後処理と通知を書く。- 秘密情報は Credentials に登録し、
withCredentialsとシングルクォートのshで渡す。 - スニペットジェネレーター・Replay・Declarative Linter で、コミット前に構文と引数を確かめる。
- 本番デプロイの前には承認ステップやバックアップを入れ、まずステージング環境で動作を確認する。