Skip to content

Commit 60273ca

Browse files
alunduilasottile
authored andcommitted
Add haskell language support to pre-commit.
1 parent f8488e3 commit 60273ca

File tree

4 files changed

+110
-0
lines changed

4 files changed

+110
-0
lines changed

.github/workflows/languages.yaml

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -63,6 +63,8 @@ jobs:
6363
echo 'C:\Strawberry\c\bin' >> "$GITHUB_PATH"
6464
shell: bash
6565
if: matrix.os == 'windows-latest' && matrix.language == 'perl'
66+
- uses: haskell/actions/setup@v2
67+
if: matrix.language == 'haskell'
6668

6769
- name: install deps
6870
run: python -mpip install -e . -r requirements-dev.txt

pre_commit/all_languages.py

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -9,6 +9,7 @@
99
from pre_commit.languages import dotnet
1010
from pre_commit.languages import fail
1111
from pre_commit.languages import golang
12+
from pre_commit.languages import haskell
1213
from pre_commit.languages import lua
1314
from pre_commit.languages import node
1415
from pre_commit.languages import perl
@@ -31,6 +32,7 @@
3132
'dotnet': dotnet,
3233
'fail': fail,
3334
'golang': golang,
35+
'haskell': haskell,
3436
'lua': lua,
3537
'node': node,
3638
'perl': perl,

pre_commit/languages/haskell.py

Lines changed: 56 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,56 @@
1+
from __future__ import annotations
2+
3+
import contextlib
4+
import os.path
5+
from typing import Generator
6+
from typing import Sequence
7+
8+
from pre_commit import lang_base
9+
from pre_commit.envcontext import envcontext
10+
from pre_commit.envcontext import PatchesT
11+
from pre_commit.envcontext import Var
12+
from pre_commit.errors import FatalError
13+
from pre_commit.prefix import Prefix
14+
15+
ENVIRONMENT_DIR = 'hs_env'
16+
get_default_version = lang_base.basic_get_default_version
17+
health_check = lang_base.basic_health_check
18+
run_hook = lang_base.basic_run_hook
19+
20+
21+
def get_env_patch(target_dir: str) -> PatchesT:
22+
bin_path = os.path.join(target_dir, 'bin')
23+
return (('PATH', (bin_path, os.pathsep, Var('PATH'))),)
24+
25+
26+
@contextlib.contextmanager
27+
def in_env(prefix: Prefix, version: str) -> Generator[None, None, None]:
28+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
29+
with envcontext(get_env_patch(envdir)):
30+
yield
31+
32+
33+
def install_environment(
34+
prefix: Prefix,
35+
version: str,
36+
additional_dependencies: Sequence[str],
37+
) -> None:
38+
lang_base.assert_version_default('haskell', version)
39+
envdir = lang_base.environment_dir(prefix, ENVIRONMENT_DIR, version)
40+
41+
pkgs = [*prefix.star('.cabal'), *additional_dependencies]
42+
if not pkgs:
43+
raise FatalError('Expected .cabal files or additional_dependencies')
44+
45+
bindir = os.path.join(envdir, 'bin')
46+
os.makedirs(bindir, exist_ok=True)
47+
lang_base.setup_cmd(prefix, ('cabal', 'update'))
48+
lang_base.setup_cmd(
49+
prefix,
50+
(
51+
'cabal', 'install',
52+
'--install-method', 'copy',
53+
'--installdir', bindir,
54+
*pkgs,
55+
),
56+
)

tests/languages/haskell_test.py

Lines changed: 50 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,50 @@
1+
from __future__ import annotations
2+
3+
import pytest
4+
5+
from pre_commit.errors import FatalError
6+
from pre_commit.languages import haskell
7+
from pre_commit.util import win_exe
8+
from testing.language_helpers import run_language
9+
10+
11+
def test_run_example_executable(tmp_path):
12+
example_cabal = '''\
13+
cabal-version: 2.4
14+
name: example
15+
version: 0.1.0.0
16+
17+
executable example
18+
main-is: Main.hs
19+
20+
build-depends: base >=4
21+
default-language: Haskell2010
22+
'''
23+
main_hs = '''\
24+
module Main where
25+
26+
main :: IO ()
27+
main = putStrLn "Hello, Haskell!"
28+
'''
29+
tmp_path.joinpath('example.cabal').write_text(example_cabal)
30+
tmp_path.joinpath('Main.hs').write_text(main_hs)
31+
32+
result = run_language(tmp_path, haskell, 'example')
33+
assert result == (0, b'Hello, Haskell!\n')
34+
35+
# should not symlink things into environments
36+
exe = tmp_path.joinpath(win_exe('hs_env-default/bin/example'))
37+
assert exe.is_file()
38+
assert not exe.is_symlink()
39+
40+
41+
def test_run_dep(tmp_path):
42+
result = run_language(tmp_path, haskell, 'hello', deps=['hello'])
43+
assert result == (0, b'Hello, World!\n')
44+
45+
46+
def test_run_empty(tmp_path):
47+
with pytest.raises(FatalError) as excinfo:
48+
run_language(tmp_path, haskell, 'example')
49+
msg, = excinfo.value.args
50+
assert msg == 'Expected .cabal files or additional_dependencies'

0 commit comments

Comments
 (0)