Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add Lua language support #2158

Merged
merged 2 commits into from Jan 17, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Jump to
Jump to file
Failed to load files.
Diff view
Diff view
4 changes: 4 additions & 0 deletions azure-pipelines.yml
Expand Up @@ -42,6 +42,8 @@ jobs:
displayName: install coursier
- bash: testing/get-dart.sh
displayName: install dart
- bash: testing/get-lua.sh
displayName: install lua
- bash: testing/get-swift.sh
displayName: install swift
- bash: testing/get-r.sh
Expand All @@ -56,6 +58,8 @@ jobs:
displayName: install coursier
- bash: testing/get-dart.sh
displayName: install dart
- bash: testing/get-lua.sh
displayName: install lua
- bash: testing/get-swift.sh
displayName: install swift
- bash: testing/get-r.sh
Expand Down
2 changes: 2 additions & 0 deletions pre_commit/languages/all.py
Expand Up @@ -13,6 +13,7 @@
from pre_commit.languages import dotnet
from pre_commit.languages import fail
from pre_commit.languages import golang
from pre_commit.languages import lua
from pre_commit.languages import node
from pre_commit.languages import perl
from pre_commit.languages import pygrep
Expand Down Expand Up @@ -51,6 +52,7 @@ class Language(NamedTuple):
'dotnet': Language(name='dotnet', ENVIRONMENT_DIR=dotnet.ENVIRONMENT_DIR, get_default_version=dotnet.get_default_version, healthy=dotnet.healthy, install_environment=dotnet.install_environment, run_hook=dotnet.run_hook), # noqa: E501
'fail': Language(name='fail', ENVIRONMENT_DIR=fail.ENVIRONMENT_DIR, get_default_version=fail.get_default_version, healthy=fail.healthy, install_environment=fail.install_environment, run_hook=fail.run_hook), # noqa: E501
'golang': Language(name='golang', ENVIRONMENT_DIR=golang.ENVIRONMENT_DIR, get_default_version=golang.get_default_version, healthy=golang.healthy, install_environment=golang.install_environment, run_hook=golang.run_hook), # noqa: E501
'lua': Language(name='lua', ENVIRONMENT_DIR=lua.ENVIRONMENT_DIR, get_default_version=lua.get_default_version, healthy=lua.healthy, install_environment=lua.install_environment, run_hook=lua.run_hook), # noqa: E501
'node': Language(name='node', ENVIRONMENT_DIR=node.ENVIRONMENT_DIR, get_default_version=node.get_default_version, healthy=node.healthy, install_environment=node.install_environment, run_hook=node.run_hook), # noqa: E501
'perl': Language(name='perl', ENVIRONMENT_DIR=perl.ENVIRONMENT_DIR, get_default_version=perl.get_default_version, healthy=perl.healthy, install_environment=perl.install_environment, run_hook=perl.run_hook), # noqa: E501
'pygrep': Language(name='pygrep', ENVIRONMENT_DIR=pygrep.ENVIRONMENT_DIR, get_default_version=pygrep.get_default_version, healthy=pygrep.healthy, install_environment=pygrep.install_environment, run_hook=pygrep.run_hook), # noqa: E501
Expand Down
90 changes: 90 additions & 0 deletions pre_commit/languages/lua.py
@@ -0,0 +1,90 @@
import contextlib
mblayman marked this conversation as resolved.
Show resolved Hide resolved
import os
import sys
from typing import Generator
from typing import Sequence
from typing import Tuple

import pre_commit.constants as C
from pre_commit.envcontext import envcontext
from pre_commit.envcontext import PatchesT
from pre_commit.envcontext import Var
from pre_commit.hook import Hook
from pre_commit.languages import helpers
from pre_commit.prefix import Prefix
from pre_commit.util import clean_path_on_failure
from pre_commit.util import cmd_output

ENVIRONMENT_DIR = 'lua_env'
get_default_version = helpers.basic_get_default_version
healthy = helpers.basic_healthy


def _get_lua_version() -> str: # pragma: win32 no cover
"""Get the Lua version used in file paths."""
_, stdout, _ = cmd_output('luarocks', 'config', '--lua-ver')
return stdout.strip()


def get_env_patch(d: str) -> PatchesT: # pragma: win32 no cover
version = _get_lua_version()
so_ext = 'dll' if sys.platform == 'win32' else 'so'
return (
('PATH', (os.path.join(d, 'bin'), os.pathsep, Var('PATH'))),
(
'LUA_PATH', (
os.path.join(d, 'share', 'lua', version, '?.lua;'),
os.path.join(d, 'share', 'lua', version, '?', 'init.lua;;'),
),
),
(
'LUA_CPATH',
(os.path.join(d, 'lib', 'lua', version, f'?.{so_ext};;'),),
),
)


def _envdir(prefix: Prefix) -> str: # pragma: win32 no cover
directory = helpers.environment_dir(ENVIRONMENT_DIR, C.DEFAULT)
return prefix.path(directory)


@contextlib.contextmanager # pragma: win32 no cover
def in_env(prefix: Prefix) -> Generator[None, None, None]:
with envcontext(get_env_patch(_envdir(prefix))):
yield


def install_environment(
prefix: Prefix,
version: str,
additional_dependencies: Sequence[str],
) -> None: # pragma: win32 no cover
helpers.assert_version_default('lua', version)

