summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorJoris Roovers <joris.roovers@gmail.com>2023-03-28 08:28:02 +0200
committerGitHub <noreply@github.com>2023-03-28 08:28:02 +0200
commitc9ebfe30aee32f1825aa807a38c41b426ec353cc (patch)
tree31182642be8fa78642a5920afda11faa8824686a
parenta3ea8aeb6398befe8ed71f7458dd240e4da83e01 (diff)
Remove sh library (#476)
Removes the dependency on the `sh` library, which has been disabled and deprecated since 0.18.0.
-rw-r--r--.github/workflows/ci.yml9
-rw-r--r--.github/workflows/test-release.yml9
-rw-r--r--gitlint-core/gitlint/shell.py123
-rw-r--r--gitlint-core/gitlint/tests/test_utils.py16
-rw-r--r--gitlint-core/gitlint/utils.py16
-rw-r--r--gitlint-core/pyproject.toml2
6 files changed, 60 insertions, 115 deletions
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 013fbc6..e7a6447 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -51,18 +51,11 @@ jobs:
run: |
hatch run qa:install-local
- - name: Integration tests (default -> GITLINT_USE_SH_LIB=1)
+ - name: Integration tests
run: |
hatch run qa:integration-tests
if: matrix.os != 'windows-latest'
- - name: Integration tests (GITLINT_USE_SH_LIB=1)
- run: |
- hatch run qa:integration-tests
- env:
- GITLINT_USE_SH_LIB: 1
- if: matrix.os != 'windows-latest'
-
- name: Integration tests (GITLINT_QA_USE_SH_LIB=0)
run: |
hatch run qa:integration-tests -k "not(test_commit_hook_continue or test_commit_hook_abort or test_commit_hook_edit)" qa
diff --git a/.github/workflows/test-release.yml b/.github/workflows/test-release.yml
index caf00dd..75e06f0 100644
--- a/.github/workflows/test-release.yml
+++ b/.github/workflows/test-release.yml
@@ -70,18 +70,11 @@ jobs:
with:
ref: ${{ inputs.repo_test_ref }}
- - name: Integration tests (default -> GITLINT_USE_SH_LIB=1)
+ - name: Integration tests
run: |
hatch run qa:integration-tests
if: matrix.os != 'windows-latest'
- - name: Integration tests (GITLINT_USE_SH_LIB=1)
- run: |
- hatch run qa:integration-tests
- env:
- GITLINT_USE_SH_LIB: 1
- if: matrix.os != 'windows-latest'
-
- name: Integration tests (GITLINT_QA_USE_SH_LIB=0)
run: |
hatch run qa:integration-tests -k "not(test_commit_hook_continue or test_commit_hook_abort or test_commit_hook_edit)" qa
diff --git a/gitlint-core/gitlint/shell.py b/gitlint-core/gitlint/shell.py
index fddece0..36160a9 100644
--- a/gitlint-core/gitlint/shell.py
+++ b/gitlint-core/gitlint/shell.py
@@ -1,12 +1,11 @@
"""
-This module implements a shim for the 'sh' library, mainly for use on Windows (sh is not supported on Windows).
-We might consider removing the 'sh' dependency altogether in the future, but 'sh' does provide a few
-capabilities wrt dealing with more edge-case environments on *nix systems that are useful.
+This module implements a shim for the `sh` library (https://amoffat.github.io/sh/), which gitlint used to depend on.
+We still keep the `sh` API and semantics so the rest of the gitlint codebase doesn't need to be changed.
"""
import subprocess
-from gitlint.utils import TERMINAL_ENCODING, USE_SH_LIB
+from gitlint.utils import TERMINAL_ENCODING
def shell(cmd):
@@ -15,64 +14,58 @@ def shell(cmd):
p.communicate()
-if USE_SH_LIB:
- # import exceptions separately, this makes it a little easier to mock them out in the unit tests
- from sh import (
- CommandNotFound,
- ErrorReturnCode,
- git,
- )
-else:
-
- class CommandNotFound(Exception):
- """Exception indicating a command was not found during execution"""
-
- class ShResult:
- """Result wrapper class. We use this to more easily migrate from using https://amoffat.github.io/sh/ to using
- the builtin subprocess module"""
-
- def __init__(self, full_cmd, stdout, stderr="", exitcode=0):
- self.full_cmd = full_cmd
- self.stdout = stdout
- self.stderr = stderr
- self.exit_code = exitcode
-
- def __str__(self):
- return self.stdout
-
- class ErrorReturnCode(ShResult, Exception):
- """ShResult subclass for unexpected results (acts as an exception)."""
-
- def git(*command_parts, **kwargs):
- """Git shell wrapper.
- Implemented as separate function here, so we can do a 'sh' style imports:
- `from shell import git`
- """
- args = ["git", *list(command_parts)]
- return _exec(*args, **kwargs)
-
- def _exec(*args, **kwargs):
- pipe = subprocess.PIPE
- popen_kwargs = {"stdout": pipe, "stderr": pipe, "shell": kwargs.get("_tty_out", False)}
- if "_cwd" in kwargs:
- popen_kwargs["cwd"] = kwargs["_cwd"]
-
- try:
- with subprocess.Popen(args, **popen_kwargs) as p:
- result = p.communicate()
- except FileNotFoundError as e:
- raise CommandNotFound from e
-
- exit_code = p.returncode
- stdout = result[0].decode(TERMINAL_ENCODING)
- stderr = result[1] # 'sh' does not decode the stderr bytes to unicode
- full_cmd = "" if args is None else " ".join(args)
-
- # If not _ok_code is specified, then only a 0 exit code is allowed
- ok_exit_codes = kwargs.get("_ok_code", [0])
-
- if exit_code in ok_exit_codes:
- return ShResult(full_cmd, stdout, stderr, exit_code)
-
- # Unexpected error code => raise ErrorReturnCode
- raise ErrorReturnCode(full_cmd, stdout, stderr, p.returncode)
+class CommandNotFound(Exception):
+ """Exception indicating a command was not found during execution"""
+
+
+class ShResult:
+ """Result wrapper class"""
+
+ def __init__(self, full_cmd, stdout, stderr="", exitcode=0):
+ self.full_cmd = full_cmd
+ self.stdout = stdout
+ self.stderr = stderr
+ self.exit_code = exitcode
+
+ def __str__(self):
+ return self.stdout
+
+
+class ErrorReturnCode(ShResult, Exception):
+ """ShResult subclass for unexpected results (acts as an exception)."""
+
+
+def git(*command_parts, **kwargs):
+ """Git shell wrapper.
+ Implemented as separate function here, so we can do a 'sh' style imports:
+ `from shell import git`
+ """
+ args = ["git", *list(command_parts)]
+ return _exec(*args, **kwargs)
+
+
+def _exec(*args, **kwargs):
+ pipe = subprocess.PIPE
+ popen_kwargs = {"stdout": pipe, "stderr": pipe, "shell": kwargs.get("_tty_out", False)}
+ if "_cwd" in kwargs:
+ popen_kwargs["cwd"] = kwargs["_cwd"]
+
+ try:
+ with subprocess.Popen(args, **popen_kwargs) as p:
+ result = p.communicate()
+ except FileNotFoundError as e:
+ raise CommandNotFound from e
+
+ exit_code = p.returncode
+ stdout = result[0].decode(TERMINAL_ENCODING)
+ stderr = result[1] # 'sh' does not decode the stderr bytes to unicode
+ full_cmd = "" if args is None else " ".join(args)
+
+ # If not _ok_code is specified, then only a 0 exit code is allowed
+ ok_exit_codes = kwargs.get("_ok_code", [0])
+
+ if exit_code in ok_exit_codes:
+ return ShResult(full_cmd, stdout, stderr, exit_code)
+
+ # Unexpected error code => raise ErrorReturnCode
+ raise ErrorReturnCode(full_cmd, stdout, stderr, p.returncode)
diff --git a/gitlint-core/gitlint/tests/test_utils.py b/gitlint-core/gitlint/tests/test_utils.py
index d21ec3f..80883c4 100644
--- a/gitlint-core/gitlint/tests/test_utils.py
+++ b/gitlint-core/gitlint/tests/test_utils.py
@@ -10,22 +10,6 @@ class UtilsTests(BaseTestCase):
# its value after we're done this doesn't influence other tests
utils.PLATFORM_IS_WINDOWS = utils.platform_is_windows()
- @patch("os.environ")
- def test_use_sh_library(self, patched_env):
- patched_env.get.return_value = "1"
- self.assertEqual(utils.use_sh_library(), True)
- patched_env.get.assert_called_once_with("GITLINT_USE_SH_LIB", None)
-
- for invalid_val in ["0", "foƶbar"]:
- patched_env.get.reset_mock() # reset mock call count
- patched_env.get.return_value = invalid_val
- self.assertEqual(utils.use_sh_library(), False, invalid_val)
- patched_env.get.assert_called_once_with("GITLINT_USE_SH_LIB", None)
-
- # Assert that when GITLINT_USE_SH_LIB is not set, we fallback to False (not using)
- patched_env.get.return_value = None
- self.assertEqual(utils.use_sh_library(), False)
-
@patch("gitlint.utils.locale")
def test_terminal_encoding_non_windows(self, mocked_locale):
utils.PLATFORM_IS_WINDOWS = False
diff --git a/gitlint-core/gitlint/utils.py b/gitlint-core/gitlint/utils.py
index 3ccb78b..ba4f956 100644
--- a/gitlint-core/gitlint/utils.py
+++ b/gitlint-core/gitlint/utils.py
@@ -22,22 +22,6 @@ def platform_is_windows():
PLATFORM_IS_WINDOWS = platform_is_windows()
########################################################################################################################
-# USE_SH_LIB
-# Determine whether to use the `sh` library
-# On windows we won't want to use the sh library since it's not supported - instead we'll use our own shell module.
-# However, we want to be able to overwrite this behavior for testing using the GITLINT_USE_SH_LIB env var.
-
-
-def use_sh_library():
- gitlint_use_sh_lib_env = os.environ.get("GITLINT_USE_SH_LIB", None)
- if gitlint_use_sh_lib_env:
- return gitlint_use_sh_lib_env == "1"
- return False
-
-
-USE_SH_LIB = use_sh_library()
-
-########################################################################################################################
# TERMINAL_ENCODING
# Encoding used for terminal encoding/decoding.
diff --git a/gitlint-core/pyproject.toml b/gitlint-core/pyproject.toml
index e65b7b0..7dd2cf1 100644
--- a/gitlint-core/pyproject.toml
+++ b/gitlint-core/pyproject.toml
@@ -36,14 +36,12 @@ dependencies = [
"arrow>=1",
"Click>=8",
"importlib-metadata >= 1.0 ; python_version < \"3.8\"",
- "sh>=1.13.0 ; sys_platform != \"win32\"",
]
[project.optional-dependencies]
trusted-deps = [
"arrow==1.2.3",
"Click==8.1.3",
- "sh==1.14.3 ; sys_platform != \"win32\"",
]
[project.scripts]