gitfluff 0.9.0__tar.gz → 0.9.1__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gitfluff
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Commit message linter and formatter
5
5
  Author-email: Na'aman Hirschfeld <nhirschfeld@gmail.com>
6
6
  License: MIT
@@ -25,7 +25,7 @@ Requires-Dist: certifi>=2024.7.4
25
25
 
26
26
  # gitfluff
27
27
 
28
- Commit message linter with presets, custom formats, and cleanup automation. Fully compliant with [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). Skips linting while Git is creating a merge commit.
28
+ Commit message linter with presets, custom formats, and cleanup automation. Fully compliant with [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). Skips linting the repository’s `COMMIT_EDITMSG` while Git is creating a merge commit.
29
29
 
30
30
  This Python package distributes prebuilt `gitfluff` binaries. On first use, the correct release binary for your platform is downloaded and cached under `~/.cache/gitfluff`. The binary's SHA-256 checksum is verified against the release's published checksums manifest before extraction.
31
31
 
@@ -96,7 +96,7 @@ default_install_hook_types:
96
96
 
97
97
  repos:
98
98
  - repo: https://github.com/Goldziher/gitfluff
99
- rev: v0.9.0
99
+ rev: v0.9.1
100
100
  hooks:
101
101
  - id: gitfluff-lint
102
102
  stages: [commit-msg]
@@ -124,7 +124,7 @@ commit-msg:
124
124
  **Install hooks:**
125
125
 
126
126
  ```bash
127
- npx lefthook install
127
+ pnpm dlx lefthook install
128
128
  ```
129
129
 
130
130
  ## Optional configuration
@@ -1,6 +1,6 @@
1
1
  # gitfluff
2
2
 
3
- Commit message linter with presets, custom formats, and cleanup automation. Fully compliant with [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). Skips linting while Git is creating a merge commit.
3
+ Commit message linter with presets, custom formats, and cleanup automation. Fully compliant with [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). Skips linting the repository’s `COMMIT_EDITMSG` while Git is creating a merge commit.
4
4
 
5
5
  This Python package distributes prebuilt `gitfluff` binaries. On first use, the correct release binary for your platform is downloaded and cached under `~/.cache/gitfluff`. The binary's SHA-256 checksum is verified against the release's published checksums manifest before extraction.
6
6
 
@@ -71,7 +71,7 @@ default_install_hook_types:
71
71
 
72
72
  repos:
73
73
  - repo: https://github.com/Goldziher/gitfluff
74
- rev: v0.9.0
74
+ rev: v0.9.1
75
75
  hooks:
76
76
  - id: gitfluff-lint
77
77
  stages: [commit-msg]
@@ -99,7 +99,7 @@ commit-msg:
99
99
  **Install hooks:**
100
100
 
101
101
  ```bash
102
- npx lefthook install
102
+ pnpm dlx lefthook install
103
103
  ```
104
104
 
105
105
  ## Optional configuration
@@ -1,2 +1,2 @@
1
1
  __all__ = ["__version__"]
2
- __version__ = "0.9.0"
2
+ __version__ = "0.9.1"
@@ -77,15 +77,15 @@ def _download(url: str, destination: Path) -> None:
77
77
  def _extract(archive: Path, ext: str, destination: Path) -> None:
78
78
  if ext == "zip":
79
79
  with zipfile.ZipFile(archive) as zf:
80
- for name in zf.namelist():
81
- if name.endswith("gitfluff") or name.endswith("gitfluff.exe"):
82
- with zf.open(name) as src, destination.open("wb") as dst:
80
+ for member in zf.infolist():
81
+ if not member.is_dir() and PurePosixPath(member.filename).name == destination.name:
82
+ with zf.open(member) as src, destination.open("wb") as dst:
83
83
  dst.write(src.read())
84
84
  return
85
85
  else:
86
86
  with tarfile.open(archive, "r:gz") as tar:
87
87
  for member in tar.getmembers():
88
- if member.name.endswith("gitfluff") or member.name.endswith("gitfluff.exe"):
88
+ if member.isfile() and PurePosixPath(member.name).name == destination.name:
89
89
  with tar.extractfile(member) as src, destination.open("wb") as dst:
