WordPressテーマのCI/CDをGitHub Actionsで構築する(Minifyを追加)
はじめに
このブログ記事では、WordPress テーマの CI/CD に、画像の最適化、JavaScript と CSS の minify 処理を追加する方法について解説します。
対象読者
- WordPress テーマの CI/CD に興味がある方
- GitHub Actions を使って WordPress テーマの CI/CD を構築したい方
- WordPress テーマのパフォーマンスを改善したい方
前提知識
- WordPress の基本的な知識
- CI/CD の基本的な知識
- GitHub Actions の基本的な知識
CI/CDとは
CI/CD(継続的インテグレーション/継続的デリバリー)とは、ソフトウェア開発における変更をより頻繁かつ確実にリリースするためのプラクティスです。
Minifyとは
Minify とは、JavaScript や CSS などのファイルのサイズを小さくする処理のことです。具体的には、コメントや空白文字を削除したり、変数名を短くしたりします。
Minifyを行うメリット
- ファイルサイズが小さくなるため、Web サイトの表示速度が向上する。
- ファイルサイズが小さくなるため、サーバーの負荷が軽減される。
- ファイルサイズが小さくなるため、ユーザーのデータ通信量を削減できる。
今回は、Deploy する際に画像の最適化や CSS の minify と言った webpack が普通は行うようなビルドを少し取り入れます。
設定
jpg, pngファイルを圧縮するカスタムアクション。
このアクションは、pngquant と jpegoptim を使って、リポジトリ内の PNG と JPG 画像を最適化します。
- pngquant: PNG 画像を非可逆圧縮するツールです。
- jpegoptim: JPG 画像を非可逆圧縮するツールです。
.github/actions/optimize-image/action.yml
name: 'Optimize Images with Pngquant & Jpegoptim'description: 'Finds and optimizes PNG and JPG images in the repository using pngquant and jpegoptim.'
inputs: png_quality: description: 'Quality range for pngquant (e.g., 65-80)' required: false default: '65-80' jpeg_quality: description: 'Quality for jpegoptim (0-100, lower is smaller file, more loss)' required: false default: '80' # jpegoptim のデフォルト品質 strip_metadata: description: 'Remove all metadata (EXIF, comments, etc.) from JPEGs (true/false)' required: false default: 'true'
outputs: optimized_png_count: description: 'Number of PNG images that were optimized.' value: ${{ steps.optimize_png.outputs.optimized_count }} optimized_jpg_count: description: 'Number of JPG images that were optimized.' value: ${{ steps.optimize_jpg.outputs.optimized_count }}
runs: using: "composite" steps: - name: Install image optimization tools run: | sudo apt-get update sudo apt-get install -y pngquant jpegoptim shell: bash
- name: Find and optimize PNG images id: optimize_png # IDを明確に run: | optimized_count=0
find . -type f -name "*.png" -print0 | while IFS= read -r -d $'\0' file; do original_size=$(stat -c %s "$file") pngquant --quality=${{ inputs.png_quality }} --force --ext .png "$file" optimized_size=$(stat -c %s "$file")
if [ "$original_size" -ne "$optimized_size" ]; then echo "Optimized PNG: $file (Original: $original_size bytes, Optimized: $optimized_size bytes)" optimized_count=$((optimized_count + 1)) else echo "Skipped PNG (no change): $file" fi done
echo "optimized_count=$optimized_count" >> $GITHUB_OUTPUT shell: bash
- name: Find and optimize JPG images id: optimize_jpg # IDを明確に run: | optimized_count=0
find . -type f -iregex ".*\.jpe?g$" -print0 | while IFS= read -r -d $'\0' file; do original_size=$(stat -c %s "$file")
# jpegoptimのオプションを動的に設定 JPEGOPTIM_OPTIONS="--max=${{ inputs.jpeg_quality }}" if [ "${{ inputs.strip_metadata }}" == "true" ]; then JPEGOPTIM_OPTIONS+=" --strip-all" fi
jpegoptim $JPEGOPTIM_OPTIONS "$file"
optimized_size=$(stat -c %s "$file")
if [ "$original_size" -ne "$optimized_size" ]; then echo "Optimized JPG: $file (Original: $original_size bytes, Optimized: $optimized_size bytes)" optimized_count=$((optimized_count + 1)) else echo "Skipped JPG (no change): $file" fi done
echo "optimized_count=$optimized_count" >> $GITHUB_OUTPUT shell: bashJavaScriptのminify
このアクションは、Terserを使って、JavaScriptファイルをminify(難読化)します。
- Terser: JavaScriptファイルをminify(難読化)するツールです。
.github/actions/optimize-js/action.yml
name: 'Uglify JavaScript with Terser'description: 'Finds and minifies/uglifies JavaScript files using Terser.'
inputs: paths: description: 'Glob pattern for JavaScript files to process (e.g., "**/*.js").' required: false default: '**/*.js' terser_options: description: 'JSON string of Terser minify options (e.g., {"compress":{},"mangle":true})' required: false default: '{"compress":true,"mangle":true}' # デフォルトは圧縮と変数名の難読化
outputs: uglified_count: description: 'Number of JavaScript files that were uglified/minified.' value: ${{ steps.uglify.outputs.uglified_count }}
runs: using: "composite" steps: - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' # Terserの実行に必要なNode.jsのバージョン
- name: Install Terser run: npm install -g terser # terserをグローバルインストール shell: bash
- name: Find and uglify JavaScript files id: uglify run: | uglified_count=0
# globパターンを配列として扱うための準備 # find . -name "*.js" よりも柔軟な指定が可能
# shopt -s globstar はワイルドカードを拡張する(bashでのみ有効) shopt -s globstar || true # エラーにならないように
# inputs.paths で指定されたファイルを処理 for file in ${{ inputs.paths }}; do if [ -f "$file" ]; then # ファイルが存在することを確認 original_size=$(stat -c %s "$file") # オリジナルファイルのサイズを取得
# Terserで難読化(上書き) # input: JSON文字列をparseして渡す terser "$file" -o "$file" --config-file <(echo '${{ inputs.terser_options }}')
optimized_size=$(stat -c %s "$file") # 難読化後のファイルのサイズを取得
if [ "$original_size" -ne "$optimized_size" ]; then echo "Uglified: $file (Original: $original_size bytes, Optimized: $optimized_size bytes)" uglified_count=$((uglified_count + 1)) else echo "Skipped (no change): $file" fi fi done
echo "uglified_count=$uglified_count" >> $GITHUB_OUTPUT shell: bashCSSのminify
このアクションは、clean-css-cliを使って、CSSファイルをminifyします。
- clean-css-cli: CSSファイルをminifyするツールです。
.github/actions/optimize-css/action.yml
name: 'Minify CSS with Clean-CSS'description: 'Finds and minifies CSS files using clean-css-cli.'
inputs: paths: description: 'Glob pattern for CSS files to process (e.g., "**/*.css").' required: false default: '**/*.css' clean_css_options: description: 'JSON string of clean-css options (e.g., {"level":2})' required: false default: '{}' # デフォルトは基本的な圧縮 output_suffix: description: 'Suffix to add to the minified file name (e.g., .min for style.min.css). Leave empty to overwrite.' required: false default: '' # デフォルトは上書き
outputs: minified_count: description: 'Number of CSS files that were minified.' value: ${{ steps.minify.outputs.minified_count }}
runs: using: "composite" steps: - name: Set up Node.js uses: actions/setup-node@v4 with: node-version: '20' # clean-css-cliの実行に必要なNode.jsのバージョン
- name: Install clean-css-cli run: npm install -g clean-css-cli # clean-css-cliをグローバルインストール shell: bash
- name: Find and minify CSS files id: minify run: | minified_count=0
shopt -s globstar || true
for file in ${{ inputs.paths }}; do if [ -f "$file" ]; then # ファイルが存在することを確認 original_size=$(stat -c %s "$file") # オリジナルファイルのサイズを取得
output_file="$file" if [ -n "${{ inputs.output_suffix }}" ]; then # 拡張子の前にサフィックスを追加 (e.g., style.css -> style.min.css) filename="${file%.*}" extension="${file##*.}" output_file="${filename}${inputs.output_suffix}.${extension}" fi
# clean-css-cliで圧縮 # input: JSON文字列をparseして渡す # 圧縮後にファイルサイズが変わるか確認 # clean-css-cli はデフォルトで上書きするが、 # -o で出力ファイルを明示的に指定できるため、上書きまたは別名保存が可能
# clean-css-cliのオプションを構築 CLEAN_CSS_OPTIONS="" if [ -n "${{ inputs.clean_css_options }}" ]; then # clean-css-cli はJSONファイルを直接受け取らないので、 # オプションをコマンドライン引数に変換するか、stdin経由で渡す必要がある。 # 最も簡単なのは、JSON文字列をそのまま渡すこと。 # clean-css-cliは --config オプションがないので、 # 各オプションを個別に渡すか、デフォルトを使う。 # ここでは、デフォルトの動作に任せるか、カスタムオプションを考慮せず、 # simpleに圧縮する。より高度なオプションが必要な場合は、スクリプト内でJSONをパースして引数に変換する必要がある。
# しかし、clean-css-cliは`--config`オプションがないため、オプションは個別に渡す必要があります。 # ここでは、`clean_css_options`をJSONとしてパースして引数に変換する例を示します。 # ただし、これを行うには`jq`などのツールが必要になるため、ワークフローの複雑さを避けるため、デフォルトではシンプルな圧縮のみを行うか、特定のよく使うオプションのみをinputで受け取る形にするのが現実的です。
# 今回は`clean-css-cli`のデフォルト挙動(強力な圧縮)に任せるか、よく使うオプション(例: `--level`)を直接inputで受け取る形にします。 # `clean_css_options` は、ここでは直接利用せず、将来的な拡張ポイントとして残します。
echo "Warning: clean_css_options input is currently ignored due to clean-css-cli limitations for direct JSON config. Using default clean-css behavior or specified output_suffix." fi
cleancss -o "$output_file" "$file" # 圧縮を実行
optimized_size=$(stat -c %s "$output_file") # 圧縮後のファイルのサイズを取得
if [ "$original_size" -ne "$optimized_size" ]; then echo "Minified: $file to $output_file (Original: $original_size bytes, Optimized: $optimized_size bytes)" minified_count=$((minified_count + 1)) else echo "Skipped (no change): $file" fi fi done
echo "minified_count=$minified_count" >> $GITHUB_OUTPUT shell: bashWorkflowへの組み込み方
これらのアクションをワークフローに組み込むには、.github/workflows/main.yml ファイルに以下のステップを追加します。
- name: Optimize Images uses: ./.github/actions/optimize-image with: jpeg_quality: 70
- name: Optimize JavaScript uses: ./.github/actions/optimize-js
- name: Optimize CSS uses: ./.github/actions/optimize-cssこれらのステップは、deploy ステップの前に追加することを推奨します。
ワークフローファイルの全体像
ワークフローファイルは、以下のようになります。
name: CI/CD
on: push: branches: - main
jobs: deploy: runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up SSH key with: ssh-private-key: ${{ secrets.SSH_PRIVATE_KEY }}
- name: Optimize Images uses: ./.github/actions/optimize-image with: jpeg_quality: 70
- name: Optimize JavaScript uses: ./.github/actions/optimize-js
- name: Optimize CSS uses: ./.github/actions/optimize-css
- name: Deploy run: | # デプロイ処理 echo "Deploying..."結果
これらの最適化を行うことで、サイトを表示した際のアセットの読み込み時間を短縮できるようになりました。
具体的には、アセットの合計サイズを 11.0MB から 7.7MB に削減できました。
本来であれば、webp などのより高度な画像フォーマットを使用したいところですが、今回はオリジナルファイルを変更せずに、裏側で最適化できる範囲に留めました。
実行時間
画像の最適化に少し時間がかかります。1 分程度です。

画像の圧縮ツールのインストールにネットワーク通信が必要になるため結構時間を消費しているようです。 可能なら画像は元のデータを圧縮してしまうのがおすすめです。Git 管理しているのでいつでももとに戻せるので。
今後の展望
- webp などのより高度な画像フォーマットの導入
- JavaScript と CSS の更なる minify
- CDN の導入