※ 技術ウミガメのスープ みたいな話ではありません。
はじめに
改行コード、意識してますか?
コードエディタ上に表示されることが多いので、エンジニアの皆さんは普段から目にされているかもしれません。
ただ、なんとなく見過ごしていると思いがけず足をすくわれることも多いのが、改行コードの厄介なところです。
今回は、改行コードが原因でつまづいた話を共有します。
あらすじ
特定のS3バケットにアップロードされているログを取得し、別のドライブにアップロードするシステムを改修していました。
CodeBuildで動いていて、デプロイ時にはソースコードと周辺ファイルをzip化してS3にアップロードする仕組みです。
本番環境でだけなぜか動かない
テスト用アカウントで試行した時は正常に動いていたのに、本番環境用では失敗します。
本番用の変更がマージされる→GitHubからファイルをダウンロード→zip化してS3アップロード→CodeBuild実行してみる→失敗
仕方がないので、本番環境でその時動いているコードのパッケージをダウンロードし、修正点だけ変えてデプロイする作業が毎回必要になっていました。
ログから推理する
COMMAND_EXECUTION_ERROR Message: Error while executing command: ./LogUploadSystem/aws/codebuild/script/run_script.sh. Reason: exit status 127
どうやら run_script.sh の実行がうまくいっていないようですね。
exit status 127 は、一般的に「コマンドが見つからない」「パスが通らない」「スクリプトに問題がある」といった状態の時に返ってくるそうです。テスト環境と同じに見えるんだけどなぁ。
原因
知見が足りず見当がつかなかったため、GitHubCopilotにログを見せて、聞いてみました。
すると、「スクリプトの改行コードがCRLFになっている可能性があります」といった趣旨の返答が返ってきました。
実際に確認すると、run_script.sh の改行コードは本当にCRLFになっていました。
なぜCRLFじゃダメなのか
ズバリ、
CodeBuildのランタイム(ビルド環境)は、基本的にAmazon LinuxなどのLinuxベースだから
です。AWS公式ドキュメント
Windows環境で作業したものをCodeBuild上で動かす際には、気を付けないとここで失敗する可能性があるのですね。
なぜ本番環境だけ?
一見謎ですが、整理するとこういうことでした。
テスト環境:
- 以前から LF で作成されていた
run_script.shを S3 から直接ダウンロード - そのファイルを使い回していたため、常に LF のまま
本番環境:
- ローカルの Windows エディタで編集 → 改行コードが CRLF に変更
- そのファイルが master にマージされる
- さらに Git からではなく、ローカルのファイルを直接 zip 化してデプロイフローに乗せてしまった
この結果、「テストでは LF で動くのに、本番だけ CRLF でコケる」という状態になっていました。
結果的に、本番用コードはmasterと差異が出ないように...!としっかりダウンロードしていたのが仇となっていたわけですね。
解決策 .gitattributes
とりあえずLFに変更したとしても、またいつかうっかりミスでCRLFに戻ってしまうかもしれません。
そのため恒久対策として、.gitattributes というファイルをルートディレクトリに作成し、以下のように記述します。
LogUploadSystem/aws/codebuild/script/run_script.sh text eol=lf
これにより、どのOSで作業していても、Gitにコミット・チェックアウトされる際に自動的にLFに統一されるようになります。
もしくは、以下のように記述することで、全てのシェルファイルを強制的にLFとして扱うこともできます。
*.sh text eol=lf
改行コードを知るためのtips
cat -e run_script.sh と叩くと、CRLFの場合末尾に ^M$ が見えるそうです。VS Codeなどで開けば下部に改行コードが表示されるので、わざわざこういったコマンドを実行する機会は少ないかもしれませんが、一括で確認したいときなどに使えそうです。
まとめ
今回の件で学んだのは、「自分の環境では動くからといって、本番環境でも動くとは限らない」ことです。
また、CopilotのようなAIは、こういった「一見正解に見えるコードの中に潜む、不可視な文字(改行コード)」の指摘が得意です。
AIを有効活用しつつも丸投げするのではなく、提示された仮説を元に原因を紐解いていくことで、経験を血肉としていけるよう取り組んでいきたいと思います。