90
90
  dst.write(src.read())
91
91
  return
@@ -171,14 +171,16 @@ def ensure_binary() -> str:
171
171
  url, ext, archive_name = _asset(__version__)
172
172
  print(f"Downloading gitfluff binary v{__version__}...", file=sys.stderr)
173
173
 
174
- with tempfile.TemporaryDirectory() as tmpdir:
174
+ with tempfile.TemporaryDirectory(dir=binary_path.parent) as tmpdir:
175
175
  archive_path = Path(tmpdir) / f"gitfluff.{ext}"
176
+ staged_binary = Path(tmpdir) / binary_path.name
176
177
  _download(url, archive_path)
177
178
  _verify_checksum(archive_path, archive_name, __version__)
178
- _extract(archive_path, ext, binary_path)
179
+ _extract(archive_path, ext, staged_binary)
179
180
 
180
- if platform.system().lower() != "windows":
181
- binary_path.chmod(0o755)
181
+ if platform.system().lower() != "windows":
182
+ staged_binary.chmod(0o755)
183
+ staged_binary.replace(binary_path)
182
184
 
183
185
  print("Binary downloaded successfully!", file=sys.stderr)
184
186
  return str(binary_path)
@@ -186,4 +188,5 @@ def ensure_binary() -> str:
186
188
 
187
189
  def run_gitfluff(args: list[str]) -> int:
188
190
  binary = ensure_binary()
189
- return subprocess.call([binary, *args])
191
+ status = subprocess.call([binary, *args])
192
+ return 128 - status if status < 0 else status
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.1
2
2
  Name: gitfluff
3
- Version: 0.9.0
3
+ Version: 0.9.1
4
4
  Summary: Commit message linter and formatter
5
5
  Author-email: Na'aman Hirschfeld <nhirschfeld@gmail.com>
6
6
  License: MIT
@@ -25,7 +25,7 @@ Requires-Dist: certifi>=2024.7.4
25
25
 
26
26
  # gitfluff
27
27
 