envdir = _envdir(prefix)
with clean_path_on_failure(envdir):
with in_env(prefix):
# luarocks doesn't bootstrap a tree prior to installing
# so ensure the directory exists.
os.makedirs(envdir, exist_ok=True)

# Older luarocks (e.g., 2.4.2) expect the rockspec as an arg
for rockspec in prefix.star('.rockspec'):
make_cmd = ('luarocks', '--tree', envdir, 'make', rockspec)
helpers.run_setup_cmd(prefix, make_cmd)

# luarocks can't install multiple packages at once
# so install them individually.
for dependency in additional_dependencies:
cmd = ('luarocks', '--tree', envdir, 'install', dependency)
helpers.run_setup_cmd(prefix, cmd)


def run_hook(
hook: Hook,
file_args: Sequence[str],
color: bool,
) -> Tuple[int, bytes]: # pragma: win32 no cover
with in_env(hook.prefix):
return helpers.run_xargs(hook, hook.cmd, file_args, color=color)
@@ -0,0 +1,12 @@
package = "pre-commit-package"
version = "dev-1"

source = {
url = "git+ssh://git@github.com/pre-commit/pre-commit.git"
}
description = {}
dependencies = {}
build = {
type = "builtin",
modules = {},
}
3 changes: 2 additions & 1 deletion pre_commit/store.py
Expand Up @@ -188,7 +188,8 @@ def _git_cmd(*args: str) -> None:

LOCAL_RESOURCES = (
'Cargo.toml', 'main.go', 'go.mod', 'main.rs', '.npmignore',
'package.json', 'pre_commit_placeholder_package.gemspec', 'setup.py',
'package.json', 'pre-commit-package-dev-1.rockspec',
'pre_commit_placeholder_package.gemspec', 'setup.py',
'environment.yml', 'Makefile.PL', 'pubspec.yaml',
'renv.lock', 'renv/activate.R', 'renv/LICENSE.renv',
)
Expand Down
2 changes: 1 addition & 1 deletion testing/gen-languages-all
Expand Up @@ -3,7 +3,7 @@ import sys

LANGUAGES = [
'conda', 'coursier', 'dart', 'docker', 'docker_image', 'dotnet', 'fail',
'golang', 'node', 'perl', 'pygrep', 'python', 'r', 'ruby', 'rust',
'golang', 'lua', 'node', 'perl', 'pygrep', 'python', 'r', 'ruby', 'rust',
'script', 'swift', 'system',
]
FIELDS = [
Expand Down
5 changes: 5 additions & 0 deletions testing/get-lua.sh
@@ -0,0 +1,5 @@
#!/usr/bin/env bash
set -euo pipefail

# Install the runtime and package manager.
sudo apt install lua5.3 liblua5.3-dev luarocks
4 changes: 4 additions & 0 deletions testing/resources/lua_repo/.pre-commit-hooks.yaml
@@ -0,0 +1,4 @@
- id: hello-world-lua
name: hello world lua
entry: hello-world-lua
language: lua
3 changes: 3 additions & 0 deletions testing/resources/lua_repo/bin/hello-world-lua
@@ -0,0 +1,3 @@
#!/usr/bin/env lua

print('hello world')
15 changes: 15 additions & 0 deletions testing/resources/lua_repo/hello-dev-1.rockspec
@@ -0,0 +1,15 @@
package = "hello"
version = "dev-1"

source = {
url = "git+ssh://git@github.com/pre-commit/pre-commit.git"
mblayman marked this conversation as resolved.
Show resolved Hide resolved
}
description = {}
dependencies = {}
build = {
type = "builtin",
modules = {},
install = {
bin = {"bin/hello-world-lua"}
},
}
4 changes: 4 additions & 0 deletions testing/util.py
Expand Up @@ -48,6 +48,10 @@ def cmd_output_mocked_pre_commit_home(
os.name == 'nt' or not docker_is_running(),
reason="Docker isn't running or can't be accessed",
)
skipif_cant_run_lua = pytest.mark.skipif(
os.name == 'nt',
reason="lua isn't installed or can't be found",
)
skipif_cant_run_swift = pytest.mark.skipif(
parse_shebang.find_executable('swift') is None,
reason="swift isn't installed or can't be found",
Expand Down
27 changes: 27 additions & 0 deletions tests/repository_test.py
Expand Up @@ -34,6 +34,7 @@
from testing.util import get_resource_path
from testing.util import skipif_cant_run_coursier
from testing.util import skipif_cant_run_docker
from testing.util import skipif_cant_run_lua
from testing.util import skipif_cant_run_swift
from testing.util import xfailif_windows

Expand Down Expand Up @@ -1128,3 +1129,29 @@ def test_non_installable_hook_error_for_additional_dependencies(store, caplog):
'using language `system` which does not install an environment. '
'Perhaps you meant to use a specific language?'
)


@skipif_cant_run_lua # pragma: win32 no cover
def test_lua_hook(tempdir_factory, store):
_test_hook_repo(
tempdir_factory, store, 'lua_repo',
'hello-world-lua', [], b'hello world\n',
)


@skipif_cant_run_lua # pragma: win32 no cover
def test_local_lua_additional_dependencies(store):
config = {
'repo': 'local',
'hooks': [{
'id': 'local-lua',
'name': 'local-lua',
'entry': 'luacheck --version',
'language': 'lua',
'additional_dependencies': ['luacheck'],
}],
}
hook = _get_hook(config, store, 'local-lua')
ret, out = _hook_run(hook, (), color=False)
assert b'Luacheck' in out
assert ret == 0