direnv 入門 - コミットせずに手元だけポート番号を変える

direnv 入門 - コミットせずに手元だけポート番号を変える

作成日:
読了:約9分
更新日:
この記事を読む人におすすめPR / Amazonアソシエイト

当サイトは Amazon.co.jp を宣伝しリンクすることで紹介料を得る手段を提供する、Amazonアソシエイト・プログラムの参加者です。価格・在庫はリンク先の最新情報をご確認ください。

別のプロジェクトがすでに3000番ポートで動いていて、いま触っているリポジトリの開発サーバーを3001番で起動したい。そういう場面がよくあります。package.jsonのdevスクリプトを書き換えれば済みますが、それは自分の環境だけの都合なので、コミットに混ぜたくありません。差分に残ったままだと、別の作業のコミットにうっかり含めてしまう危険もあります。

この「コミットはしたくないが、手元だけ少し設定を変えたい」に、direnvがよく合いました。この記事では、ポート番号を手元だけ変える例を軸に、direnvの基本的な使い方をまとめます。

direnvとは

direnvは、カレントディレクトリに応じて環境変数を読み込んだり外したりするシェル拡張です。公式サイトの説明では、プロンプトを表示する直前に、現在のディレクトリと親ディレクトリに.envrcがあるかを確認します。見つかった.envrcはbashのコードとしてサブシェルで実行され、そこでexportされた変数だけが今のシェルに反映されます。

対応シェルはbash、zsh、fish、tcsh、elvish、PowerShell、murex、Nushellです。~/.zshrcに案件ごとの変数を書き足していく運用から抜け出せるのが、一番の利点です。

インストールとシェルへの組み込み

macOSならHomebrewで入ります。

インストール
brew install direnv
direnv version
# 2.37.1

入れただけでは動きません。シェルに「プロンプトの前にdirenvを呼ぶ」フックを仕込む必要があります。zshの場合は、公式ドキュメントのとおり~/.zshrcの末尾に1行足します。

~/.zshrc
eval "$(direnv hook zsh)"

シェルを開き直せば準備完了です。Oh My Zshを使っている場合は、direnv用のプラグインも用意されています。

3000番ポートを手元だけ3001番にする

開発サーバーを起動するリポジトリの直下に、.envrcを作ります。

.envrc
export PORT=3001

作った直後は、まだ読み込まれません。direnvは知らない.envrcを勝手に実行しないので、次のエラーが出ます。

承認前
direnv: error /path/to/project/.envrc is blocked. Run `direnv allow` to approve its content

中身を確認したうえで承認します。

承認
direnv allow .
承認後の出力
direnv: loading /path/to/project/.envrc
direnv: export +PORT

export +PORTは「PORTが追加された」という意味です。これで、このディレクトリにいる間はPORT=3001が効いた状態になり、npm run devをそのまま叩けば3001番で起動します。package.jsonには一切手を入れていません。

Next.jsでは.envにPORTを書いても効かない

「それなら.env.localにPORT=3001と書けばいいのでは」と思うかもしれませんが、Next.jsではうまくいきません。公式ドキュメントのCLIリファレンスに、次の注記があります。

PORT cannot be set in .env as booting up the HTTP server happens before any other code is initialized.

HTTPサーバーの起動が.envの読み込みより先に走るため、PORTだけは.envから設定できない、という説明です。next devとnext startのポートは、-pオプションか、起動時点で環境変数PORTに入っている値で決まります。シェルの環境変数そのものを差し替えるdirenvは、この制約にちょうど合います。

注意点が1つあります。package.jsonにnext dev -p 9001のようにポートが直書きされていると、そちらが使われます。手元のNext.js 16.1.6で、PORT=9555を渡しつつ-p 9666を指定して起動したところ、Local: http://localhost:9666となりました。スクリプトに-pが無いリポジトリなら、direnvのPORTがそのまま効きます。

.envrcをコミット対象から外す

.envrcも、放っておけばgit statusに未追跡ファイルとして出てきます。ここで.gitignoreに追記すると、今度は.gitignore自体の変更がコミット対象になってしまいます。

自分の手元だけで無視したい場合は、.git/info/excludeを使います。Gitのドキュメントでは、リポジトリ固有だが他の人と共有する必要のないパターンを書く場所として紹介されています。書式は.gitignoreと同じで、.gitの中にあるのでコミットされません。

