ggcode 1.0.7__py3-none-any.whl

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,16 @@
1
+ Metadata-Version: 2.4
2
+ Name: ggcode
3
+ Version: 1.0.7
4
+ Summary: Thin Python wrapper that installs the ggcode GitHub Release binary
5
+ Author: topcheer
6
+ License: MIT
7
+ Project-URL: Homepage, https://github.com/topcheer/ggcode
8
+ Project-URL: Repository, https://github.com/topcheer/ggcode.git
9
+ Project-URL: Issues, https://github.com/topcheer/ggcode/issues
10
+ Requires-Python: >=3.9
11
+ Description-Content-Type: text/markdown
12
+
13
+ # ggcode Python wrapper
14
+
15
+ This package installs a small Python launcher that downloads the matching `ggcode`
16
+ binary from GitHub Releases on first run and then executes it.
@@ -0,0 +1,7 @@
1
+ ggcode_release_installer/__init__.py,sha256=BW7SWRpHoxuOQZ67pS20yog2LWYl-nK7-BEFBNrHGgA,22
2
+ ggcode_release_installer/cli.py,sha256=EjGZYULebEjmxB8KZV2-qOmgMeyyxI_D6-JDr4dBGN4,4217
3
+ ggcode-1.0.7.dist-info/METADATA,sha256=lf4TkUaqd-c83OgLEH5laCuY-7u8ONlOV8kyBey9E1o,576
4
+ ggcode-1.0.7.dist-info/WHEEL,sha256=aeYiig01lYGDzBgS8HxWXOg3uV61G9ijOsup-k9o1sk,91
5
+ ggcode-1.0.7.dist-info/entry_points.txt,sha256=_AnrNL0ZLbfZSsoco8ZUV0p07rwiv1MAwdk8I5RibmE,61
6
+ ggcode-1.0.7.dist-info/top_level.txt,sha256=zWijDQlEgfSedV-nYB9EKlPuOFVhQoFW922NVTcpGKg,25
7
+ ggcode-1.0.7.dist-info/RECORD,,
@@ -0,0 +1,5 @@
1
+ Wheel-Version: 1.0
2
+ Generator: setuptools (82.0.1)
3
+ Root-Is-Purelib: true
4
+ Tag: py3-none-any
5
+
@@ -0,0 +1,2 @@
1
+ [console_scripts]
2
+ ggcode = ggcode_release_installer.cli:main
@@ -0,0 +1 @@
1
+ ggcode_release_installer
@@ -0,0 +1 @@
1
+ __version__ = "1.0.7"
@@ -0,0 +1,134 @@
1
+ from __future__ import annotations
2
+
3
+ import hashlib
4
+ import os
5
+ import platform
6
+ import shutil
7
+ import stat
8
+ import subprocess
9
+ import sys
10
+ import tarfile
11
+ import tempfile
12
+ import urllib.request
13
+ import zipfile
14
+ from pathlib import Path
15
+
16
+ from . import __version__
17
+
18
+ OWNER = "topcheer"
19
+ REPO = "ggcode"
20
+
21
+
22
+ def normalize_version() -> str:
23
+ selected = os.environ.get("GGCODE_INSTALL_VERSION", __version__).strip()
24
+ if not selected or selected == "latest" or selected.startswith("0.0.0"):
25
+ return "latest"
26
+ if selected.startswith("v"):
27
+ return selected
28
+ return f"v{selected}"
29
+
30
+
31
+ def resolve_target() -> tuple[str, str]:
32
+ platform = sys.platform
33
+ if platform.startswith("linux"):
34
+ goos = "linux"
35
+ elif platform == "darwin":
36
+ goos = "darwin"
37
+ elif platform in ("win32", "cygwin"):
38
+ goos = "windows"
39
+ else:
40
+ raise RuntimeError(f"Unsupported platform: {platform}")
41
+
42
+ machine = platform.machine().lower() or os.environ.get("PROCESSOR_ARCHITECTURE", "").lower()
43
+ if machine in ("x86_64", "amd64"):
44
+ arch = "x86_64"
45
+ elif machine in ("arm64", "aarch64"):
46
+ arch = "arm64"
47
+ else:
48
+ raise RuntimeError(f"Unsupported architecture: {machine or 'unknown'}")
49
+ return goos, arch
50
+
51
+
52
+ def release_base(version: str) -> str:
53
+ if version == "latest":
54
+ return f"https://github.com/{OWNER}/{REPO}/releases/latest/download"
55
+ return f"https://github.com/{OWNER}/{REPO}/releases/download/{version}"
56
+
57
+
58
+ def cache_root() -> Path:
59
+ if os.name == "nt":
60
+ base = Path(os.environ.get("LOCALAPPDATA", tempfile.gettempdir()))
61
+ return base / "ggcode" / "python"
62
+ return Path.home() / ".cache" / "ggcode" / "python"
63
+
64
+
65
+ def download(url: str) -> bytes:
66
+ with urllib.request.urlopen(url) as response:
67
+ return response.read()
68
+
69
+
70
+ def parse_checksums(body: str) -> dict[str, str]:
71
+ checksums: dict[str, str] = {}
72
+ for raw_line in body.splitlines():
73
+ parts = raw_line.strip().split()
74
+ if len(parts) >= 2:
75
+ checksums[parts[-1]] = parts[0]
76
+ return checksums
77
+
78
+
79
+ def ensure_installed() -> Path:
80
+ version = normalize_version()
81
+ goos, arch = resolve_target()
82
+ archive_ext = ".zip" if goos == "windows" else ".tar.gz"
83
+ archive_name = f"ggcode_{goos}_{arch}{archive_ext}"
84
+ binary_name = "ggcode.exe" if goos == "windows" else "ggcode"
85
+ install_dir = cache_root() / version / f"{goos}-{arch}"
86
+ binary_path = install_dir / binary_name
87
+ if binary_path.exists():
88
+ return binary_path
89
+
90
+ base = release_base(version)
91
+ archive = download(f"{base}/{archive_name}")
92
+ checksums = parse_checksums(download(f"{base}/checksums.txt").decode("utf-8"))
93
+ expected = checksums.get(archive_name)
94
+ if not expected:
95
+ raise RuntimeError(f"Checksum for {archive_name} not found")
96
+ actual = hashlib.sha256(archive).hexdigest()
97
+ if actual.lower() != expected.lower():
98
+ raise RuntimeError(f"Checksum mismatch for {archive_name}")
99
+
100
+ with tempfile.TemporaryDirectory(prefix="ggcode-py-") as temp_dir:
101
+ temp_root = Path(temp_dir)
102
+ archive_path = temp_root / archive_name
103
+ archive_path.write_bytes(archive)
104
+ extract_dir = temp_root / "extract"
105
+ extract_dir.mkdir(parents=True, exist_ok=True)
106
+
107
+ if archive_ext == ".zip":
108
+ with zipfile.ZipFile(archive_path) as zf:
109
+ zf.extractall(extract_dir)
110
+ else:
111
+ with tarfile.open(archive_path, "r:gz") as tf:
112
+ tf.extractall(extract_dir)
113
+
114
+ extracted = next((p for p in extract_dir.rglob(binary_name) if p.is_file()), None)
115
+ if extracted is None:
116
+ raise RuntimeError(f"Could not find {binary_name} in {archive_name}")
117
+
118
+ install_dir.mkdir(parents=True, exist_ok=True)
119
+ shutil.copy2(extracted, binary_path)
120
+ if os.name != "nt":
121
+ current_mode = binary_path.stat().st_mode
122
+ binary_path.chmod(current_mode | stat.S_IXUSR | stat.S_IXGRP | stat.S_IXOTH)
123
+
124
+ return binary_path
125
+
126
+
127
+ def main() -> int:
128
+ binary = ensure_installed()
129
+ result = subprocess.run([str(binary), *sys.argv[1:]])
130
+ return result.returncode
131
+
132
+
133
+ if __name__ == "__main__":
134
+ raise SystemExit(main())