問題の状況
モノレポ環境でCI/CDパイプラインを運用していると、予想外のエラーに遭遇することがあります。私たちのチームでも、GitHub ActionsのファイルチェックワークフローでArgument list too longというエラーが発生しました。
このワークフローは、PRで変更されたファイルが該当サービスブランチで修正可能なファイルかどうかを検証する役割を持っています。例えば、service-aブランチではserviceAディレクトリのファイルのみ修正できるように制限するものです。
既存の実装はtj-actions/changed-filesで変更ファイル一覧を取得した後、Bashのforループで環境変数に渡されたファイルリストを巡回する方式でした。
for file in $ALL_CHANGED_FILES; do
service_type=$(echo "$file" | cut -d'/' -f2)
if [ "$service_type" != "$service_directory" ]; then
echo "The file $file is not allowed to be changed in the $SERVICE_NAME branch."
exit 1
fi
done
問題は変更ファイル数が多い場合に発生します。$ALL_CHANGED_FILES環境変数に格納されたファイルパスがOSの引数長制限(Linuxの場合、デフォルトのARG_MAX = 約2MB)を超えるとArgument list too longエラーが発生します。モノレポの規模が大きくなるほど、1つのPRで数百、数千のファイルが変更されるケースが増え、この制限に引っかかりやすくなります。
解決方法
解決のポイントは2つです。
1. 変更ファイル一覧をファイルに保存
tj-actions/changed-filesのwrite_output_filesオプションを活用して、変更ファイル一覧を環境変数の代わりにファイルに保存するように変更しました。
- name: Get changed files
id: changed-files
uses: tj-actions/[email protected]
with:
files: |
**
write_output_files: true
output_dir: /tmp
これにより/tmp/all_changed_files.txtに変更ファイル一覧が記録されます。ファイルシステムを通じてデータをやり取りするため、OSの引数長制限の影響を受けません。
2. BashからJavaScriptへの切り替え
ファイルを読み込んで処理するロジックを、Bashの代わりにactions/github-scriptを使用したJavaScriptで書き直しました。
- name: Validate file changes
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const serviceDirectoryMap = {
'service-a': 'serviceA',
'service-b': 'serviceB',
'service-c': 'serviceC',
// ...
};
const serviceName = process.env.SERVICE_NAME;
const serviceDirectory = serviceDirectoryMap[serviceName];
// ...
const fileContent = fs.readFileSync('/tmp/all_changed_files.txt', 'utf8');
const files = fileContent.trim().split(/\s+/).filter(file => file.trim() !== '');
for (const filename of files) {
// 許可ファイルチェックとサービスディレクトリ検証
}
この切り替えで得られたメリットは以下の通りです。
| 項目 | Bash(変更前) | JavaScript(変更後) |
|---|---|---|
| ファイル一覧の受け渡し | 環境変数(長さ制限あり) | ファイルI/O(制限なし) |
| サービスマッピング | case文 | オブジェクトリテラル(Map) |
| エラーハンドリング | exit 1 | core.setFailed() |
| 可読性 | Shell特有の構文 | 直感的なJavaScript |
重要なポイント
環境変数で大量のデータを渡さないでください。 これはGitHub Actionsに限った問題ではなく、Unix/Linuxシステム全般に該当する制約です。シェルコマンドの引数長にはOSレベルの制限があり、CI環境で変更ファイル数が予測不可能な場合は、必ずファイルベースの受け渡し方式を使用すべきです。
また、actions/github-scriptはGitHub Actionsで複雑なロジックを処理する際のBashの良い代替手段です。Node.jsランタイム上で動作するため、fsやpathなどの標準モジュールを自由に使用でき、core.setFailed()のようなActions専用APIもすぐに活用できます。
まとめ
小さな修正ですが、CIパイプラインの安定性を大きく向上させた変更でした。モノレポの規模が大きくなるほど、このようなシステムレベルの制約に遭遇する可能性が高くなるため、「今は動いているから大丈夫」ではなく、スケーラビリティを考慮した設計が重要だということを改めて感じました。
ファイルチェックワークフローの全体構成について詳しく知りたい方は、モノレポでブランチごとにファイル変更範囲を制限するGitHub Actionsワークフローをご参照ください。
私のブログが役に立ちましたか?下にコメントを残してください。それは私にとって大きな大きな力になります!
アプリ広報
Dekuが開発したアプリを使ってみてください。Dekuが開発したアプリはFlutterで開発されています。興味がある方はアプリをダウンロードしてアプリを使ってくれると本当に助かります。