手元だけで無視する
echo '.envrc' >> .git/info/exclude
git status --short --ignored
出力
!! .envrc

!!は「無視されているファイル」を表します。通常のgit statusにはもう出てこないので、git add -Aで巻き込む心配もなくなります。リポジトリをcloneし直すと.git/info/excludeも消える点だけ覚えておいてください。

書き換えると再承認が必要になる

.envrcを書き換えると、direnvは再びブロックします。試しにPORT=3002に変えると、承認前と同じis blockedのエラーに戻りました。内容が変わったファイルを黙って実行しない作りです。

毎回direnv allowを打つのが面倒なら、direnv edit .を使います。$EDITORで.envrcを開き、閉じたあとにそのまま承認まで済ませてくれます。

ディレクトリから出ると、読み込んだ変数は外れて元の状態に戻ります。別の案件のディレクトリに移ったらPORTが3001のまま残っていた、ということは起きません。

チームで.envrcを共有している場合

リポジトリによっては、.envrc自体がコミット済みのこともあります。その場合は共有の.envrcに次の1行を入れてもらい、個人用の上書きは.envrc.localに書く形が使えます。

.envrc(共有)
export DATABASE_URL=postgres://localhost:5432/app_dev
source_env_if_exists .envrc.local
.envrc.local(個人用・コミットしない)
export PORT=3001

source_env_if_existsは、指定したファイルがあれば読み込み、無ければ何もしないdirenvの標準関数です。.envrc.localは先ほどと同じく.gitignoreか.git/info/excludeで無視しておきます。

よく使う標準関数

.envrcの中では、direnvが用意している標準関数(stdlib)を呼べます。公式のマニュアルから、使い道の多いものを挙げます。

関数役割
dotenv.envを読み込む。dotenv_if_existsは無ければ何もしない
source_env_if_exists別の.envrcを、存在する場合だけ読み込む
source_up親ディレクトリをさかのぼって.envrcを探して読み込む
PATH_add bin指定したディレクトリをPATHの先頭に足す
layout node./node_modules/.binをPATHに足す
watch_file指定したファイルが変わったら再読み込みする
env_vars_required必要な変数が空ならエラーを出す

たとえばlayout nodeを書いておくと、npxを付けずにnextやeslintをそのまま実行できます。

また、~/.config/direnv/direnv.tomlにload_dotenv = trueを設定すると、.envrcに加えて.envも自動で読み込むようになります。

使うときの注意

  • .envrcはbashのコードとして実行されます。cloneしたばかりの他人のリポジトリで、中身を見ずにdirenv allowしないでください。承認の仕組みはこのためにあります。
  • 環境変数は暗号化されません。APIキーなどを.envrcに書く場合も、扱いは.envと同じです。詳しくは環境変数と.envによる設定と秘密情報の管理で書きました。
  • 読み込みのきっかけはシェルのプロンプトです。ターミナルを経由しないプロセスには効かないので、そうした場面ではdirenv exec <ディレクトリ> <コマンド>で明示的に読み込んで実行します。

手元だけの小さな設定変更は、これまでなら「変更したけど戻し忘れる」「コミットから外し忘れる」の二択になりがちでした。.envrcと.git/info/excludeの組み合わせなら、リポジトリには何も残さずに済みます。.gitignoreや除外の仕組みについてはGit 基本コマンドとエイリアスもあわせてどうぞ。

参考

Git 3.0 で何が変わるのか - SHA-256・reftable・main 既定化と、備えるための自作ツール git3ready

Git 3.0 で何が変わるのか - SHA-256・reftable・main 既定化と、備えるための自作ツール git3ready

約25分

Git の次の破壊的リリース Git 3.0 で予定されている変更を、公式の Documentation/BreakingChanges.adoc をもとに整理します。新規リポジトリの既定が SHA-256・reftable・main になり、safe.bareRepository が explicit に、git whatchanged などが削除され、ビルドに Rust が必須になる予定です。壊れるのは Git 本体ではなく周辺のスクリプト・フック・CI であることを示し、その箇所を洗い出して新しい既定値でテストを試走できる自作 CLI「git3ready」を紹介します。