lib-cli-exit-tools 1.1.1__tar.gz → 1.2.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.
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/.env.example +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/.github/workflows/ci.yml +1 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/.github/workflows/codeql.yml +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/.github/workflows/release.yml +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/.gitignore +1 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/AGENTS.md +10 -14
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/CHANGELOG.md +4 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/CONTRIBUTING.md +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/DEVELOPMENT.md +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/LICENSE +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/Makefile +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/PKG-INFO +2 -2
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/README.md +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/codecov.yml +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/docs/system-design/reference.md +7 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/notebooks/Quickstart.ipynb +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/packaging/README.md +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/packaging/brew/Formula/lib-cli-exit-tools.rb +7 -2
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/packaging/conda/recipe/meta.yaml +2 -2
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/packaging/nix/flake.nix +20 -22
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/pyproject.toml +2 -2
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/qlty.toml +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/__init__.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/_utils.py +131 -0
- lib_cli_exit_tools-1.2.0/scripts/build.py +66 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/bump.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/bump_major.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/bump_minor.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/bump_patch.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/bump_version.py +121 -15
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/clean.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/dev.py +0 -0
- lib_cli_exit_tools-1.2.0/scripts/generate_nix_flake.py +191 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/install.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/menu.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/push.py +3 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/release.py +3 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/run_cli.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/test.py +144 -73
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/scripts/version_current.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/src/lib_cli_exit_tools/__init__.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/src/lib_cli_exit_tools/__init__conf__.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/src/lib_cli_exit_tools/__main__.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/src/lib_cli_exit_tools/cli.py +49 -1
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/src/lib_cli_exit_tools/lib_cli_exit_tools.py +1 -1
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/src/lib_cli_exit_tools/py.typed +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/tests/test_cli.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/tests/test_cli_extra.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/tests/test_exit_tools.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/tests/test_extend_cli.py +0 -0
- {lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/tests/test_lib_extra.py +0 -0
- lib_cli_exit_tools-1.1.1/scripts/build.py +0 -45
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -2,20 +2,17 @@
|
|
|
2
2
|
|
|
3
3
|
## On session start
|
|
4
4
|
|
|
5
|
-
|
|
6
|
-
- Read following files and keep their guidance in working memory:
|
|
7
|
-
- core_programming_solid.md
|
|
8
|
-
- bash_clean_architecture.md
|
|
9
|
-
- bash_clean_code.md
|
|
10
|
-
- bash_small_functions.md
|
|
11
|
-
- python_solid_architecture_enforcer.md
|
|
12
|
-
- python_clean_architecture.md/media/srv-main-softdev/projects/libs/bitranox_template_py_cli
|
|
13
|
-
- python_clean_code.md
|
|
14
|
-
- python_small_functions_style.md
|
|
15
|
-
- python_libraries_to_use.md
|
|
16
|
-
- python_structure_template.md
|
|
17
|
-
- self_documenting.md
|
|
5
|
+
Review the system prompt files in `/media/srv-main/softdev/*/projects/softwarestack/systemprompts` and keep their guidance in working memory:
|
|
18
6
|
|
|
7
|
+
- core_programming_solid.md
|
|
8
|
+
- python_solid_architecture_enforcer.md
|
|
9
|
+
- python_clean_architecture.md
|
|
10
|
+
- python_clean_code.md
|
|
11
|
+
- python_small_functions_style.md
|
|
12
|
+
- python_libraries_to_use.md
|
|
13
|
+
- python_structure_template.md
|
|
14
|
+
- self_documenting.md
|
|
15
|
+
- self_documenting_template.md
|
|
19
16
|
|
|
20
17
|
always apply those Rules :
|
|
21
18
|
|
|
@@ -37,7 +34,6 @@ when writing or refracturing Python scripts, apply those Rules :
|
|
|
37
34
|
- python_libraries_to_use.md
|
|
38
35
|
- python_lib_structure_template.md
|
|
39
36
|
|
|
40
|
-
|
|
41
37
|
## Project Structure & Module Organization
|
|
42
38
|
|
|
43
39
|
- `src/lib_cli_exit_tools/`: Python package.
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: lib_cli_exit_tools
|
|
3
|
-
Version: 1.
|
|
3
|
+
Version: 1.2.0
|
|
4
4
|
Summary: CLI exit handling helpers: clean signals, exit codes, and error printing
|
|
5
5
|
Project-URL: Homepage, https://github.com/bitranox/lib_cli_exit_tools
|
|
6
6
|
Project-URL: Repository, https://github.com/bitranox/lib_cli_exit_tools.git
|
|
@@ -16,7 +16,7 @@ Classifier: Programming Language :: Python :: 3
|
|
|
16
16
|
Classifier: Programming Language :: Python :: 3 :: Only
|
|
17
17
|
Classifier: Typing :: Typed
|
|
18
18
|
Requires-Python: >=3.10
|
|
19
|
-
Requires-Dist: click>=
|
|
19
|
+
Requires-Dist: rich-click>=1.9.1
|
|
20
20
|
Provides-Extra: dev
|
|
21
21
|
Requires-Dist: build>=1.2; extra == 'dev'
|
|
22
22
|
Requires-Dist: codecov>=2.1.13; extra == 'dev'
|
|
File without changes
|
|
File without changes
|
|
@@ -167,6 +167,13 @@ The package is the core runtime library for handling CLI exits within the broade
|
|
|
167
167
|
**Output:** Dict passed to Click context settings.
|
|
168
168
|
**Location:** `lib_cli_exit_tools/cli.py`
|
|
169
169
|
|
|
170
|
+
#### Function: _configure_rich_click_output
|
|
171
|
+
**Purpose:** Neutralise rich-click styling when stdout is a pipe or uses non-UTF encodings so help text stays ASCII-safe on Windows CI.
|
|
172
|
+
**Input:** None; inspects the active stdout stream via `click.get_text_stream`.
|
|
173
|
+
**Output:** Adjusts rich-click globals (forced terminal flag, colour system, panel boxes) to avoid emitting characters that legacy encodings cannot represent.
|
|
174
|
+
**Location:** `lib_cli_exit_tools/cli.py`
|
|
175
|
+
|
|
176
|
+
|
|
170
177
|
#### Function: cli
|
|
171
178
|
**Purpose:** Root Click group that stores global options (currently `--traceback`) and updates library configuration.
|
|
172
179
|
**Input:** Click `Context`, boolean `traceback` option.
|
|
File without changes
|
|
File without changes
|
{lib_cli_exit_tools-1.1.1 → lib_cli_exit_tools-1.2.0}/packaging/brew/Formula/lib-cli-exit-tools.rb
RENAMED
|
@@ -3,7 +3,7 @@ class LibCliExitTools < Formula
|
|
|
3
3
|
|
|
4
4
|
desc "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
homepage "https://github.com/bitranox/lib_cli_exit_tools"
|
|
6
|
-
url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.
|
|
6
|
+
url "https://github.com/bitranox/lib_cli_exit_tools/archive/refs/tags/v1.2.0.tar.gz"
|
|
7
7
|
k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
8
8
|
license "MIT"
|
|
9
9
|
|
|
@@ -11,10 +11,15 @@ k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
|
11
11
|
|
|
12
12
|
# Vendor Python deps (fill versions/sha256 for an actual formula)
|
|
13
13
|
resource "click" do
|
|
14
|
-
url "https://files.pythonhosted.org/packages/
|
|
14
|
+
url "https://files.pythonhosted.org/packages/46/61/de6cd827efad202d7057d93e0fed9294b96952e188f7384832791c7b2254/click-8.3.0.tar.gz"
|
|
15
15
|
k3cc316e7d0b09dd79e617cb3e91b14ee414b7e6695b108a326d9d3ccd08dd9"
|
|
16
16
|
end
|
|
17
17
|
|
|
18
|
+
resource "rich-click" do
|
|
19
|
+
url "https://files.pythonhosted.org/packages/29/c2/f08b5e7c1a33af8a115be640aa0796ba01c4732696da6d2254391376b314/rich_click-1.9.1.tar.gz"
|
|
20
|
+
sha256 "4f2620589d7287f86265432e6a909de4f281de909fe68d8c835fbba49265d268"
|
|
21
|
+
end
|
|
22
|
+
|
|
18
23
|
def install
|
|
19
24
|
virtualenv_install_with_resources
|
|
20
25
|
end
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{% set name = "lib_cli_exit_tools" %}
|
|
2
|
-
{% set version = "1.
|
|
2
|
+
{% set version = "1.2.0" %}
|
|
3
3
|
|
|
4
4
|
package:
|
|
5
5
|
name: {{ name|lower }}
|
|
@@ -28,7 +28,7 @@ requirements:
|
|
|
28
28
|
- hatchling >=1.25
|
|
29
29
|
run:
|
|
30
30
|
- python >=3.10
|
|
31
|
-
- click >=
|
|
31
|
+
- rich-click >=1.9.1
|
|
32
32
|
|
|
33
33
|
test:
|
|
34
34
|
commands:
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
description = "
|
|
2
|
+
description = "bitranox_template_py_cli Nix flake";
|
|
3
3
|
|
|
4
4
|
inputs.nixpkgs.url = "github:NixOS/nixpkgs/nixos-24.05";
|
|
5
5
|
inputs.flake-utils.url = "github:numtide/flake-utils";
|
|
@@ -11,15 +11,13 @@
|
|
|
11
11
|
lib = pkgs.lib;
|
|
12
12
|
pypkgs = pkgs.python310Packages;
|
|
13
13
|
|
|
14
|
-
# Vendor hatchling>=1.25 from PyPI (wheel) to satisfy PEP 517 build
|
|
15
14
|
hatchlingVendor = pypkgs.buildPythonPackage rec {
|
|
16
15
|
pname = "hatchling";
|
|
17
16
|
version = "1.25.0";
|
|
18
|
-
format = "wheel";
|
|
19
|
-
# Use explicit URL for py3 wheel; nixpkgs 24.05 fetchPypi may choose a py2.py3 path.
|
|
17
|
+
format = "wheel";
|
|
20
18
|
src = pkgs.fetchurl {
|
|
21
|
-
url = "https://files.pythonhosted.org/packages/py3/h/hatchling
|
|
22
|
-
hash = "sha256-tHlI5F1NlzA0WE3UyznBS2pwInzyh6t+wK15g0CKiCw
|
|
19
|
+
url = "https://files.pythonhosted.org/packages/py3/h/hatchling/hatchling-1.25.0-py3-none-any.whl";
|
|
20
|
+
hash = "sha256-tHlI5F1NlzA0WE3UyznBS2pwInzyh6t+wK15g0CKiCw";
|
|
23
21
|
};
|
|
24
22
|
propagatedBuildInputs = [
|
|
25
23
|
pypkgs.packaging
|
|
@@ -31,30 +29,30 @@
|
|
|
31
29
|
];
|
|
32
30
|
doCheck = false;
|
|
33
31
|
};
|
|
32
|
+
richClickVendor = pypkgs.buildPythonPackage rec {
|
|
33
|
+
pname = "rich-click";
|
|
34
|
+
version = "1.9.1";
|
|
35
|
+
format = "wheel";
|
|
36
|
+
src = pkgs.fetchurl {
|
|
37
|
+
url = "https://files.pythonhosted.org/packages/a8/77/e9144dcf68a0b3f3f4386986f97255c3d9f7c659be58bb7a5fe8f26f3efa/rich_click-1.9.1-py3-none-any.whl";
|
|
38
|
+
sha256 = "sha256-6mEUqeCBt9aMwHsxUHA5j4BvAbsODEnaVvEp5nKHeBc=";
|
|
39
|
+
};
|
|
40
|
+
doCheck = false;
|
|
41
|
+
};
|
|
42
|
+
|
|
34
43
|
in
|
|
35
44
|
{
|
|
36
45
|
packages.default = pypkgs.buildPythonPackage {
|
|
37
46
|
pname = "lib_cli_exit_tools";
|
|
38
|
-
version = "1.
|
|
47
|
+
version = "1.2.0";
|
|
39
48
|
pyproject = true;
|
|
40
|
-
# Build from the repository root (two levels up from packaging/nix)
|
|
41
49
|
src = ../..;
|
|
42
|
-
# For pinned releases, swap src for fetchFromGitHub with a rev/sha256.
|
|
43
|
-
# src = pkgs.fetchFromGitHub {
|
|
44
|
-
# owner = "bitranox";
|
|
45
|
-
# repo = "lib_cli_exit_tools";
|
|
46
|
-
# rev = "v1.1.1";
|
|
47
|
-
# sha256 = "<fill-me>";
|
|
48
|
-
# };
|
|
49
|
-
|
|
50
|
-
# Ensure PEP 517 backend is available at required version
|
|
51
|
-
# Ensure PEP 517 backend available at required version (>=1.25)
|
|
52
50
|
nativeBuildInputs = [ hatchlingVendor ];
|
|
53
|
-
propagatedBuildInputs = [
|
|
51
|
+
propagatedBuildInputs = [ richClickVendor ];
|
|
54
52
|
|
|
55
53
|
meta = with pkgs.lib; {
|
|
56
|
-
description = "
|
|
57
|
-
homepage = "https://github.com/bitranox/
|
|
54
|
+
description = "Rich-powered logging helpers for colorful terminal output";
|
|
55
|
+
homepage = "https://github.com/bitranox/bitranox_template_py_cli";
|
|
58
56
|
license = licenses.mit;
|
|
59
57
|
maintainers = [];
|
|
60
58
|
platforms = platforms.unix ++ platforms.darwin;
|
|
@@ -65,7 +63,7 @@
|
|
|
65
63
|
packages = [
|
|
66
64
|
pkgs.python310
|
|
67
65
|
hatchlingVendor
|
|
68
|
-
|
|
66
|
+
richClickVendor
|
|
69
67
|
pypkgs.pytest
|
|
70
68
|
pkgs.ruff
|
|
71
69
|
pkgs.nodejs
|
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
[project]
|
|
2
2
|
name = "lib_cli_exit_tools"
|
|
3
|
-
version = "1.
|
|
3
|
+
version = "1.2.0"
|
|
4
4
|
description = "CLI exit handling helpers: clean signals, exit codes, and error printing"
|
|
5
5
|
readme = "README.md"
|
|
6
6
|
requires-python = ">=3.10"
|
|
7
|
-
dependencies = ["click>=
|
|
7
|
+
dependencies = ["rich-click>=1.9.1"]
|
|
8
8
|
license = { text = "MIT" }
|
|
9
9
|
authors = [{ name = "bitranox", email = "bitranox@gmail.com" }]
|
|
10
10
|
keywords = ["cli", "exit", "signals", "errors", "click"]
|
|
File without changes
|
|
File without changes
|
|
@@ -1,9 +1,15 @@
|
|
|
1
1
|
from __future__ import annotations
|
|
2
2
|
|
|
3
|
+
import os
|
|
4
|
+
import platform
|
|
3
5
|
import re
|
|
4
6
|
import shlex
|
|
5
7
|
import subprocess
|
|
6
8
|
import sys
|
|
9
|
+
import tempfile
|
|
10
|
+
import hashlib
|
|
11
|
+
import json
|
|
12
|
+
from urllib.request import urlopen
|
|
7
13
|
from dataclasses import dataclass
|
|
8
14
|
from pathlib import Path
|
|
9
15
|
from subprocess import CompletedProcess
|
|
@@ -194,6 +200,95 @@ def read_version_from_pyproject(pyproject: Path = Path("pyproject.toml")) -> str
|
|
|
194
200
|
return match.group(1) if match else ""
|
|
195
201
|
|
|
196
202
|
|
|
203
|
+
def ensure_conda(auto_install: bool = True) -> bool:
|
|
204
|
+
"""Ensure the `conda` CLI is available, optionally bootstrapping Miniforge."""
|
|
205
|
+
|
|
206
|
+
if cmd_exists("conda"):
|
|
207
|
+
return True
|
|
208
|
+
|
|
209
|
+
if not auto_install:
|
|
210
|
+
return False
|
|
211
|
+
|
|
212
|
+
system = platform.system().lower()
|
|
213
|
+
machine = platform.machine().lower()
|
|
214
|
+
|
|
215
|
+
if system.startswith("linux"):
|
|
216
|
+
os_name = "Linux"
|
|
217
|
+
elif system == "darwin":
|
|
218
|
+
os_name = "MacOSX"
|
|
219
|
+
else:
|
|
220
|
+
return False
|
|
221
|
+
|
|
222
|
+
if machine in {"x86_64", "amd64"}:
|
|
223
|
+
arch = "x86_64"
|
|
224
|
+
elif machine in {"aarch64", "arm64"}:
|
|
225
|
+
arch = "aarch64" if os_name == "Linux" else "arm64"
|
|
226
|
+
else:
|
|
227
|
+
return False
|
|
228
|
+
|
|
229
|
+
filename = f"Miniforge3-{os_name}-{arch}.sh"
|
|
230
|
+
base = "https://github.com/conda-forge/miniforge/releases/latest/download/"
|
|
231
|
+
asset_url = base + filename
|
|
232
|
+
sums_url = base + "SHA256SUMS"
|
|
233
|
+
|
|
234
|
+
try:
|
|
235
|
+
with urlopen("https://api.github.com/repos/conda-forge/miniforge/releases/latest", timeout=10) as resp:
|
|
236
|
+
release = json.loads(resp.read().decode("utf-8"))
|
|
237
|
+
for asset in release.get("assets", []):
|
|
238
|
+
name = asset.get("name")
|
|
239
|
+
if name == filename:
|
|
240
|
+
asset_url = asset.get("browser_download_url", asset_url)
|
|
241
|
+
elif name == "SHA256SUMS":
|
|
242
|
+
sums_url = asset.get("browser_download_url", sums_url)
|
|
243
|
+
except Exception:
|
|
244
|
+
pass
|
|
245
|
+
|
|
246
|
+
conda_root = Path.home() / "miniforge3"
|
|
247
|
+
|
|
248
|
+
if not conda_root.exists():
|
|
249
|
+
attempts = 3
|
|
250
|
+
for _ in range(attempts):
|
|
251
|
+
try:
|
|
252
|
+
installer_bytes = urlopen(asset_url, timeout=30).read()
|
|
253
|
+
sums_text = urlopen(sums_url, timeout=30).read().decode("utf-8")
|
|
254
|
+
except Exception:
|
|
255
|
+
continue
|
|
256
|
+
|
|
257
|
+
expected_hash = None
|
|
258
|
+
for line in sums_text.splitlines():
|
|
259
|
+
line = line.strip()
|
|
260
|
+
if line.endswith(f" {filename}"):
|
|
261
|
+
parts = line.split()
|
|
262
|
+
if parts:
|
|
263
|
+
expected_hash = parts[0]
|
|
264
|
+
break
|
|
265
|
+
|
|
266
|
+
if not expected_hash:
|
|
267
|
+
continue
|
|
268
|
+
|
|
269
|
+
actual_hash = hashlib.sha256(installer_bytes).hexdigest()
|
|
270
|
+
if actual_hash != expected_hash:
|
|
271
|
+
continue
|
|
272
|
+
|
|
273
|
+
with tempfile.TemporaryDirectory() as tmp:
|
|
274
|
+
installer = Path(tmp) / filename
|
|
275
|
+
installer.write_bytes(installer_bytes)
|
|
276
|
+
installer.chmod(0o755)
|
|
277
|
+
install = run(["bash", str(installer), "-b"], check=False, capture=False)
|
|
278
|
+
if install.code == 0:
|
|
279
|
+
break
|
|
280
|
+
else:
|
|
281
|
+
return False
|
|
282
|
+
candidates = [conda_root / "condabin", conda_root / "bin"]
|
|
283
|
+
|
|
284
|
+
existing = os.environ.get("PATH", "").split(os.pathsep)
|
|
285
|
+
new_entries = [str(p) for p in candidates if p.exists()]
|
|
286
|
+
if new_entries:
|
|
287
|
+
os.environ["PATH"] = os.pathsep.join(new_entries + [p for p in existing if p])
|
|
288
|
+
|
|
289
|
+
return cmd_exists("conda")
|
|
290
|
+
|
|
291
|
+
|
|
197
292
|
def ensure_clean_git_tree() -> None:
|
|
198
293
|
dirty = subprocess.call(["bash", "-lc", "! git diff --quiet || ! git diff --cached --quiet"], stdout=subprocess.DEVNULL)
|
|
199
294
|
if dirty == 0:
|
|
@@ -241,6 +336,7 @@ def gh_release_edit(tag: str, title: str, body: str) -> None:
|
|
|
241
336
|
|
|
242
337
|
def sync_packaging() -> None:
|
|
243
338
|
run([sys.executable, "scripts/bump_version.py", "--sync-packaging"], check=False)
|
|
339
|
+
run([sys.executable, "scripts/generate_nix_flake.py"], check=False)
|
|
244
340
|
|
|
245
341
|
|
|
246
342
|
def bootstrap_dev() -> None:
|
|
@@ -253,3 +349,38 @@ def bootstrap_dev() -> None:
|
|
|
253
349
|
import_module("sqlite3")
|
|
254
350
|
except Exception:
|
|
255
351
|
run([sys.executable, "-m", "pip", "install", "pysqlite3-binary"], check=False)
|
|
352
|
+
|
|
353
|
+
|
|
354
|
+
def ensure_nix(auto_install: bool = True) -> bool:
|
|
355
|
+
"""Ensure the `nix` command is available, optionally bootstrapping it."""
|
|
356
|
+
|
|
357
|
+
if cmd_exists("nix"):
|
|
358
|
+
return True
|
|
359
|
+
|
|
360
|
+
if not auto_install:
|
|
361
|
+
return False
|
|
362
|
+
|
|
363
|
+
import os
|
|
364
|
+
import sys
|
|
365
|
+
|
|
366
|
+
platform = sys.platform
|
|
367
|
+
if not (platform.startswith("linux") or platform == "darwin"):
|
|
368
|
+
return False
|
|
369
|
+
|
|
370
|
+
# Install single-user Nix (no-daemon) for non-root environments.
|
|
371
|
+
install_cmd = "curl -L https://nixos.org/nix/install | sh -s -- --no-daemon"
|
|
372
|
+
run(["bash", "-lc", install_cmd], check=False, capture=False)
|
|
373
|
+
|
|
374
|
+
profile_dir = Path.home() / ".nix-profile"
|
|
375
|
+
profile_env = profile_dir / "etc" / "profile.d" / "nix.sh"
|
|
376
|
+
bin_dir = profile_dir / "bin"
|
|
377
|
+
|
|
378
|
+
if bin_dir.exists():
|
|
379
|
+
path_entries = os.environ.get("PATH", "").split(os.pathsep)
|
|
380
|
+
if str(bin_dir) not in path_entries:
|
|
381
|
+
os.environ["PATH"] = os.pathsep.join([str(bin_dir)] + [p for p in path_entries if p])
|
|
382
|
+
|
|
383
|
+
if profile_env.exists():
|
|
384
|
+
run(["bash", "-lc", f". {profile_env} >/dev/null 2>&1 && nix --version"], check=False, capture=False)
|
|
385
|
+
|
|
386
|
+
return cmd_exists("nix")
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
from __future__ import annotations
|
|
2
|
+
|
|
3
|
+
import sys
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
import click
|
|
7
|
+
|
|
8
|
+
try: # allow running as package module or stand-alone script
|
|
9
|
+
from ._utils import cmd_exists, ensure_conda, ensure_nix, get_project_metadata, run
|
|
10
|
+
except ImportError: # pragma: no cover - direct script execution path
|
|
11
|
+
sys.path.insert(0, str(Path(__file__).resolve().parents[1]))
|
|
12
|
+
from scripts._utils import cmd_exists, ensure_conda, ensure_nix, get_project_metadata, run
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
@click.command(help="Build wheel/sdist, optionally attempt conda/brew/nix builds if tools present")
|
|
16
|
+
@click.option("--conda/--no-conda", default=True, help="Attempt conda build if conda present")
|
|
17
|
+
@click.option("--brew/--no-brew", default=True, help="Attempt Homebrew build if brew present (macOS)")
|
|
18
|
+
@click.option("--nix/--no-nix", default=True, help="Attempt Nix build if nix present")
|
|
19
|
+
def main(conda: bool, brew: bool, nix: bool) -> None:
|
|
20
|
+
click.echo("[1/4] Building wheel/sdist via python -m build")
|
|
21
|
+
build_result = run(["python", "-m", "build"], check=False, capture=False)
|
|
22
|
+
build_msg = click.style("success", fg="green") if build_result.code == 0 else click.style("failed", fg="red")
|
|
23
|
+
click.echo(f"[build] {build_msg}")
|
|
24
|
+
if build_result.code != 0:
|
|
25
|
+
raise SystemExit(build_result.code)
|
|
26
|
+
|
|
27
|
+
project = get_project_metadata()
|
|
28
|
+
|
|
29
|
+
click.echo("[2/4] Attempting conda-build")
|
|
30
|
+
conda_msg: str
|
|
31
|
+
if conda:
|
|
32
|
+
if ensure_conda():
|
|
33
|
+
conda_cmd = ". $HOME/miniforge3/etc/profile.d/conda.sh >/dev/null 2>&1 || true; conda clean --all --yes >/dev/null 2>&1 || true; CONDA_USE_LOCAL=1 conda build packaging/conda/recipe"
|
|
34
|
+
conda_result = run(["bash", "-lc", conda_cmd], check=False, capture=False)
|
|
35
|
+
conda_msg = click.style("success", fg="green") if conda_result.code == 0 else click.style("failed", fg="red")
|
|
36
|
+
else:
|
|
37
|
+
conda_msg = click.style("skipped", fg="yellow")
|
|
38
|
+
else:
|
|
39
|
+
conda_msg = click.style("skipped", fg="yellow")
|
|
40
|
+
click.echo(f"[conda] {conda_msg}")
|
|
41
|
+
|
|
42
|
+
click.echo("[3/4] Attempting Homebrew build/install from local formula")
|
|
43
|
+
brew_msg: str
|
|
44
|
+
if brew and cmd_exists("brew"):
|
|
45
|
+
brew_result = run(["bash", "-lc", f"brew install --build-from-source {project.brew_formula_path}"], check=False, capture=False)
|
|
46
|
+
brew_msg = click.style("success", fg="green") if brew_result.code == 0 else click.style("failed", fg="red")
|
|
47
|
+
else:
|
|
48
|
+
brew_msg = click.style("skipped", fg="yellow")
|
|
49
|
+
click.echo(f"[brew] {brew_msg}")
|
|
50
|
+
|
|
51
|
+
click.echo("[4/4] Attempting Nix flake build")
|
|
52
|
+
nix_msg: str
|
|
53
|
+
if nix:
|
|
54
|
+
if ensure_nix():
|
|
55
|
+
nix_cmd = ". $HOME/.nix-profile/etc/profile.d/nix.sh >/dev/null 2>&1 || true; nix build --extra-experimental-features 'nix-command flakes' ./packaging/nix#default -L"
|
|
56
|
+
nix_result = run(["bash", "-lc", nix_cmd], check=False, capture=False)
|
|
57
|
+
nix_msg = click.style("success", fg="green") if nix_result.code == 0 else click.style("failed", fg="red")
|
|
58
|
+
else:
|
|
59
|
+
nix_msg = click.style("skipped", fg="yellow")
|
|
60
|
+
else:
|
|
61
|
+
nix_msg = click.style("skipped", fg="yellow")
|
|
62
|
+
click.echo(f"[nix] {nix_msg}")
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
if __name__ == "__main__":
|
|
66
|
+
main()
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|