oxymake 0.1.0__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,32 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: oxymake
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Content-addressable workflow engine — git checkout no longer rebuilds everything
|
|
5
|
+
Project-URL: Homepage, https://oxymake.dev
|
|
6
|
+
Project-URL: Repository, https://github.com/noogram/oxymake
|
|
7
|
+
Project-URL: Releases, https://github.com/noogram/oxymake/releases
|
|
8
|
+
Author-email: Emmanuel Sérié <emmanuel@serie.dev>
|
|
9
|
+
License: MIT OR Apache-2.0
|
|
10
|
+
Keywords: build,cache,pipeline,snakemake,workflow
|
|
11
|
+
Requires-Python: >=3.8
|
|
12
|
+
Description-Content-Type: text/markdown
|
|
13
|
+
|
|
14
|
+
# OxyMake (Python launcher)
|
|
15
|
+
|
|
16
|
+
A content-addressable workflow engine. You `git checkout` an old branch, re-run
|
|
17
|
+
your pipeline, and it **does not** rebuild everything — change detection is a
|
|
18
|
+
BLAKE3 hash of file *content*, not timestamps.
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv tool install oxymake # or: pipx install oxymake
|
|
22
|
+
ox --help
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
This package is a thin launcher: on first run it downloads the prebuilt `ox`
|
|
26
|
+
binary for your platform from the
|
|
27
|
+
[GitHub release](https://github.com/noogram/oxymake/releases/latest), verifies
|
|
28
|
+
its SHA-256, caches it, and execs it. No Rust toolchain required — which is the
|
|
29
|
+
point: bioinformatics and data-science users who live in conda/pip can try
|
|
30
|
+
OxyMake without a source build.
|
|
31
|
+
|
|
32
|
+
Full documentation: <https://github.com/noogram/oxymake>
|
|
@@ -0,0 +1,5 @@
|
|
|
1
|
+
oxymake_launcher.py,sha256=rXBVhu5vhPoyc2KVNYjtmsSVx13-L5EACXSRDH72Itk,3074
|
|
2
|
+
oxymake-0.1.0.dist-info/METADATA,sha256=q8hq6pgDBckHJ5xe8uRUXSne3eA2bIwrr3o_vwmK_aE,1258
|
|
3
|
+
oxymake-0.1.0.dist-info/WHEEL,sha256=mffPy8wBnZQn2VnJUU5jE99KsxaSfiyMHV9Yt0aLVxs,87
|
|
4
|
+
oxymake-0.1.0.dist-info/entry_points.txt,sha256=y5yi2BPajfAS6EvK_LpkZKaQxES9ipdNhzUuRGhDQqk,77
|
|
5
|
+
oxymake-0.1.0.dist-info/RECORD,,
|
oxymake_launcher.py
ADDED
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
"""Thin launcher for the OxyMake `ox` binary.
|
|
2
|
+
|
|
3
|
+
Installed via `uv tool install oxymake` / `pipx install oxymake`, this module
|
|
4
|
+
exposes the `ox` and `oxymake` console scripts. On first invocation it downloads
|
|
5
|
+
the prebuilt binary matching the host platform from the GitHub release, verifies
|
|
6
|
+
its SHA-256 against the `.sha256` sidecar, caches it under the user cache dir,
|
|
7
|
+
and execs it. No Rust toolchain is required.
|
|
8
|
+
|
|
9
|
+
This keeps the conda/pip onboarding path open: the audience that lives in Python
|
|
10
|
+
never has to `cargo install` from source.
|
|
11
|
+
"""
|
|
12
|
+
|
|
13
|
+
from __future__ import annotations
|
|
14
|
+
|
|
15
|
+
import hashlib
|
|
16
|
+
import os
|
|
17
|
+
import platform
|
|
18
|
+
import stat
|
|
19
|
+
import sys
|
|
20
|
+
import tarfile
|
|
21
|
+
import tempfile
|
|
22
|
+
import urllib.request
|
|
23
|
+
from pathlib import Path
|
|
24
|
+
|
|
25
|
+
__version__ = "0.1.0"
|
|
26
|
+
|
|
27
|
+
_REPO = "noogram/oxymake"
|
|
28
|
+
_BASE = f"https://github.com/{_REPO}/releases/download/v{__version__}"
|
|
29
|
+
|
|
30
|
+
# (sys.platform startswith, machine) -> release target triple
|
|
31
|
+
_TARGETS = {
|
|
32
|
+
("darwin", "arm64"): "aarch64-apple-darwin",
|
|
33
|
+
("darwin", "x86_64"): "x86_64-apple-darwin",
|
|
34
|
+
("linux", "x86_64"): "x86_64-unknown-linux-gnu",
|
|
35
|
+
("linux", "amd64"): "x86_64-unknown-linux-gnu",
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
|
|
39
|
+
def _target() -> str:
|
|
40
|
+
key = (sys.platform, platform.machine().lower())
|
|
41
|
+
target = _TARGETS.get(key)
|
|
42
|
+
if target is None:
|
|
43
|
+
raise SystemExit(
|
|
44
|
+
f"oxymake: no prebuilt binary for {key}. "
|
|
45
|
+
f"Build from source: https://github.com/{_REPO}#install"
|
|
46
|
+
)
|
|
47
|
+
return target
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
def _cache_dir() -> Path:
|
|
51
|
+
root = os.environ.get("XDG_CACHE_HOME") or str(Path.home() / ".cache")
|
|
52
|
+
d = Path(root) / "oxymake" / __version__
|
|
53
|
+
d.mkdir(parents=True, exist_ok=True)
|
|
54
|
+
return d
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def _download(url: str, dest: Path) -> None:
|
|
58
|
+
with urllib.request.urlopen(url) as resp, open(dest, "wb") as fh: # noqa: S310
|
|
59
|
+
fh.write(resp.read())
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
def _verify(tarball: Path, target: str) -> None:
|
|
63
|
+
"""Verify the tarball against its published .sha256 sidecar."""
|
|
64
|
+
expected = _download_text(f"{_BASE}/ox-{target}.tar.gz.sha256").split()[0].strip()
|
|
65
|
+
actual = hashlib.sha256(tarball.read_bytes()).hexdigest()
|
|
66
|
+
if actual != expected:
|
|
67
|
+
raise SystemExit(
|
|
68
|
+
f"oxymake: checksum mismatch for ox-{target}.tar.gz "
|
|
69
|
+
f"(expected {expected}, got {actual}). Refusing to run."
|
|
70
|
+
)
|
|
71
|
+
|
|
72
|
+
|
|
73
|
+
def _download_text(url: str) -> str:
|
|
74
|
+
with urllib.request.urlopen(url) as resp: # noqa: S310
|
|
75
|
+
return resp.read().decode()
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
def _ensure_binary() -> Path:
|
|
79
|
+
target = _target()
|
|
80
|
+
binary = _cache_dir() / "ox"
|
|
81
|
+
if binary.exists():
|
|
82
|
+
return binary
|
|
83
|
+
|
|
84
|
+
url = f"{_BASE}/ox-{target}.tar.gz"
|
|
85
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
86
|
+
tarball = Path(tmp) / "ox.tar.gz"
|
|
87
|
+
_download(url, tarball)
|
|
88
|
+
_verify(tarball, target)
|
|
89
|
+
with tarfile.open(tarball) as tf:
|
|
90
|
+
tf.extract("ox", _cache_dir()) # noqa: S202 - controlled, verified archive
|
|
91
|
+
binary.chmod(binary.stat().st_mode | stat.S_IEXEC)
|
|
92
|
+
return binary
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
def main() -> "int":
|
|
96
|
+
binary = _ensure_binary()
|
|
97
|
+
os.execv(str(binary), [str(binary), *sys.argv[1:]])
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
if __name__ == "__main__":
|
|
101
|
+
main()
|