Add initial implementation for running SBT commands.

This commit is contained in:
Stewart Hutchins 2022-11-23 18:13:43 +00:00
parent 81db32e148
commit 163e418754
10 changed files with 185 additions and 1 deletions

View file

@ -21,6 +21,7 @@ from pre_commit.languages import python
from pre_commit.languages import r from pre_commit.languages import r
from pre_commit.languages import ruby from pre_commit.languages import ruby
from pre_commit.languages import rust from pre_commit.languages import rust
from pre_commit.languages import sbt
from pre_commit.languages import script from pre_commit.languages import script
from pre_commit.languages import swift from pre_commit.languages import swift
from pre_commit.languages import system from pre_commit.languages import system
@ -60,6 +61,7 @@ languages = {
'r': Language(name='r', ENVIRONMENT_DIR=r.ENVIRONMENT_DIR, get_default_version=r.get_default_version, health_check=r.health_check, install_environment=r.install_environment, run_hook=r.run_hook), # noqa: E501 'r': Language(name='r', ENVIRONMENT_DIR=r.ENVIRONMENT_DIR, get_default_version=r.get_default_version, health_check=r.health_check, install_environment=r.install_environment, run_hook=r.run_hook), # noqa: E501
'ruby': Language(name='ruby', ENVIRONMENT_DIR=ruby.ENVIRONMENT_DIR, get_default_version=ruby.get_default_version, health_check=ruby.health_check, install_environment=ruby.install_environment, run_hook=ruby.run_hook), # noqa: E501 'ruby': Language(name='ruby', ENVIRONMENT_DIR=ruby.ENVIRONMENT_DIR, get_default_version=ruby.get_default_version, health_check=ruby.health_check, install_environment=ruby.install_environment, run_hook=ruby.run_hook), # noqa: E501
'rust': Language(name='rust', ENVIRONMENT_DIR=rust.ENVIRONMENT_DIR, get_default_version=rust.get_default_version, health_check=rust.health_check, install_environment=rust.install_environment, run_hook=rust.run_hook), # noqa: E501 'rust': Language(name='rust', ENVIRONMENT_DIR=rust.ENVIRONMENT_DIR, get_default_version=rust.get_default_version, health_check=rust.health_check, install_environment=rust.install_environment, run_hook=rust.run_hook), # noqa: E501
'sbt': Language(name='sbt', ENVIRONMENT_DIR=sbt.ENVIRONMENT_DIR, get_default_version=sbt.get_default_version, health_check=sbt.health_check, install_environment=sbt.install_environment, run_hook=sbt.run_hook), # noqa: E501
'script': Language(name='script', ENVIRONMENT_DIR=script.ENVIRONMENT_DIR, get_default_version=script.get_default_version, health_check=script.health_check, install_environment=script.install_environment, run_hook=script.run_hook), # noqa: E501 'script': Language(name='script', ENVIRONMENT_DIR=script.ENVIRONMENT_DIR, get_default_version=script.get_default_version, health_check=script.health_check, install_environment=script.install_environment, run_hook=script.run_hook), # noqa: E501
'swift': Language(name='swift', ENVIRONMENT_DIR=swift.ENVIRONMENT_DIR, get_default_version=swift.get_default_version, health_check=swift.health_check, install_environment=swift.install_environment, run_hook=swift.run_hook), # noqa: E501 'swift': Language(name='swift', ENVIRONMENT_DIR=swift.ENVIRONMENT_DIR, get_default_version=swift.get_default_version, health_check=swift.health_check, install_environment=swift.install_environment, run_hook=swift.run_hook), # noqa: E501
'system': Language(name='system', ENVIRONMENT_DIR=system.ENVIRONMENT_DIR, get_default_version=system.get_default_version, health_check=system.health_check, install_environment=system.install_environment, run_hook=system.run_hook), # noqa: E501 'system': Language(name='system', ENVIRONMENT_DIR=system.ENVIRONMENT_DIR, get_default_version=system.get_default_version, health_check=system.health_check, install_environment=system.install_environment, run_hook=system.run_hook), # noqa: E501

View file

@ -0,0 +1,44 @@
from __future__ import annotations
from typing import Sequence
from pre_commit.hook import Hook
from pre_commit.languages import helpers
ENVIRONMENT_DIR = None
install_environment = helpers.no_install
health_check = helpers.basic_health_check
get_default_version = helpers.basic_get_default_version
def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> tuple[int, bytes]:
# TODO: Improve impl to connect to run commands via SBT server
return run_sbt_hook_via_commandline(hook, file_args, color)
def run_sbt_hook_via_commandline(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> tuple[int, bytes]:
"""
Run an SBT hook, via the commandline. The command to be run is:
sbt ${entry} ${args} ${files}
The entry and args will not be quoted (so should be wrapped in quotes as
appropriate by the hook author),however files will be quoted, so any
filenames with spaces will be interpreted as a single argument by SBT
"""
entry_part = hook.entry
args_part = ' '.join(hook.args)
files_part = ' '.join(_quote(file) for file in file_args)
sbt_command = f'{entry_part} {args_part} {files_part}'
shell_cmd = ('sbt', sbt_command)
return helpers.run_xargs(hook, shell_cmd, [], color=color)
def _quote(s: str) -> str:
return f"\"{s}\""

View file

@ -6,7 +6,7 @@ import sys
LANGUAGES = ( LANGUAGES = (
'conda', 'coursier', 'dart', 'docker', 'docker_image', 'dotnet', 'fail', 'conda', 'coursier', 'dart', 'docker', 'docker_image', 'dotnet', 'fail',
'golang', 'lua', 'node', 'perl', 'pygrep', 'python', 'r', 'ruby', 'rust', 'golang', 'lua', 'node', 'perl', 'pygrep', 'python', 'r', 'ruby', 'rust',
'script', 'swift', 'system', 'sbt', 'script', 'swift', 'system',
) )
FIELDS = ( FIELDS = (
'ENVIRONMENT_DIR', 'get_default_version', 'health_check', 'ENVIRONMENT_DIR', 'get_default_version', 'health_check',

View file

@ -0,0 +1,6 @@
- id: sbt-create-files
name: touch
entry: touch
args: ["file1.txt", "\"file2 with space.txt\""]
description: "creates files provided by `args` or `files`"
language: sbt

View file

@ -0,0 +1,6 @@
import TouchCommand._
lazy val root = (project in file("."))
.settings(
commands ++= Seq(touchCommand)
)

View file

@ -0,0 +1,13 @@
import sbt.Command
import java.nio.file.{Files, Paths}
object TouchCommand {
def touchCommand = Command.args("touch", "args") { (state, args) =>
args.map(Paths.get(_).toAbsolutePath).foreach { path =>
println(f"Creating file: $path")
Files.createFile(path)
}
state
}
}

View file

@ -54,6 +54,10 @@ skipif_cant_run_lua = pytest.mark.skipif(
os.name == 'nt', os.name == 'nt',
reason="lua isn't installed or can't be found", reason="lua isn't installed or can't be found",
) )
skipif_cant_run_sbt = pytest.mark.skipif(
parse_shebang.find_executable('sbt') is None,
reason="SBT isn't installed or can't be found",
)
skipif_cant_run_swift = pytest.mark.skipif( skipif_cant_run_swift = pytest.mark.skipif(
parse_shebang.find_executable('swift') is None, parse_shebang.find_executable('swift') is None,
reason="swift isn't installed or can't be found", reason="swift isn't installed or can't be found",

View file

@ -4,6 +4,7 @@ import functools
import io import io
import logging import logging
import os.path import os.path
from pathlib import Path
from unittest import mock from unittest import mock
import pytest import pytest
@ -16,6 +17,7 @@ from pre_commit.util import cmd_output
from pre_commit.util import make_executable from pre_commit.util import make_executable
from testing.fixtures import git_dir from testing.fixtures import git_dir
from testing.fixtures import make_consuming_repo from testing.fixtures import make_consuming_repo
from testing.fixtures import make_repo
from testing.fixtures import write_config from testing.fixtures import write_config
from testing.util import cwd from testing.util import cwd
from testing.util import git_commit from testing.util import git_commit
@ -250,3 +252,9 @@ def set_git_templatedir(tmpdir_factory):
tdir = str(tmpdir_factory.mktemp('git_template_dir')) tdir = str(tmpdir_factory.mktemp('git_template_dir'))
with envcontext((('GIT_TEMPLATE_DIR', tdir),)): with envcontext((('GIT_TEMPLATE_DIR', tdir),)):
yield yield
@pytest.fixture
def sbt_project_with_touch_command(tempdir_factory):
project_repo = make_repo(tempdir_factory, 'sbt_repo_with_touch_command')
return Path(project_repo)

View file

@ -0,0 +1,63 @@
from __future__ import annotations
from itertools import product
from pathlib import Path
from typing import Any
import pytest
from pre_commit.hook import Hook
from pre_commit.languages import sbt
from testing.util import cwd
from testing.util import skipif_cant_run_sbt
@skipif_cant_run_sbt
@pytest.mark.parametrize(
['args', 'files'],
product(
[
[], ['argfile1.txt'], ['argfile1.txt', 'argfile2.txt'],
['\"arg file1.txt\"'], ['\"arg file1.txt\"', '\"arg file2.txt\"'],
],
[
[], ['filesfile1.txt'], ['filesfile1.txt', 'filesfile2.txt'],
['files file1.txt'], ['files file1.txt', 'files file2.txt'],
],
),
)
def test_sbt_hook(
sbt_project_with_touch_command: Path,
args: list[str],
files: list[str],
) -> None:
# arrange
project_root = sbt_project_with_touch_command
hook = _create_hook(
language='sbt',
entry='touch',
args=args,
)
# act
with cwd(project_root):
ret, out = sbt.run_hook(hook, files, False)
# assert
output = out.decode('UTF-8')
assert ret == 0
for file in args + files:
unquoted_file = _unquote(file)
expected_file = project_root.joinpath(unquoted_file).absolute()
assert expected_file.exists()
assert f'Creating file: {expected_file}' in output
def _unquote(s: str) -> str:
return s.strip("\"")
def _create_hook(**kwargs: Any) -> Hook:
default_values = {field: None for field in Hook._fields}
actual_values = {**default_values, **kwargs}
return Hook(**actual_values) # type: ignore

View file

@ -35,6 +35,7 @@ from testing.util import get_resource_path
from testing.util import skipif_cant_run_coursier from testing.util import skipif_cant_run_coursier
from testing.util import skipif_cant_run_docker from testing.util import skipif_cant_run_docker
from testing.util import skipif_cant_run_lua from testing.util import skipif_cant_run_lua
from testing.util import skipif_cant_run_sbt
from testing.util import skipif_cant_run_swift from testing.util import skipif_cant_run_swift
from testing.util import xfailif_windows from testing.util import xfailif_windows
@ -1150,3 +1151,40 @@ def test_local_lua_additional_dependencies(store):
ret, out = _hook_run(hook, (), color=False) ret, out = _hook_run(hook, (), color=False)
assert b'Luacheck' in out assert b'Luacheck' in out
assert ret == 0 assert ret == 0
@skipif_cant_run_sbt
def test_sbt_hook(
sbt_project_with_touch_command,
tempdir_factory,
store,
):
# arrange
project_root = sbt_project_with_touch_command
hooks_repo = make_repo(tempdir_factory, 'sbt_hooks_repo')
config = make_config_from_repo(hooks_repo)
hook = _get_hook(config, store, 'sbt-create-files')
# act
with cwd(project_root):
ret, out = _hook_run(
hook,
['file3.txt', 'file4 with space.txt'],
color=False,
)
# assert
output = out.decode('UTF-8')
assert ret == 0
file1 = project_root.joinpath('file1.txt').absolute()
assert file1.exists()
assert f'Creating file: {file1}' in output
file2 = project_root.joinpath('file2 with space.txt').absolute()
assert file2.exists()
assert f'Creating file: {file2}' in output
file3 = project_root.joinpath('file3.txt').absolute()
assert file3.exists()
assert f'Creating file: {file3}' in output
file4 = project_root.joinpath('file4 with space.txt').absolute()
assert file4.exists()
assert f'Creating file: {file4}' in output