ifchange 0.1.0__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.
@@ -0,0 +1,4 @@
1
+ /target
2
+ /npm/bin/
3
+ /pypi/ifchange/bin/
4
+ /pypi/dist/
@@ -0,0 +1,14 @@
1
+ Metadata-Version: 2.4
2
+ Name: ifchange
3
+ Version: 0.1.0
4
+ Summary: A fast linter for enforcing conditional change directives in source code
5
+ Project-URL: Homepage, https://github.com/slnc/ifchange
6
+ Project-URL: Repository, https://github.com/slnc/ifchange
7
+ License-Expression: MIT
8
+ Classifier: Development Status :: 4 - Beta
9
+ Classifier: Environment :: Console
10
+ Classifier: Intended Audience :: Developers
11
+ Classifier: License :: OSI Approved :: MIT License
12
+ Classifier: Programming Language :: Python :: 3
13
+ Classifier: Topic :: Software Development :: Quality Assurance
14
+ Requires-Python: >=3.8
@@ -0,0 +1,3 @@
1
+ """ifchange: A fast linter for enforcing conditional change directives."""
2
+
3
+ __version__ = "0.1.0" # x-release-please-version
@@ -0,0 +1,20 @@
1
+ """Entry point for ifchange."""
2
+
3
+ import os
4
+ import sys
5
+
6
+ from .install import download_binary
7
+
8
+
9
+ def main():
10
+ bin_path = str(download_binary())
11
+ if sys.platform == "win32":
12
+ import subprocess
13
+ result = subprocess.run([bin_path] + sys.argv[1:])
14
+ sys.exit(result.returncode)
15
+ else:
16
+ os.execvp(bin_path, [bin_path] + sys.argv[1:])
17
+
18
+
19
+ if __name__ == "__main__":
20
+ main()
@@ -0,0 +1,119 @@
1
+ """Download and cache the ifchange binary from GitHub releases."""
2
+
3
+ import hashlib
4
+ import io
5
+ import os
6
+ import platform
7
+ import stat
8
+ import sys
9
+ import tarfile
10
+ import tempfile
11
+ import urllib.request
12
+ import zipfile
13
+ from pathlib import Path
14
+
15
+ from . import __version__
16
+
17
+ REPO = "slnc/ifchange"
18
+ BINARY = "ifchange"
19
+
20
+ PLATFORM_MAP = {
21
+ ("Linux", "x86_64"): "x86_64-unknown-linux-gnu",
22
+ ("Linux", "aarch64"): "aarch64-unknown-linux-gnu",
23
+ ("Darwin", "x86_64"): "x86_64-apple-darwin",
24
+ ("Darwin", "arm64"): "aarch64-apple-darwin",
25
+ ("Windows", "AMD64"): "x86_64-pc-windows-msvc",
26
+ }
27
+
28
+
29
+ def get_target():
30
+ system = platform.system()
31
+ machine = platform.machine()
32
+ target = PLATFORM_MAP.get((system, machine))
33
+ if not target:
34
+ raise RuntimeError(f"Unsupported platform: {system} {machine}")
35
+ return target
36
+
37
+
38
+ def get_bin_dir():
39
+ return Path(__file__).parent / "bin"
40
+
41
+
42
+ def get_bin_path():
43
+ name = f"{BINARY}.exe" if platform.system() == "Windows" else BINARY
44
+ return get_bin_dir() / name
45
+
46
+
47
+ def _fetch(url):
48
+ req = urllib.request.Request(url, headers={"User-Agent": "ifchange-pypi"})
49
+ with urllib.request.urlopen(req) as resp:
50
+ return resp.read()
51
+
52
+
53
+ def _verify_checksum(data, archive_name, checksums_data):
54
+ for line in checksums_data.decode().splitlines():
55
+ if archive_name in line:
56
+ expected = line.strip().split()[0]
57
+ actual = hashlib.sha256(data).hexdigest()
58
+ if expected != actual:
59
+ raise RuntimeError(
60
+ f"Checksum mismatch: expected {expected}, got {actual}"
61
+ )
62
+ return
63
+ raise RuntimeError(f"Checksum not found for {archive_name}")
64
+
65
+
66
+ def download_binary():
67
+ bin_path = get_bin_path()
68
+ if bin_path.exists():
69
+ return bin_path
70
+
71
+ target = get_target()
72
+ version = f"v{__version__}"
73
+ is_windows = platform.system() == "Windows"
74
+ ext = "zip" if is_windows else "tar.gz"
75
+ archive_name = f"{BINARY}-{version}-{target}.{ext}"
76
+
77
+ base_url = f"https://github.com/{REPO}/releases/download/{version}"
78
+ archive_url = f"{base_url}/{archive_name}"
79
+ checksums_url = f"{base_url}/SHA256SUMS"
80
+
81
+ print(f"Downloading {archive_name}...", file=sys.stderr)
82
+ archive_data = _fetch(archive_url)
83
+ checksums_data = _fetch(checksums_url)
84
+
85
+ _verify_checksum(archive_data, archive_name, checksums_data)
86
+ print("Checksum verified.", file=sys.stderr)
87
+
88
+ # Extract binary
89
+ bin_name = f"{BINARY}.exe" if is_windows else BINARY
90
+ extracted = None
91
+
92
+ if ext == "zip":
93
+ with zipfile.ZipFile(io.BytesIO(archive_data)) as zf:
94
+ for name in zf.namelist():
95
+ if name.endswith(bin_name):
96
+ extracted = zf.read(name)
97
+ break
98
+ else:
99
+ with tarfile.open(fileobj=io.BytesIO(archive_data), mode="r:gz") as tf:
100
+ for member in tf.getmembers():
101
+ # Guard against path traversal (e.g. ../../etc/passwd)
102
+ if member.name.startswith("/") or ".." in member.name.split("/"):
103
+ raise RuntimeError(f"Unsafe path in archive: {member.name}")
104
+ if member.name.endswith(bin_name) and member.isfile():
105
+ f = tf.extractfile(member)
106
+ if f:
107
+ extracted = f.read()
108
+ break
109
+
110
+ if not extracted:
111
+ raise RuntimeError(f"Could not find {bin_name} in archive")
112
+
113
+ bin_dir = get_bin_dir()
114
+ bin_dir.mkdir(parents=True, exist_ok=True)
115
+ bin_path.write_bytes(extracted)
116
+ bin_path.chmod(bin_path.stat().st_mode | stat.S_IEXEC | stat.S_IXGRP | stat.S_IXOTH)
117
+
118
+ print(f"Installed {BINARY} to {bin_path}", file=sys.stderr)
119
+ return bin_path
@@ -0,0 +1,28 @@
1
+ [build-system]
2
+ requires = ["hatchling"]
3
+ build-backend = "hatchling.build"
4
+
5
+ [project]
6
+ name = "ifchange"
7
+ version = "0.1.0"
8
+ description = "A fast linter for enforcing conditional change directives in source code"
9
+ license = "MIT"
10
+ requires-python = ">=3.8"
11
+ classifiers = [
12
+ "Development Status :: 4 - Beta",
13
+ "Environment :: Console",
14
+ "Intended Audience :: Developers",
15
+ "License :: OSI Approved :: MIT License",
16
+ "Programming Language :: Python :: 3",
17
+ "Topic :: Software Development :: Quality Assurance",
18
+ ]
19
+
20
+ [project.urls]
21
+ Homepage = "https://github.com/slnc/ifchange"
22
+ Repository = "https://github.com/slnc/ifchange"
23
+
24
+ [project.scripts]
25
+ ifchange = "ifchange.__main__:main"
26
+
27
+ [tool.hatch.build.targets.wheel]
28
+ packages = ["ifchange"]