2007年に執筆した『Smarty動的Webサイト構築入門』のサンプルコードリポジトリでは、レガシーSmartyやPHPを現代の基準でどのようにリファクタリングしていけばよいのか、その具体的な実践例を示すためのメンテナンスをしています。
本書のサンプルコードは、どこかの本番環境へデプロイ・リリースされるものではありません。だからこそ、「レガシーなコードをどうモダンな開発プラクティスに乗せ換えていくか」という移行のプロセスやベストプラクティスを、ありのままに見せるための格好の実験場となっています。
レガシーなシステムを引き継いでしまったエンジニアが最も恐れるのは、「ちょっとした修正がどこに波及するか分からない」「手動での確認漏れで本番が壊れる」という恐怖です。この恐怖を払拭し、リファクタリングを加速させるために導入したのが、GitHub ActionsによるCI(継続的インテグレーション)環境です。
今回は、マルチ言語・多層防御を実現した実際の GitHub Actions(コンポジットアクション)の構成と、その設計の裏側をご紹介します。
1. 変更のあったファイルだけを狙い撃つ:dorny/paths-filter の活用
レガシーな大規模リポジトリでは、あらゆる変更に対してすべての静的解析やテストをフルで回していると、CIの実行時間が長くなり、開発のテンポが落ちてしまいます。
そこで、.github/actions/lint/action.yml の先頭では dorny/paths-filter を使用し、追加・変更されたファイルの拡張子やパスに応じて、必要なチェックだけを選択的に実行するようにしています。
# ========================================
# ファイル変更検出系
# ========================================
- name: Detect changed files
uses: dorny/paths-filter@v4
id: paths_filter
with:
list-files: shell
filters: |
sh:
- added|modified: '**/*.{sh,sh.tmpl}'
docker:
- added|modified: 'docker/**/Dockerfile'
js:
- added|modified: 'smartybook/**/*.{js,mjs,ts,tsx}'
php:
- added|modified: 'app/**/*.php'
- added|modified: 'bootstrap/**/*.php'
- added|modified: 'smartybook/**/*.php'
- added|modified: 'tests/**/*.php'
github_yaml:
- added|modified: '.github/**/*.{yml,yaml}'
prettier:
- added|modified: '**/*.{css,yml,yaml,md,json,jsonc}'
これにより、例えば PHP のファイルを1つ直したときに、無関係な JS や Docker の静的解析が走って待ち時間が増えるのを防いでいます。
2. 多言語・多層防御のリンター&静的解析スタック
レガシーコードの近代化では、コードの「見た目(フォーマット)」から「品質(静的解析)」まで、機械的に強制する仕組みが不可欠です。本リポジトリでは、以下のツール群を GitHub Actions 上で完全自動化しています。
シェル・コンテナ・ワークフロー系
- ShellCheck: シェルスクリプトのバグやアンチパターンを検出
- Hadolint: Dockerfile のベストプラクティス違反をチェック
- actionlint: GitHub Actions のワークフロー定義ミスの検出
JS / フロントエンド系
- Prettier (JS/CSS/YAML/MD/JSON 等): コードフォーマットの統一。なお、自動生成物である
pnpm-lock.yamlは整形規則が衝突しやすいため.prettierignoreで除外しています。 - ESLint: JavaScript のコード品質・作法チェック
- ts-check: TypeScript による型チェック(JSDocを通した緩やかな型安全性の担保も含む)
PHP / Smarty 系
- PHP 構文チェック (
php -l): 最速でパースエラーを弾く最初の砦 - php-cs-fixer: コーディング規約の自動チェック・強制
- SmartyLint: レガシーなテンプレート構文のエラーやミスを検知
- PHPStan: 型やメソッド呼び出しの静的解析(レガシーPHPの「存在しないメソッド呼び出し」などの致命傷をコンパイルタイムならぬCIタイムで防ぐ)
3. 実際のコンポジットアクション定義
これらの一連のチェックをまとめた .github/actions/lint/action.yml の全体像は以下の通りです。
name: lint
description: Lint files (format, lint, static analysis)
runs:
using: composite
steps:
- name: Detect changed files
uses: dorny/paths-filter@v4
id: paths_filter
with:
list-files: shell
filters: |
sh:
- added|modified: '**/*.{sh,sh.tmpl}'
docker:
- added|modified: 'docker/**/Dockerfile'
js:
- added|modified: 'smartybook/**/*.{js,mjs,ts,tsx}'
php:
- added|modified: 'app/**/*.php'
- added|modified: 'bootstrap/**/*.php'
- added|modified: 'smartybook/**/*.php'
- added|modified: 'tests/**/*.php'
github_yaml:
- added|modified: '.github/**/*.{yml,yaml}'
prettier:
- added|modified: '**/*.{css,yml,yaml,md,json,jsonc}'
- name: Run ShellCheck
if: steps.paths_filter.outputs.sh == 'true'
shell: bash
run: shellcheck ${{ steps.paths_filter.outputs.sh_files }}
- name: Run Hadolint
if: steps.paths_filter.outputs.docker == 'true'
uses: hadolint/hadolint-action@v3.5.0
with:
dockerfile: ${{ steps.paths_filter.outputs.docker_files }}
- name: Check format (Prettier)
if: steps.paths_filter.outputs.prettier == 'true'
shell: bash
run: pnpm run format:check ${{ steps.paths_filter.outputs.prettier_files }}
- name: Run actionlint
if: steps.paths_filter.outputs.github_yaml == 'true'
uses: reviewdog/action-actionlint@v1
with:
fail_level: warning
reporter: github-pr-annotations
- name: JS Check formatting
if: ${{ steps.paths_filter.outputs.js == 'true' }}
shell: bash
run: pnpm run format:check ${{ steps.paths_filter.outputs.js_files }}
- name: ESLint
if: ${{ steps.paths_filter.outputs.js == 'true' }}
shell: bash
run: pnpm run lint ${{ steps.paths_filter.outputs.js_files }}
- name: ts-check
if: ${{ steps.paths_filter.outputs.js == 'true' }}
shell: bash
run: pnpm run ts-check
- name: PHP syntax check
shell: bash
if: ${{ steps.paths_filter.outputs.php == 'true' }}
run: php -l ${{ steps.paths_filter.outputs.php_files }}
- name: Run php-cs-fixer
if: ${{ steps.paths_filter.outputs.php == 'true' }}
shell: bash
run: composer run-script format:check ${{ steps.paths_filter.outputs.php_files }}
- name: Run smarty-lint
shell: bash
run: composer run-script smarty-lint templates
- name: phpstan
shell: bash
run: composer run-script lint
4. 常に走るテスト系と自動更新(Renovate等)の連携
上記の静検・Lintに加え、テスト系として以下が常に実行されます。
- Pest 単体ユニットテスト
- Docker環境を構築しての Golden Master テスト(※これについては別記事で詳しく解説予定です)
このCI基盤があるおかげで、RenovateやDependabotによるライブラリの自動更新PRであっても、テストと静的解析をすべてパスすれば人間がレビューしなくても安心して自動マージするといった運用が可能になります。
まとめ:めったに触らないシステムだからこそ、AIの力を借りてCI/CDを組もう
こうしたレガシーシステムは「めったに触らない(だからこそ手順や知見がチームにたまらない)」というケースがほとんどです。 たまのメンテナンスでデプロイや環境構築をしようとして、古い手順書と格闘した経験がある方も多いと思います。
- めったに触らないからこそ、ビルドやテスト、静的解析の手順はすべて GitHub Actions などの CI/CD にコードとして閉じ込め、人間が忘れても機械が覚えていてくれる状態にする。
- 知見がたまりにくいなら、AIにしつこいくらい「一般的な方法」「標準的な書き方」を質問しながら、一歩ずつ CI/CD の設定ファイルを組み立てていく。
CI/CD の構築や、各種リンター・テストの導入は、かんたんではありませんが、AIの力を借りながら、CI/CDを構築してみましょう。