28
- Commit message linter with presets, custom formats, and cleanup automation. Fully compliant with [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). Skips linting while Git is creating a merge commit.
28
+ Commit message linter with presets, custom formats, and cleanup automation. Fully compliant with [Conventional Commits 1.0.0](https://www.conventionalcommits.org/en/v1.0.0/). Skips linting the repository’s `COMMIT_EDITMSG` while Git is creating a merge commit.
29
29
 
30
30
  This Python package distributes prebuilt `gitfluff` binaries. On first use, the correct release binary for your platform is downloaded and cached under `~/.cache/gitfluff`. The binary's SHA-256 checksum is verified against the release's published checksums manifest before extraction.
31
31
 
@@ -96,7 +96,7 @@ default_install_hook_types:
96
96
 
97
97
  repos:
98
98
  - repo: https://github.com/Goldziher/gitfluff
99
- rev: v0.9.0
99
+ rev: v0.9.1
100
100
  hooks:
101
101
  - id: gitfluff-lint
102
102
  stages: [commit-msg]
@@ -124,7 +124,7 @@ commit-msg:
124
124
  **Install hooks:**
125
125
 
126
126
  ```bash
127
- npx lefthook install
127
+ pnpm dlx lefthook install
128
128
  ```
129
129
 
130
130
  ## Optional configuration
@@ -9,4 +9,5 @@ gitfluff.egg-info/SOURCES.txt
9
9
  gitfluff.egg-info/dependency_links.txt
10
10
  gitfluff.egg-info/entry_points.txt
11
11
  gitfluff.egg-info/requires.txt
12
- gitfluff.egg-info/top_level.txt
12
+ gitfluff.egg-info/top_level.txt
13
+ tests/test_downloader.py
@@ -4,7 +4,7 @@ build-backend = "setuptools.build_meta"
4
4
 
5
5
  [project]
6
6
  name = "gitfluff"
7
- version = "0.9.0"
7
+ version = "0.9.1"
8
8
  description = "Commit message linter and formatter"
9
9
  readme = "README.md"
10
10
  license = { text = "MIT" }
@@ -0,0 +1,78 @@
1
+ import io
2
+ import os
3
+ import signal
4
+ import sys
5
+ import tarfile
6
+ import tempfile
7
+ import unittest
8
+ import zipfile
9
+ from contextlib import ExitStack
10
+ from pathlib import Path
11
+ from unittest.mock import patch
12
+
13
+ sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
14
+
15
+ from gitfluff import downloader
16
+
17
+
18
+ class DownloaderTests(unittest.TestCase):
19
+ def test_should_ignore_suffix_lookalikes_when_extracting_binary(self) -> None:
20
+ with tempfile.TemporaryDirectory() as directory:
21
+ archive = Path(directory) / "release.tar.gz"
22
+ destination = Path(directory) / "gitfluff"
23
+ with tarfile.open(archive, "w:gz") as release:
24
+ for name, content in [("notgitfluff", b"wrong"), ("bin/gitfluff", b"correct")]:
25
+ member = tarfile.TarInfo(name)
26
+ member.size = len(content)
27
+ release.addfile(member, io.BytesIO(content))
28
+ downloader._extract(archive, "tar.gz", destination)
29
+ self.assertEqual(destination.read_bytes(), b"correct")
30
+
31
+ def test_should_extract_exact_binary_name_from_zip(self) -> None:
32
+ with tempfile.TemporaryDirectory() as directory:
33
+ archive = Path(directory) / "release.zip"
34
+ destination = Path(directory) / "gitfluff.exe"
35
+ with zipfile.ZipFile(archive, "w") as release:
36
+ release.writestr("notgitfluff.exe", b"wrong")
37
+ release.writestr("bin/gitfluff.exe", b"correct")
38
+ downloader._extract(archive, "zip", destination)
39
+ self.assertEqual(destination.read_bytes(), b"correct")
40
+
41
+ def test_should_reject_directory_when_extracting_binary(self) -> None:
42
+ with tempfile.TemporaryDirectory() as directory:
43
+ archive = Path(directory) / "release.tar.gz"
44
+ destination = Path(directory) / "gitfluff"
45
+ with tarfile.open(archive, "w:gz") as release:
46
+ member = tarfile.TarInfo("gitfluff")
47
+ member.type = tarfile.DIRTYPE
48
+ release.addfile(member)
49
+ with self.assertRaisesRegex(RuntimeError, "Binary not found"):
50
+ downloader._extract(archive, "tar.gz", destination)
51
+
52
+ def test_should_leave_no_cached_binary_when_extraction_fails(self) -> None:
53
+ with tempfile.TemporaryDirectory() as directory:
54
+ destination = Path(directory) / "gitfluff"
55
+
56
+ def fail_extraction(_archive: Path, _extension: str, output: Path) -> None:
57
+ output.write_bytes(b"partial")
58
+ raise OSError("interrupted extraction")
59
+
60
+ with ExitStack() as stack:
61
+ stack.enter_context(patch.dict(os.environ, {"GITFLUFF_BINARY": ""}))
62
+ stack.enter_context(patch.object(downloader, "_cache_path", return_value=destination))
63
+ stack.enter_context(patch.object(downloader, "_download"))
64
+ stack.enter_context(patch.object(downloader, "_verify_checksum"))
65
+ stack.enter_context(patch.object(downloader, "_extract", side_effect=fail_extraction))
66
+ stack.enter_context(self.assertRaisesRegex(OSError, "interrupted extraction"))
67
+ downloader.ensure_binary()
68
+ self.assertEqual(destination.exists(), False)
69
+
70
+ @unittest.skipIf(os.name == "nt", "POSIX signals required")
71
+ def test_should_return_failure_status_when_child_is_terminated(self) -> None:
72
+ with patch.dict(os.environ, {"GITFLUFF_BINARY": sys.executable}):
73
+ status = downloader.run_gitfluff(["-c", "import os, signal; os.kill(os.getpid(), signal.SIGTERM)"])
74
+ self.assertEqual(status, 128 + signal.SIGTERM)
75
+
76
+
77
+ if __name__ == "__main__":
78
+ unittest.main()
File without changes
File without changes
File without changes