Jenkins はオープンソースの CI/CD オートメーションサーバーです。ビルド・テスト・デプロイといったパイプラインをプラグインで柔軟に拡張でき、オンプレミス環境での自律的な CI 基盤として広く利用されています。
本リポジトリを Oracle Linux 上の Jenkins でビルドするには、Jenkins のインストール・初期設定に加えて、rootless Podman の設定と、GitHub Actions と同じコンテナイメージを使ったビルドジョブの構成が必要です。以下の手順例では、そのセットアップから動作確認、ドキュメントの静的サイト公開までをカバーしています。コンテナイメージは GitHub Container Registry (ghcr.io) と Docker Hub のどちらからでも取得できます。
本書は、Oracle Linux 8 上に Jenkins を導入し、c-modernization-kit のビルドとテストを実行できる状態にするための手順例です。
git、make、Podman が利用可能であること8080/tcp を開放する場合は、信頼できる送信元に限定してください。Jenkins ジョブ内でリポジトリを clone するため、先に Git をインストールします。
sudo dnf install -y git
git --versiongit --version で利用可能なことを確認してください。
Jenkins で必要となる Java 17 をインストールします。
sudo dnf install -y java-17-openjdk
java -versionjava -version で Java 17 系が利用できることを確認してください。
Jenkins の公式 RPM リポジトリを登録し、パッケージをインストールします。
sudo wget -O /etc/yum.repos.d/jenkins.repo \
https://pkg.jenkins.io/redhat-stable/jenkins.repo
sudo rpm --import https://pkg.jenkins.io/redhat-stable/jenkins.io.key
sudo dnf install -y jenkinsサービスを有効化して起動します。
sudo systemctl daemon-reexec
sudo systemctl enable jenkins
sudo systemctl start jenkins
sudo systemctl status jenkinsactive (running) と表示されれば起動しています。
Jenkins の Web UI を利用する場合は、必要に応じてポートを開放します。
sudo firewall-cmd --add-port=8080/tcp --permanent
sudo firewall-cmd --reload本番運用では、単純な全体開放ではなく、送信元制限やリバースプロキシの導入を検討してください。
ブラウザで次の URL にアクセスします。
http://<JENKINS_SERVER>:8080
初回アクセス時は、Jenkins が表示する案内に従って初期管理者パスワードを取得します。値そのものは記録・共有せず、その場でのみ使用してください。
初期設定ウィザードでは、通常は次の流れで進めます。
公開用手順書では、実際のパスワード値や画面キャプチャ内の機密情報は掲載しない運用を推奨します。
Oracle Linux 8 の標準的な構成では、Jenkins サービスは jenkins ユーザーで動作します。ホームディレクトリは通常 /var/lib/jenkins です。
ビルドジョブから Podman を利用する場合は、ジョブがこの実行ユーザー権限で動くことを前提に設定してください。
本リポジトリのビルド補助やコンテナ利用を想定し、Jenkins 実行ユーザーで rootless Podman を使えるようにします。
sudo dnf install -y podman slirp4netns fuse-overlayfs shadow-utils
rpm -q podmanrootless Podman では、jenkins ユーザーに subordinate UID/GID が必要です。未設定の場合は、no subuid ranges found for user "jenkins" のようなエラーになります。
sudo usermod --add-subuids 100000-165535 --add-subgids 100000-165535 jenkins設定後、結果を確認します。
grep '^jenkins:' /etc/subuid
grep '^jenkins:' /etc/subgidrootless コンテナを安定して利用するため、jenkins ユーザーに linger を設定します。
sudo loginctl enable-linger jenkinsjenkins ユーザーとして Podman 情報を確認し、rootless で利用できることを確認します。
sudo -u jenkins -H bash -c 'cd ~ && podman info --debug'確認時の主な観点は次のとおりです。
rootless: true であること必要に応じて、簡単なコンテナ起動確認も jenkins ユーザーで実施してください。
sudo -u jenkins -H bash -c 'cd ~ && podman run --rm docker.io/library/alpine:latest echo ok'外部公開ポートを使った確認は、検証用ネットワークに限定して実施してください。
Jenkins では、ジョブ実行時にワークスペース内へリポジトリを clone し、その後 Podman で GitHub Actions と同じコンテナイメージを使ってビルドする構成にできます。
この構成では、Source Code Management (ソースコード管理) は使わず、Build Steps (ビルド手順) 内のシェルスクリプトで以下を実行します。
podman pull するgit clone するmake と make test を実行する本リポジトリの Linux CI では、Oracle Linux 開発コンテナを使用しています。コンテナイメージは GitHub Container Registry (ghcr.io) と Docker Hub の両方から取得できます。
認証なしで pull できます(公開イメージ)。
| イメージ | タグ |
|---|---|
| oracle-linux-8-dev | latest / main / vYYYYMMDD.x.x |
| oracle-linux-10-dev | latest / main / vYYYYMMDD.x.x |
podman pull ghcr.io/hondarer/oracle-linux-container/oracle-linux-8-dev:latest
podman pull ghcr.io/hondarer/oracle-linux-container/oracle-linux-10-dev:latestGitHub Secrets (DOCKERHUB_USERNAME / DOCKERHUB_TOKEN) が設定されている場合に ghcr.io と同一タグで push されます。公開されているため、認証なしで pull できます。
| イメージ | タグ |
|---|---|
| hondarer/oracle-linux-8-dev | latest / main / vYYYYMMDD.x.x |
| hondarer/oracle-linux-10-dev | latest / main / vYYYYMMDD.x.x |
podman pull hondarer/oracle-linux-8-dev:latest
podman pull hondarer/oracle-linux-10-dev:latestどちらのレジストリを使用するかは環境に応じて選択してください。ネットワーク制限や帯域の都合がなければ ghcr.io を推奨します。
公開リポジトリであれば HTTPS で clone できます。非公開リポジトリの場合は、Jenkins Credentials に読み取り専用の認証情報を登録し、Build Steps から安全に参照してください。
最も単純な方法として Freestyle Project または Pipeline のどちらでも構成できます。ここでは Freestyle Project を前提に最小構成を示します。
この例では、ジョブ内で git clone を実行するため、Source Code Management (ソースコード管理) は None (なし) のままにします。
リポジトリ URL や認証情報は、後述の Execute shell 内で使用します。
ビルドが蓄積するとワークスペースとアーティファクトがディスクを圧迫します。General (全般) > Discard Old Builds (古いビルドの破棄) を有効にし、Advanced… (高度な設定) を開いて以下のように設定します。
| 項目 | 設定例 | 説明 |
|---|---|---|
| Max # of builds to keep | 10 |
ビルド記録 (ログ・テスト結果) の最大保持件数 |
| Max # of builds to keep with artifacts | 5 |
アーティファクト (HTML ドキュメント・zip) を保持するビルドの最大件数 |
この設定により、ビルド記録は直近 10 件、アーティファクトは直近 5 件 (≒ 5 世代) を保持します。
古いビルドのアーティファクトは自動削除されますが、ビルドログは 10 件分残るため、過去の実行状況の確認が可能です。
ヒント
Days to keep builds/Days to keep artifactsとの組み合わせも可能です。
たとえばDays to keep builds = 90、Max # of builds to keep with artifacts = 5とすると
「直近 90 日のビルド記録を保持しつつ、アーティファクトは最新 5 件のみ保持」という運用になります。
Build Steps (ビルド手順) に Execute shell (シェルの実行) を追加し、次のようなコマンドを設定します。
Oracle Linux 開発コンテナは既定の ENTRYPOINT で entrypoint.sh を実行し、最終的に sshd -D で待機します。
そのため Jenkins でワンショット実行する場合は、--entrypoint /bin/bash で既定エントリーポイントを上書きし、コンテナ内で devcontainer-entrypoint.sh を明示的に呼び出してからビルドを実行します。
この例では、Jenkins ワークスペース内に source ディレクトリを作成し、そこへ clone した内容を /workspace にマウントしてビルドします。
REPO_URL は、適宜変更してください。
また、この例は .github/workflows/ci.yml の Linux ジョブに合わせて、Oracle Linux 8 イメージ、ログ出力、LD_LIBRARY_PATH の設定方針を反映しています。
GitHub Actions では HOST_UID=1001、HOST_GID=127 の固定値を使っていますが、Jenkins では実行ユーザーの UID/GID が環境依存のため、以下の例では動的に取得して渡します。
set -eu
REPO_URL="https://github.com/Hondarer/c-modernization-kit.git"
IMAGE="ghcr.io/hondarer/oracle-linux-container/oracle-linux-8-dev:latest"
WORKDIR="$PWD/source"
OS_NAME="ol8"
BUILD_DOCS="1"
HOST_USER="$(id -un)"
HOST_UID="$(id -u)"
HOST_GID="$(id -g)"
rm -rf "$WORKDIR"
mkdir -p "$WORKDIR"
podman pull "$IMAGE"
git clone --recurse-submodules "$REPO_URL" "$WORKDIR"
podman run --rm -i \
--user root \
--userns=keep-id \
--entrypoint /bin/bash \
-e HOST_USER="$HOST_USER" \
-e HOST_UID="$HOST_UID" \
-e HOST_GID="$HOST_GID" \
-e OS_NAME="$OS_NAME" \
-e BUILD_DOCS="$BUILD_DOCS" \
-v "$WORKDIR:/workspace:Z" \
"$IMAGE" \
-s <<'EOF'
/usr/local/bin/devcontainer-entrypoint.sh
su - "$HOST_USER" -c "OS_NAME='$OS_NAME' BUILD_DOCS='$BUILD_DOCS' bash -l -s" <<'INNER_EOF'
git config --global --add safe.directory /workspace
cd /workspace
mkdir -p logs
# ビルドログを保存しながら make を実行
set -o pipefail
make 2>&1 | tee "logs/linux-${OS_NAME}-build.log"
# テスト実行時に必要な共有ライブラリ検索パスを設定
export LD_LIBRARY_PATH="/workspace/prod/calc/lib:/workspace/prod/calc.net/lib:${LD_LIBRARY_PATH:-}"
# テストログを保存しながら make test を実行
set -o pipefail
make test 2>&1 | tee "logs/linux-${OS_NAME}-test.log"
# テスト結果とログのアーカイブ (GitHub Actions と同じ命名規則)
mkdir -p /workspace/docs/artifacts
if find /workspace/test -type d -name results 2>/dev/null | grep -q .; then
(cd /workspace && zip -r "docs/artifacts/linux-${OS_NAME}-test-results.zip" \
$(find test -type d -name results)) || true
fi
if [ -d "/workspace/logs" ]; then
(cd /workspace && zip -r "docs/artifacts/linux-${OS_NAME}-logs.zip" logs) || true
fi
# ドキュメント生成
if [ "${BUILD_DOCS}" = "1" ]; then
make doxy 2>&1 | tee "logs/linux-${OS_NAME}-doxy.log"
make docs 2>&1 | tee "logs/linux-${OS_NAME}-docs.log"
# HTML ドキュメントのアーカイブ (GitHub Actions と同じ命名規則)
if [ -d "/workspace/docs/doxygen" ]; then
(cd /workspace && zip -r docs/artifacts/docs-html-doxygen.zip docs/doxygen) || true
fi
find /workspace/docs -mindepth 2 -type d -name html | sort | while read -r html_dir; do
label=$(echo "$html_dir" | sed 's|^/workspace/docs/||;s|/html$||;s|/|-|g')
(cd /workspace && zip -r "docs/artifacts/docs-html-${label}.zip" "${html_dir#/workspace/}") || true
done
# DOCX ドキュメントのアーカイブ
find /workspace/docs -mindepth 2 -type d -name docx | sort | while read -r docx_dir; do
label=$(echo "$docx_dir" | sed 's|^/workspace/docs/||;s|/docx$||;s|/|-|g')
(cd /workspace && zip -r "docs/artifacts/docs-docx-${label}.zip" "${docx_dir#/workspace/}") || true
done
fi
# docs/index.html の生成 (GitHub Actions と同じ構造, HTML Publisher Plugin のエントリーページ)
{
cat <<'INDEX_TOP'
<!DOCTYPE html>
<html lang="ja">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1">
<title>c-modernization-kit ドキュメント</title>
<style>
body { font-family: sans-serif; max-width: 900px; margin: 2em auto; padding: 0 1em; }
h1 { border-bottom: 2px solid #333; padding-bottom: 0.3em; }
h2 { border-bottom: 1px solid #ccc; margin-top: 1.5em; }
table { border-collapse: collapse; width: 100%; }
th, td { text-align: left; padding: 0.4em 0.8em; border: 1px solid #ddd; }
th { background: #f6f8fa; }
a { color: #0366d6; text-decoration: none; }
a:hover { text-decoration: underline; }
</style>
</head>
<body>
<h1>c-modernization-kit ドキュメント</h1>
INDEX_TOP
if [ "${BUILD_DOCS}" = "1" ]; then
# Doxygen サブフォルダを自動探索してリンク生成
if [ -d "/workspace/docs/doxygen" ] && \
find /workspace/docs/doxygen -maxdepth 1 -mindepth 1 -type d | grep -q .; then
echo ' <h2>Doxygen ドキュメント</h2>'
echo ' <table>'
echo ' <tr><th>モジュール</th><th>リンク</th></tr>'
find /workspace/docs/doxygen -maxdepth 1 -mindepth 1 -type d | sort | while read -r subdir; do
name=$(basename "$subdir")
echo " <tr><td>${name}</td><td><a href=\"doxygen/${name}/index.html\">${name} リファレンス</a></td></tr>"
done
echo ' </table>'
fi
# Markdown ドキュメント
if find /workspace/docs -maxdepth 2 -name html -type d | grep -q .; then
cat <<'LANG_TABLE'
<h2>Markdown ドキュメント</h2>
<table>
<tr><th>言語</th><th>種別</th><th>リンク</th></tr>
<tr><td>日本語</td><td>通常版</td><td><a href="ja/html/index.html">日本語ドキュメント</a></td></tr>
<tr><td>English</td><td>Standard</td><td><a href="en/html/index.html">English Documentation</a></td></tr>
<tr><td>日本語</td><td>詳細版</td><td><a href="ja-details/html/index.html">日本語ドキュメント (詳細)</a></td></tr>
<tr><td>English</td><td>Details</td><td><a href="en-details/html/index.html">English Documentation (Details)</a></td></tr>
</table>
LANG_TABLE
fi
fi
# アーティファクト (BUILD_DOCS に関係なく常に表示、存在するものを列挙)
if [ -d "/workspace/docs/artifacts" ] && \
find /workspace/docs/artifacts -name "*.zip" | grep -q .; then
echo ' <h2>アーティファクト</h2>'
echo ' <table>'
echo ' <tr><th>ファイル名</th></tr>'
find /workspace/docs/artifacts -name "*.zip" | sort | while read -r zipfile; do
fname=$(basename "$zipfile")
echo " <tr><td><a href=\"artifacts/${fname}\">${fname}</a></td></tr>"
done
echo ' </table>'
fi
cat <<'INDEX_BOTTOM'
</body>
</html>
INDEX_BOTTOM
} > /workspace/docs/index.html
INNER_EOF
EOFドキュメント生成を実施しない場合は、BUILD_DOCS="0" に変更してください。
この構成では、コンテナ内でまず devcontainer-entrypoint.sh が実行され、HOST_USER / HOST_UID / HOST_GID に基づいてユーザーとホームディレクトリが初期化されます。その後、作成済みユーザー権限で make などを実行します。
非公開リポジトリを clone する場合は、アクセストークンやパスワードをスクリプトへ直書きしないでください。Jenkins Credentials と Credentials Binding を使い、環境変数経由で参照してください。
また、コンテナイメージを GitHub Container Registry や Docker Hub から取得する際に認証が必要な場合も、同様に Jenkins Credentials を使用してください。
ジョブ保存後に Build Now (今すぐビルド) を実行し、以下を確認します。
podman pull で CI と同じイメージを取得できるdevcontainer-entrypoint.sh によるユーザー初期化が成功する/workspace でコマンドを実行できるmake が成功するmake test が成功するsource/docs/artifacts/linux-ol8-test-results.zip 等が生成されるsource/docs/index.html が生成されるBUILD_DOCS="1" の場合は source/docs/artifacts/docs-html-doxygen.zip 等も生成される必要に応じて Console Output を確認し、環境変数や依存パッケージ不足を調整してください。
make
make test
make doxy
make docsビルドジョブで生成したドキュメント (source/docs) を、Jenkins の登録ユーザーのみ閲覧できる静的サイトとして公開する方法を示します。
Jenkins の HTML Publisher Plugin を使うと、ジョブのワークスペース内にあるディレクトリを HTML として公開できます。
公開された URL へのアクセスには Jenkins へのログインが必要になるため、Jenkins に登録されたユーザーのみが閲覧できます。外部の Web サーバーは不要です。
ジョブ名に関する注意
ジョブ名にスペースが含まれる場合 (例:build test)、URL はbuild%20testのようにエンコードされます。
運用上の混乱を避けるため、ジョブ名はスペースを使わない名前 (例:build-test) にすることを推奨します。
HTML Publisher と入力する既存のジョブ設定を開き、Post-build Actions (ビルド後の処理) に Publish HTML reports (HTMLレポートの公開) を追加します。
| 項目 | 設定値 |
|---|---|
| HTML directory to archive | source/docs |
| Index page(s) | index.html |
| Report title | docs-and-artifacts (任意) |
| Keep past HTML reports | チェックを入れると過去のビルドのレポートも保持される |
設定を保存し、ジョブを実行します。ビルド完了後、ジョブのトップページに Docs リンクが表示されます。
ビルドスクリプトにより source/docs/index.html が自動生成されるため、アクセス時に Doxygen API ドキュメント (モジュール別)・言語別ドキュメント・アーティファクトへのリンクをまとめたナビゲーションページが表示されます。
公開 URL のパターンは次のとおりです。
http://<JENKINS_SERVER>:8080/job/<ジョブ名>/docs-and-artifacts/
特定ビルドのレポートを参照する場合は次の URL になります。
http://<JENKINS_SERVER>:8080/job/<ジョブ名>/<ビルド番号>/docs-and-artifacts/
Jenkins 2.x 以降は、デフォルトで厳格な Content Security Policy (CSP) が適用されており、公開した HTML 内の CSS や JavaScript は動作しません。Doxygen が生成するドキュメントもスクリプトが動作しないため、正常に閲覧するには CSP の緩和が必要です。
注意
CSP の緩和は、公開するコンテンツの信頼性を確認したうえで実施してください。
外部からの入力をそのまま HTML として出力するようなコンテンツには適用しないでください。
スクリプトコンソールからの一時的な適用
Jenkins 再起動まで有効な一時的な緩和は、Manage Jenkins (Jenkinsの管理) > Script Console から次の Groovy スクリプトを実行します。
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")再起動後も有効にする設定
Jenkins 起動時に自動で適用されるよう、Init Script を使います。
/var/lib/jenkins/init.groovy.d/ ディレクトリを作成します (存在しない場合)。
sudo mkdir -p /var/lib/jenkins/init.groovy.d次の内容のスクリプトファイルを作成します。
sudo tee /var/lib/jenkins/init.groovy.d/disable-csp.groovy <<'EOF'
import jenkins.model.Jenkins
System.setProperty("hudson.model.DirectoryBrowserSupport.CSP", "")
EOFファイルのオーナーを jenkins ユーザーに設定します。
sudo chown jenkins:jenkins /var/lib/jenkins/init.groovy.d/disable-csp.groovyJenkins を再起動して設定を反映します。
sudo systemctl restart jenkinsドキュメントが Jenkins 登録ユーザーのみ閲覧できることを確認します。
匿名アクセスが無効になっていることを確認する
匿名ユーザーに Read 権限が付与されている場合、ログインなしでドキュメントにアクセスできてしまいます。
ブラウザで動作確認する