0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

VS Code の GUI commit とターミナル commit で pre-commit の実行環境が違った話

0
Posted at

VS Code の terminal からではなくGUI上で commit すると conda 環境の pre-commit が失敗した話

はじめに

Python プロジェクトで pre-commit を使い、commit 時に linter を実行する設定を入れていました。

普段は VS Code の統合ターミナル上で conda 環境を有効化して開発しており、必要なライブラリもその環境内にインストール済みでした。

そのため、VS Code 左側の GUI上(以降Source Controlとする) から commit しても同じように動くと思っていました。

しかし、ターミナルから git commit した場合は成功するのに、VS Code の Source Control から commit した場合だけ pre-commit が失敗しました。

発生した事象

conda 環境を有効化した状態で開発していました。

conda activate sample-env

その環境内に、linter が依存するライブラリをインストールしていました。

pip install xxx

この状態で、ターミナルから commit すると問題なく成功します。

git commit

一方で、VS Code の Source Control から commit すると、pre-commit の hook 内で linter が失敗しました。

ModuleNotFoundError: No module named 'xxx'

つまり、同じリポジトリ・同じ設定にもかかわらず、commit の実行経路によって結果が変わる状態でした。

前提

今回の pre-commit 設定は、概ね以下のような構成でした。

repos:
  - repo: local
    hooks:
      - id: lint
        name: lint
        entry: python -m flake8
        language: system
        types: [python]

ポイントは language: system を使っている点です。

language: system の場合、pre-commit は hook 用の独立した仮想環境を作らず、実行時の環境にあるコマンドを利用します。

そのため、entry に書いた python がどの Python を指すかは、実行時の PATH に依存します。

原因

原因は、VS Code の Source Control から実行される Git 操作が、統合ターミナルで activate している conda 環境と同じ実行環境で動くとは限らないことでした。

ターミナルで以下を実行すると、

conda activate sample-env

そのターミナルセッションでは conda 環境側の Python が優先されます。

そのため、ターミナルから git commit した場合、hook 内の python -m flake8 も conda 環境の Python で実行され、必要なライブラリを参照できました。

一方で、VS Code の Source Control から commit した場合、Git は VS Code 側から実行されます。

このとき、統合ターミナルで有効化した conda 環境の状態が、そのまま Git hook の実行環境に反映されるとは限りません。

結果として、hook 内の python が conda 環境ではない Python を指し、conda 環境内に存在するライブラリを見つけられず失敗しました。

確認方法

この手の問題では、hook 内で実際にどの Python が使われているかを確認すると切り分けしやすいです。

一時的に以下のような hook を追加します。

repos:
  - repo: local
    hooks:
      - id: debug-python
        name: debug python
        entry: python -c "import sys; print(sys.executable); print(sys.path)"
        language: system
        pass_filenames: false

そのうえで、以下を比較します。

  • ターミナルから git commit
  • VS Code の Source Control から commit

出力される sys.executable が異なる場合、commit の実行経路によって参照している Python が違うと判断できます。

対処法

conda 環境を明示して実行する

conda run を使うことで、hook 内で使用する conda 環境を明示できます。

repos:
  - repo: local
    hooks:
      - id: lint
        name: lint
        entry: conda run -n sample-env python -m flake8
        language: system
        types: [python]

これにより、PATH の状態に依存せず、指定した conda 環境で linter を実行できます。

ただし、開発者ごとに conda 環境名が異なる場合は、この方法も環境依存になります。

pre-commit 側で環境を管理する

より再現性を重視するなら、language: system に依存せず、pre-commit 側に hook 環境を管理させる方法があります。

例えば flake8 であれば、以下のように設定できます。

repos:
  - repo: https://github.com/pycqa/flake8
    rev: 7.1.1
    hooks:
      - id: flake8

追加の plugin が必要な場合は、additional_dependencies に定義できます。

repos:
  - repo: https://github.com/pycqa/flake8
    rev: 7.1.1
    hooks:
      - id: flake8
        additional_dependencies:
          - flake8-bugbear

この構成であれば、現在 activate している conda 環境への依存を減らせます。

まとめ

今回の問題は、VS Code の Source Control が悪いというより、pre-commit の設定が実行時環境に依存していたことが原因でした。

特に、以下の条件が揃うと発生しやすいです。

  • conda 環境を使っている
  • pre-commit で linter を実行している
  • language: system を使っている
  • entrypython など PATH 依存のコマンドを書いている
  • ターミナルからの git commit は成功する
  • VS Code の Source Control からの commit だけ失敗する

VS Code の統合ターミナルで conda 環境を有効化していても、Source Control から実行される Git hook が同じ環境で動くとは限りません。

pre-commit を使う場合は、「自分のターミナルで動くか」だけでなく、「どの経路で commit しても同じように動くか」まで確認した方が安全だと感じました。

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?