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: bash
JavaScriptの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: bash
CSSの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: bash
Workflowへの組み込み方
これらのアクションをワークフローに組み込むには、.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
uses: webfactory/ssh-agent@v0.8.0
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の導入
Discussion