env-module-manager 0.7.0__tar.gz → 0.8.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.
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/PKG-INFO +12 -1
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/README.md +11 -0
- env_module_manager-0.8.0/docs/collective-environments.md +186 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/python-tools.md +70 -3
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/reference.md +9 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/module_manager/__init__.py +1 -1
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/module_manager/cli.py +49 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/module_manager/deploy.py +116 -1
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/pyproject.toml +1 -1
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/tests/test_cli.py +34 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/tests/test_modulefile.py +64 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/uv.lock +1 -1
- env_module_manager-0.7.0/docs/collective-environments.md +0 -85
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/.github/workflows/publish.yml +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/.github/workflows/tag.yml +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/.github/workflows/test.yml +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/.gitignore +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/.python-version +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/.readthedocs.yaml +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/AGENTS.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/CONTRIBUTING.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/LICENSE.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/configuration.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/development.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/environment-modules.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/index.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/installation.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/rust-tools.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/shell-tools.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/stylesheets/extra.css +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/docs/uninstall.md +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/example.bash +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/mkdocs.yml +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/module_manager/config.py +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/module_manager/modulefile.py +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/tests/test_config.py +0 -0
- {env_module_manager-0.7.0 → env_module_manager-0.8.0}/tests/test_integration.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: env-module-manager
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.8.0
|
|
4
4
|
Summary: Deploy Python, Rust, shell, and collective CLI environments behind GNU environment modulefiles
|
|
5
5
|
Project-URL: Homepage, https://github.com/ejb90/module-manager
|
|
6
6
|
Project-URL: Documentation, https://module-manager.readthedocs.io/
|
|
@@ -73,6 +73,7 @@ Deploy a Python CLI with `uv tool install`:
|
|
|
73
73
|
```sh
|
|
74
74
|
module-manager deploy-python ruff 0.8.0 \
|
|
75
75
|
--package 'ruff==0.8.0' \
|
|
76
|
+
--default-index https://packages.example/simple \
|
|
76
77
|
--constraints /prod/constraints/runtime.txt \
|
|
77
78
|
--prefix /prod/tools \
|
|
78
79
|
--module-root /prod/modulefiles \
|
|
@@ -94,6 +95,16 @@ Generate a constraints file for a Python CLI:
|
|
|
94
95
|
module-manager auto-constraints ruff==0.8.0 --output constraints.txt
|
|
95
96
|
```
|
|
96
97
|
|
|
98
|
+
Deploy from a direct wheel or Git URL:
|
|
99
|
+
|
|
100
|
+
```sh
|
|
101
|
+
module-manager deploy-python my-tool 1.0.0 \
|
|
102
|
+
--package 'my-tool @ file:///prod/wheels/my_tool-1.0.0-py3-none-any.whl' \
|
|
103
|
+
--prefix /prod/tools \
|
|
104
|
+
--module-root /prod/modulefiles \
|
|
105
|
+
--execute-install
|
|
106
|
+
```
|
|
107
|
+
|
|
97
108
|
Deploy a shell script:
|
|
98
109
|
|
|
99
110
|
```sh
|
|
@@ -41,6 +41,7 @@ Deploy a Python CLI with `uv tool install`:
|
|
|
41
41
|
```sh
|
|
42
42
|
module-manager deploy-python ruff 0.8.0 \
|
|
43
43
|
--package 'ruff==0.8.0' \
|
|
44
|
+
--default-index https://packages.example/simple \
|
|
44
45
|
--constraints /prod/constraints/runtime.txt \
|
|
45
46
|
--prefix /prod/tools \
|
|
46
47
|
--module-root /prod/modulefiles \
|
|
@@ -62,6 +63,16 @@ Generate a constraints file for a Python CLI:
|
|
|
62
63
|
module-manager auto-constraints ruff==0.8.0 --output constraints.txt
|
|
63
64
|
```
|
|
64
65
|
|
|
66
|
+
Deploy from a direct wheel or Git URL:
|
|
67
|
+
|
|
68
|
+
```sh
|
|
69
|
+
module-manager deploy-python my-tool 1.0.0 \
|
|
70
|
+
--package 'my-tool @ file:///prod/wheels/my_tool-1.0.0-py3-none-any.whl' \
|
|
71
|
+
--prefix /prod/tools \
|
|
72
|
+
--module-root /prod/modulefiles \
|
|
73
|
+
--execute-install
|
|
74
|
+
```
|
|
75
|
+
|
|
65
76
|
Deploy a shell script:
|
|
66
77
|
|
|
67
78
|
```sh
|
|
@@ -0,0 +1,186 @@
|
|
|
1
|
+
# Collective Environments
|
|
2
|
+
|
|
3
|
+
Collective environments package several tools behind one modulefile. Use them
|
|
4
|
+
when a shared workflow needs Python CLIs, Rust binaries, and shell scripts to
|
|
5
|
+
appear together from one `module load`.
|
|
6
|
+
|
|
7
|
+
Create a TOML manifest:
|
|
8
|
+
|
|
9
|
+
```toml
|
|
10
|
+
name = "dev-tools"
|
|
11
|
+
version = "2026.05"
|
|
12
|
+
description = "Shared developer command-line tools"
|
|
13
|
+
prefix = "/prod/tools"
|
|
14
|
+
module_root = "/prod/modulefiles"
|
|
15
|
+
default = true
|
|
16
|
+
|
|
17
|
+
[[tools]]
|
|
18
|
+
type = "python"
|
|
19
|
+
name = "ruff"
|
|
20
|
+
version = "0.8.0"
|
|
21
|
+
package = "ruff==0.8.0"
|
|
22
|
+
python = "3.12"
|
|
23
|
+
indexes = ["https://packages.example/simple"]
|
|
24
|
+
default_index = "https://packages.example/simple"
|
|
25
|
+
find_links = ["/prod/wheels"]
|
|
26
|
+
no_index = false
|
|
27
|
+
index_strategy = "first-index"
|
|
28
|
+
constraints = ["/prod/constraints/runtime.txt"]
|
|
29
|
+
no_cache = false
|
|
30
|
+
refresh = false
|
|
31
|
+
refresh_packages = ["ruff"]
|
|
32
|
+
force = false
|
|
33
|
+
reinstall = false
|
|
34
|
+
uv_config_file = "/prod/config/uv.toml"
|
|
35
|
+
|
|
36
|
+
[[tools]]
|
|
37
|
+
type = "rust"
|
|
38
|
+
name = "ripgrep"
|
|
39
|
+
version = "14.1.1"
|
|
40
|
+
binary = "./target/release/rg"
|
|
41
|
+
|
|
42
|
+
[[tools]]
|
|
43
|
+
type = "script"
|
|
44
|
+
name = "lab-helper"
|
|
45
|
+
version = "1.0.0"
|
|
46
|
+
script = "./scripts/lab-helper"
|
|
47
|
+
```
|
|
48
|
+
|
|
49
|
+
Deploy the environment:
|
|
50
|
+
|
|
51
|
+
```sh
|
|
52
|
+
module-manager deploy-env --file dev-tools.toml
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
This writes one shared environment:
|
|
56
|
+
|
|
57
|
+
```text
|
|
58
|
+
/prod/tools/dev-tools/2026.05/bin
|
|
59
|
+
/prod/modulefiles/dev-tools/2026.05
|
|
60
|
+
```
|
|
61
|
+
|
|
62
|
+
The generated modulefile prepends the shared `bin` directory to `PATH`, so all
|
|
63
|
+
tools in the manifest are available after:
|
|
64
|
+
|
|
65
|
+
```sh
|
|
66
|
+
module use /prod/modulefiles
|
|
67
|
+
module load dev-tools
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
Relative `binary` and `script` paths are resolved relative to the manifest file.
|
|
71
|
+
Relative Python `constraints` paths are resolved the same way.
|
|
72
|
+
|
|
73
|
+
## Multiple Python Tools
|
|
74
|
+
|
|
75
|
+
A collective environment can install several Python CLIs into the same shared
|
|
76
|
+
`bin` directory. Each Python tool can use its own package spec and uv resolver
|
|
77
|
+
options:
|
|
78
|
+
|
|
79
|
+
```toml
|
|
80
|
+
name = "python-dev-tools"
|
|
81
|
+
version = "2026.05"
|
|
82
|
+
prefix = "/prod/tools"
|
|
83
|
+
module_root = "/prod/modulefiles"
|
|
84
|
+
|
|
85
|
+
[[tools]]
|
|
86
|
+
type = "python"
|
|
87
|
+
name = "ruff"
|
|
88
|
+
version = "0.8.0"
|
|
89
|
+
package = "ruff==0.8.0"
|
|
90
|
+
python = "3.12"
|
|
91
|
+
constraints = ["constraints.txt"]
|
|
92
|
+
|
|
93
|
+
[[tools]]
|
|
94
|
+
type = "python"
|
|
95
|
+
name = "gitconductor"
|
|
96
|
+
version = "0.7.0"
|
|
97
|
+
package = "gitconductor==0.7.0"
|
|
98
|
+
default_index = "https://packages.example/simple"
|
|
99
|
+
constraints = ["constraints.txt"]
|
|
100
|
+
refresh_packages = ["gitconductor"]
|
|
101
|
+
|
|
102
|
+
[[tools]]
|
|
103
|
+
type = "python"
|
|
104
|
+
name = "internal-review"
|
|
105
|
+
version = "1.4.2"
|
|
106
|
+
package = "internal-review @ file:///prod/wheels/internal_review-1.4.2-py3-none-any.whl"
|
|
107
|
+
find_links = ["/prod/wheels"]
|
|
108
|
+
no_index = true
|
|
109
|
+
```
|
|
110
|
+
|
|
111
|
+
Deploy it with:
|
|
112
|
+
|
|
113
|
+
```sh
|
|
114
|
+
module-manager deploy-env --file python-dev-tools.toml
|
|
115
|
+
```
|
|
116
|
+
|
|
117
|
+
After `module load python-dev-tools`, the `ruff`, `gitconductor`, and
|
|
118
|
+
`internal-review` executables are available from one module.
|
|
119
|
+
|
|
120
|
+
## Python Tools and Bash Scripts
|
|
121
|
+
|
|
122
|
+
Use script entries for Bash helpers that should live alongside Python CLIs:
|
|
123
|
+
|
|
124
|
+
```toml
|
|
125
|
+
name = "release-tools"
|
|
126
|
+
version = "2026.05"
|
|
127
|
+
description = "Release automation helpers"
|
|
128
|
+
prefix = "/prod/tools"
|
|
129
|
+
module_root = "/prod/modulefiles"
|
|
130
|
+
|
|
131
|
+
[[tools]]
|
|
132
|
+
type = "python"
|
|
133
|
+
name = "gitconductor"
|
|
134
|
+
version = "0.7.0"
|
|
135
|
+
package = "gitconductor==0.7.0"
|
|
136
|
+
constraints = ["constraints.txt"]
|
|
137
|
+
|
|
138
|
+
[[tools]]
|
|
139
|
+
type = "python"
|
|
140
|
+
name = "changelog-builder"
|
|
141
|
+
version = "2.1.0"
|
|
142
|
+
package = "changelog-builder @ git+https://github.com/org/changelog-builder.git@v2.1.0"
|
|
143
|
+
|
|
144
|
+
[[tools]]
|
|
145
|
+
type = "script"
|
|
146
|
+
name = "release-check"
|
|
147
|
+
version = "1.0.0"
|
|
148
|
+
script = "./scripts/release-check.sh"
|
|
149
|
+
|
|
150
|
+
[[tools]]
|
|
151
|
+
type = "script"
|
|
152
|
+
name = "publish-docs"
|
|
153
|
+
version = "1.0.0"
|
|
154
|
+
script = "./scripts/publish-docs.sh"
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
Deploy it with:
|
|
158
|
+
|
|
159
|
+
```sh
|
|
160
|
+
module-manager deploy-env --file release-tools.toml
|
|
161
|
+
```
|
|
162
|
+
|
|
163
|
+
The script files are copied into the shared environment `bin` directory and
|
|
164
|
+
marked executable, so they appear on `PATH` next to the Python entry points.
|
|
165
|
+
|
|
166
|
+
## Preview
|
|
167
|
+
|
|
168
|
+
Use `--dry-run` to preview the install, copy, and modulefile actions without
|
|
169
|
+
creating files:
|
|
170
|
+
|
|
171
|
+
```sh
|
|
172
|
+
module-manager deploy-env --file dev-tools.toml --dry-run
|
|
173
|
+
```
|
|
174
|
+
|
|
175
|
+
## Overrides
|
|
176
|
+
|
|
177
|
+
The manifest can define `prefix`, `module_root`, and `default`, but CLI options
|
|
178
|
+
can override them:
|
|
179
|
+
|
|
180
|
+
```sh
|
|
181
|
+
module-manager deploy-env \
|
|
182
|
+
--file dev-tools.toml \
|
|
183
|
+
--prefix /scratch/tools \
|
|
184
|
+
--module-root /scratch/modulefiles \
|
|
185
|
+
--no-default
|
|
186
|
+
```
|
|
@@ -34,20 +34,37 @@ unchanged.
|
|
|
34
34
|
```sh
|
|
35
35
|
module-manager deploy-python gitconductor 0.1.0 \
|
|
36
36
|
--package gitconductor==0.1.0 \
|
|
37
|
+
--default-index https://packages.example/simple \
|
|
37
38
|
--index https://packages.example/simple \
|
|
38
39
|
--prefix /prod/tools \
|
|
39
40
|
--module-root /prod/modulefiles \
|
|
40
41
|
--execute-install
|
|
41
42
|
```
|
|
42
43
|
|
|
43
|
-
`--index` may be used more than once.
|
|
44
|
+
`--index` may be used more than once. Use `--default-index` to replace uv's
|
|
45
|
+
default package index, or `--no-index` when resolution should use only direct
|
|
46
|
+
URLs and `--find-links` locations.
|
|
44
47
|
|
|
45
48
|
## Deploy from a Wheelhouse
|
|
46
49
|
|
|
50
|
+
Use `--find-links` when wheels are staged in a directory or simple HTML page:
|
|
51
|
+
|
|
52
|
+
```sh
|
|
53
|
+
module-manager deploy-python gitconductor 0.1.0 \
|
|
54
|
+
--package gitconductor==0.1.0 \
|
|
55
|
+
--find-links /prod/wheels \
|
|
56
|
+
--prefix /prod/tools \
|
|
57
|
+
--module-root /prod/modulefiles \
|
|
58
|
+
--execute-install
|
|
59
|
+
```
|
|
60
|
+
|
|
61
|
+
Add `--no-index` to make the install fully wheelhouse-only:
|
|
62
|
+
|
|
47
63
|
```sh
|
|
48
64
|
module-manager deploy-python gitconductor 0.1.0 \
|
|
49
65
|
--package gitconductor==0.1.0 \
|
|
50
66
|
--find-links /prod/wheels \
|
|
67
|
+
--no-index \
|
|
51
68
|
--prefix /prod/tools \
|
|
52
69
|
--module-root /prod/modulefiles \
|
|
53
70
|
--execute-install
|
|
@@ -55,6 +72,28 @@ module-manager deploy-python gitconductor 0.1.0 \
|
|
|
55
72
|
|
|
56
73
|
`--find-links` may be used more than once.
|
|
57
74
|
|
|
75
|
+
## Deploy from a Wheel URL
|
|
76
|
+
|
|
77
|
+
Pass a direct wheel URL or file URL as the package spec:
|
|
78
|
+
|
|
79
|
+
```sh
|
|
80
|
+
module-manager deploy-python gitconductor 0.1.0 \
|
|
81
|
+
--package 'gitconductor @ https://packages.example/files/gitconductor-0.1.0-py3-none-any.whl' \
|
|
82
|
+
--prefix /prod/tools \
|
|
83
|
+
--module-root /prod/modulefiles \
|
|
84
|
+
--execute-install
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
For a local wheel file:
|
|
88
|
+
|
|
89
|
+
```sh
|
|
90
|
+
module-manager deploy-python gitconductor 0.1.0 \
|
|
91
|
+
--package 'gitconductor @ file:///prod/wheels/gitconductor-0.1.0-py3-none-any.whl' \
|
|
92
|
+
--prefix /prod/tools \
|
|
93
|
+
--module-root /prod/modulefiles \
|
|
94
|
+
--execute-install
|
|
95
|
+
```
|
|
96
|
+
|
|
58
97
|
## Use Constraint Files
|
|
59
98
|
|
|
60
99
|
Pass requirement constraint files to `uv tool install`:
|
|
@@ -70,6 +109,24 @@ module-manager deploy-python gitconductor 0.1.0 \
|
|
|
70
109
|
|
|
71
110
|
`--constraints` may be used more than once.
|
|
72
111
|
|
|
112
|
+
## Cache and Refresh Controls
|
|
113
|
+
|
|
114
|
+
Pass uv cache controls through when you need fresh index or package metadata:
|
|
115
|
+
|
|
116
|
+
```sh
|
|
117
|
+
module-manager deploy-python gitconductor 0.1.0 \
|
|
118
|
+
--package gitconductor==0.1.0 \
|
|
119
|
+
--refresh \
|
|
120
|
+
--refresh-package gitconductor \
|
|
121
|
+
--no-cache \
|
|
122
|
+
--prefix /prod/tools \
|
|
123
|
+
--module-root /prod/modulefiles \
|
|
124
|
+
--execute-install
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
Use `--force` or `--reinstall` when re-running an install into an existing
|
|
128
|
+
versioned tool environment.
|
|
129
|
+
|
|
73
130
|
## Generate Constraint Files
|
|
74
131
|
|
|
75
132
|
Use `auto-constraints` to compile a package and its dependencies into a
|
|
@@ -111,8 +168,8 @@ through the environment.
|
|
|
111
168
|
|
|
112
169
|
## Deploy from VCS
|
|
113
170
|
|
|
114
|
-
`--package` is passed directly to `uv tool install`, so
|
|
115
|
-
supported.
|
|
171
|
+
`--package` is passed directly to `uv tool install`, so Git package specs are
|
|
172
|
+
supported. Pin a tag, branch, or commit to keep deployments reproducible.
|
|
116
173
|
|
|
117
174
|
```sh
|
|
118
175
|
module-manager deploy-python mytool 1.0.0 \
|
|
@@ -122,6 +179,16 @@ module-manager deploy-python mytool 1.0.0 \
|
|
|
122
179
|
--execute-install
|
|
123
180
|
```
|
|
124
181
|
|
|
182
|
+
For an SSH Git URL:
|
|
183
|
+
|
|
184
|
+
```sh
|
|
185
|
+
module-manager deploy-python mytool 1.0.0 \
|
|
186
|
+
--package 'git+ssh://git@github.com/org/repo.git@v1.0.0' \
|
|
187
|
+
--prefix /prod/tools \
|
|
188
|
+
--module-root /prod/modulefiles \
|
|
189
|
+
--execute-install
|
|
190
|
+
```
|
|
191
|
+
|
|
125
192
|
For a package in a repository subdirectory:
|
|
126
193
|
|
|
127
194
|
```sh
|
|
@@ -49,8 +49,17 @@ module-manager deploy-python NAME VERSION \
|
|
|
49
49
|
--package PACKAGE \
|
|
50
50
|
[--python PYTHON] \
|
|
51
51
|
[--index URL] \
|
|
52
|
+
[--default-index URL] \
|
|
52
53
|
[--find-links PATH_OR_URL] \
|
|
54
|
+
[--no-index] \
|
|
55
|
+
[--index-strategy STRATEGY] \
|
|
56
|
+
[--keyring-provider PROVIDER] \
|
|
53
57
|
[--constraints PATH] \
|
|
58
|
+
[--no-cache] \
|
|
59
|
+
[--refresh] \
|
|
60
|
+
[--refresh-package PACKAGE] \
|
|
61
|
+
[--force] \
|
|
62
|
+
[--reinstall] \
|
|
54
63
|
[--uv-config-file PATH] \
|
|
55
64
|
[--prefix PATH] \
|
|
56
65
|
[--module-root PATH] \
|
|
@@ -203,18 +203,40 @@ def main(ctx: click.Context, config: Path | None) -> None:
|
|
|
203
203
|
multiple=True,
|
|
204
204
|
help="Additional package index URL passed to uv. May be used more than once.",
|
|
205
205
|
)
|
|
206
|
+
@click.option("--default-index", help="Default package index URL passed to uv.")
|
|
206
207
|
@click.option(
|
|
207
208
|
"--find-links",
|
|
208
209
|
"find_links",
|
|
209
210
|
multiple=True,
|
|
210
211
|
help="Directory or HTML page of packages passed to uv. May be used more than once.",
|
|
211
212
|
)
|
|
213
|
+
@click.option("--no-index", is_flag=True, help="Ignore registry indexes and use direct URLs or find-links.")
|
|
214
|
+
@click.option(
|
|
215
|
+
"--index-strategy",
|
|
216
|
+
type=click.Choice(["first-index", "unsafe-first-match", "unsafe-best-match"]),
|
|
217
|
+
help="Package index strategy passed to uv.",
|
|
218
|
+
)
|
|
219
|
+
@click.option(
|
|
220
|
+
"--keyring-provider",
|
|
221
|
+
type=click.Choice(["disabled", "subprocess"]),
|
|
222
|
+
help="Keyring provider passed to uv.",
|
|
223
|
+
)
|
|
212
224
|
@click.option(
|
|
213
225
|
"--constraints",
|
|
214
226
|
"constraints",
|
|
215
227
|
multiple=True,
|
|
216
228
|
help="Requirements constraint file passed to uv. May be used more than once.",
|
|
217
229
|
)
|
|
230
|
+
@click.option("--no-cache", is_flag=True, help="Avoid reading from or writing to the uv cache.")
|
|
231
|
+
@click.option("--refresh", is_flag=True, help="Refresh all uv cached data.")
|
|
232
|
+
@click.option(
|
|
233
|
+
"--refresh-package",
|
|
234
|
+
"refresh_packages",
|
|
235
|
+
multiple=True,
|
|
236
|
+
help="Refresh cached data for a package. May be used more than once.",
|
|
237
|
+
)
|
|
238
|
+
@click.option("--force", is_flag=True, help="Replace existing executable entries.")
|
|
239
|
+
@click.option("--reinstall", is_flag=True, help="Reinstall all packages in the tool environment.")
|
|
218
240
|
@click.option(
|
|
219
241
|
"--uv-config-file",
|
|
220
242
|
type=PATH,
|
|
@@ -238,8 +260,17 @@ def deploy_python(
|
|
|
238
260
|
package: str,
|
|
239
261
|
python: str | None,
|
|
240
262
|
indexes: tuple[str, ...],
|
|
263
|
+
default_index: str | None,
|
|
241
264
|
find_links: tuple[str, ...],
|
|
265
|
+
no_index: bool,
|
|
266
|
+
index_strategy: str | None,
|
|
267
|
+
keyring_provider: str | None,
|
|
242
268
|
constraints: tuple[str, ...],
|
|
269
|
+
no_cache: bool,
|
|
270
|
+
refresh: bool,
|
|
271
|
+
refresh_packages: tuple[str, ...],
|
|
272
|
+
force: bool,
|
|
273
|
+
reinstall: bool,
|
|
243
274
|
uv_config_file: Path | None,
|
|
244
275
|
execute_install: bool,
|
|
245
276
|
) -> None:
|
|
@@ -257,8 +288,17 @@ def deploy_python(
|
|
|
257
288
|
package: Package spec passed to `uv tool install`.
|
|
258
289
|
python: Optional Python interpreter or version passed to uv.
|
|
259
290
|
indexes: Additional package index URLs passed to uv.
|
|
291
|
+
default_index: Default package index URL passed to uv.
|
|
260
292
|
find_links: Wheelhouse directories or HTML package pages passed to uv.
|
|
293
|
+
no_index: Whether uv should ignore registry indexes.
|
|
294
|
+
index_strategy: Package index strategy passed to uv.
|
|
295
|
+
keyring_provider: Keyring provider passed to uv.
|
|
261
296
|
constraints: Requirements constraint files passed to uv.
|
|
297
|
+
no_cache: Whether uv should avoid reading from or writing to cache.
|
|
298
|
+
refresh: Whether uv should refresh cached data.
|
|
299
|
+
refresh_packages: Packages whose cached data uv should refresh.
|
|
300
|
+
force: Whether uv should replace existing executable entries.
|
|
301
|
+
reinstall: Whether uv should reinstall all packages.
|
|
262
302
|
uv_config_file: Optional uv configuration file passed to `uv tool`.
|
|
263
303
|
execute_install: Whether to run `uv tool install` immediately.
|
|
264
304
|
|
|
@@ -282,8 +322,17 @@ def deploy_python(
|
|
|
282
322
|
homepage=homepage,
|
|
283
323
|
python=python,
|
|
284
324
|
indexes=indexes or config.indexes,
|
|
325
|
+
default_index=default_index,
|
|
285
326
|
find_links=find_links or config.find_links,
|
|
327
|
+
no_index=no_index,
|
|
328
|
+
index_strategy=index_strategy,
|
|
329
|
+
keyring_provider=keyring_provider,
|
|
286
330
|
constraints=constraints,
|
|
331
|
+
no_cache=no_cache,
|
|
332
|
+
refresh=refresh,
|
|
333
|
+
refresh_packages=refresh_packages,
|
|
334
|
+
force=force,
|
|
335
|
+
reinstall=reinstall,
|
|
287
336
|
uv_config_file=(uv_config_file.expanduser() if uv_config_file else config.uv_config_file),
|
|
288
337
|
execute_install=execute_install,
|
|
289
338
|
make_default=make_default,
|
|
@@ -72,8 +72,17 @@ class EnvironmentToolSpec:
|
|
|
72
72
|
script: Shell script path to copy into the shared `bin` directory.
|
|
73
73
|
python: Optional Python interpreter or version passed to uv.
|
|
74
74
|
indexes: Additional package index URLs passed to uv.
|
|
75
|
+
default_index: Default package index URL passed to uv.
|
|
75
76
|
find_links: Wheelhouse directories or HTML package pages passed to uv.
|
|
77
|
+
no_index: Whether uv should ignore registry indexes.
|
|
78
|
+
index_strategy: Package index strategy passed to uv.
|
|
79
|
+
keyring_provider: Keyring provider passed to uv.
|
|
76
80
|
constraints: Constraint requirement files passed to uv.
|
|
81
|
+
no_cache: Whether uv should avoid reading from or writing to cache.
|
|
82
|
+
refresh: Whether uv should refresh cached data.
|
|
83
|
+
refresh_packages: Packages whose cached data uv should refresh.
|
|
84
|
+
force: Whether uv should replace existing executable entries.
|
|
85
|
+
reinstall: Whether uv should reinstall all packages.
|
|
77
86
|
description: Optional tool description.
|
|
78
87
|
homepage: Optional upstream homepage.
|
|
79
88
|
"""
|
|
@@ -87,8 +96,17 @@ class EnvironmentToolSpec:
|
|
|
87
96
|
script: Path | None = None
|
|
88
97
|
python: str | None = None
|
|
89
98
|
indexes: tuple[str, ...] = ()
|
|
99
|
+
default_index: str | None = None
|
|
90
100
|
find_links: tuple[str, ...] = ()
|
|
101
|
+
no_index: bool = False
|
|
102
|
+
index_strategy: str | None = None
|
|
103
|
+
keyring_provider: str | None = None
|
|
91
104
|
constraints: tuple[str, ...] = ()
|
|
105
|
+
no_cache: bool = False
|
|
106
|
+
refresh: bool = False
|
|
107
|
+
refresh_packages: tuple[str, ...] = ()
|
|
108
|
+
force: bool = False
|
|
109
|
+
reinstall: bool = False
|
|
92
110
|
description: str | None = None
|
|
93
111
|
homepage: str | None = None
|
|
94
112
|
|
|
@@ -176,8 +194,17 @@ def uv_install_command(
|
|
|
176
194
|
package: str,
|
|
177
195
|
python: str | None = None,
|
|
178
196
|
indexes: tuple[str, ...] = (),
|
|
197
|
+
default_index: str | None = None,
|
|
179
198
|
find_links: tuple[str, ...] = (),
|
|
199
|
+
no_index: bool = False,
|
|
200
|
+
index_strategy: str | None = None,
|
|
201
|
+
keyring_provider: str | None = None,
|
|
180
202
|
constraints: tuple[str, ...] = (),
|
|
203
|
+
no_cache: bool = False,
|
|
204
|
+
refresh: bool = False,
|
|
205
|
+
refresh_packages: tuple[str, ...] = (),
|
|
206
|
+
force: bool = False,
|
|
207
|
+
reinstall: bool = False,
|
|
181
208
|
uv_config_file: Path | None = None,
|
|
182
209
|
) -> list[str]:
|
|
183
210
|
"""Build the uv command used to install a Python CLI tool.
|
|
@@ -186,8 +213,17 @@ def uv_install_command(
|
|
|
186
213
|
package: Package spec passed to `uv tool install`.
|
|
187
214
|
python: Optional Python interpreter or version passed to uv.
|
|
188
215
|
indexes: Additional package index URLs.
|
|
216
|
+
default_index: Default package index URL.
|
|
189
217
|
find_links: Wheelhouse directories or HTML package pages.
|
|
218
|
+
no_index: Whether to ignore registry indexes.
|
|
219
|
+
index_strategy: Package index strategy.
|
|
220
|
+
keyring_provider: Keyring provider.
|
|
190
221
|
constraints: Constraint requirement files.
|
|
222
|
+
no_cache: Whether to avoid reading from or writing to cache.
|
|
223
|
+
refresh: Whether to refresh cached data.
|
|
224
|
+
refresh_packages: Packages whose cached data uv should refresh.
|
|
225
|
+
force: Whether to replace existing executable entries.
|
|
226
|
+
reinstall: Whether to reinstall all packages.
|
|
191
227
|
uv_config_file: Optional uv configuration file passed to `uv tool`.
|
|
192
228
|
|
|
193
229
|
Returns:
|
|
@@ -201,10 +237,28 @@ def uv_install_command(
|
|
|
201
237
|
command.extend(["--python", python])
|
|
202
238
|
for index in indexes:
|
|
203
239
|
command.extend(["--index", index])
|
|
240
|
+
if default_index:
|
|
241
|
+
command.extend(["--default-index", default_index])
|
|
204
242
|
for link in find_links:
|
|
205
243
|
command.extend(["--find-links", link])
|
|
244
|
+
if no_index:
|
|
245
|
+
command.append("--no-index")
|
|
246
|
+
if index_strategy:
|
|
247
|
+
command.extend(["--index-strategy", index_strategy])
|
|
248
|
+
if keyring_provider:
|
|
249
|
+
command.extend(["--keyring-provider", keyring_provider])
|
|
206
250
|
for constraint in constraints:
|
|
207
251
|
command.extend(["--constraints", constraint])
|
|
252
|
+
if no_cache:
|
|
253
|
+
command.append("--no-cache")
|
|
254
|
+
if refresh:
|
|
255
|
+
command.append("--refresh")
|
|
256
|
+
for package_name in refresh_packages:
|
|
257
|
+
command.extend(["--refresh-package", package_name])
|
|
258
|
+
if force:
|
|
259
|
+
command.append("--force")
|
|
260
|
+
if reinstall:
|
|
261
|
+
command.append("--reinstall")
|
|
208
262
|
command.append(package)
|
|
209
263
|
return command
|
|
210
264
|
|
|
@@ -688,8 +742,17 @@ def parse_environment_tool(data: object, base_dir: Path, index: int) -> Environm
|
|
|
688
742
|
script=optional_manifest_path(data, "script", base_dir),
|
|
689
743
|
python=optional_string(data, "python"),
|
|
690
744
|
indexes=optional_string_tuple(data, "indexes"),
|
|
745
|
+
default_index=optional_string(data, "default_index"),
|
|
691
746
|
find_links=optional_string_tuple(data, "find_links"),
|
|
747
|
+
no_index=optional_bool(data, "no_index", False),
|
|
748
|
+
index_strategy=optional_string(data, "index_strategy"),
|
|
749
|
+
keyring_provider=optional_string(data, "keyring_provider"),
|
|
692
750
|
constraints=tuple(str(path) for path in optional_manifest_paths(data, "constraints", base_dir)),
|
|
751
|
+
no_cache=optional_bool(data, "no_cache", False),
|
|
752
|
+
refresh=optional_bool(data, "refresh", False),
|
|
753
|
+
refresh_packages=optional_string_tuple(data, "refresh_packages"),
|
|
754
|
+
force=optional_bool(data, "force", False),
|
|
755
|
+
reinstall=optional_bool(data, "reinstall", False),
|
|
693
756
|
description=optional_string(data, "description"),
|
|
694
757
|
homepage=optional_string(data, "homepage"),
|
|
695
758
|
)
|
|
@@ -747,8 +810,17 @@ def environment_actions(spec: EnvironmentSpec, paths: DeploymentPaths) -> tuple[
|
|
|
747
810
|
tool.package,
|
|
748
811
|
tool.python,
|
|
749
812
|
tool.indexes,
|
|
813
|
+
tool.default_index,
|
|
750
814
|
tool.find_links,
|
|
815
|
+
tool.no_index,
|
|
816
|
+
tool.index_strategy,
|
|
817
|
+
tool.keyring_provider,
|
|
751
818
|
tool.constraints,
|
|
819
|
+
tool.no_cache,
|
|
820
|
+
tool.refresh,
|
|
821
|
+
tool.refresh_packages,
|
|
822
|
+
tool.force,
|
|
823
|
+
tool.reinstall,
|
|
752
824
|
tool.uv_config_file,
|
|
753
825
|
)
|
|
754
826
|
actions.append(
|
|
@@ -806,8 +878,17 @@ def deploy_environment(
|
|
|
806
878
|
tool.package,
|
|
807
879
|
tool.python,
|
|
808
880
|
tool.indexes,
|
|
881
|
+
tool.default_index,
|
|
809
882
|
tool.find_links,
|
|
883
|
+
tool.no_index,
|
|
884
|
+
tool.index_strategy,
|
|
885
|
+
tool.keyring_provider,
|
|
810
886
|
tool.constraints,
|
|
887
|
+
tool.no_cache,
|
|
888
|
+
tool.refresh,
|
|
889
|
+
tool.refresh_packages,
|
|
890
|
+
tool.force,
|
|
891
|
+
tool.reinstall,
|
|
811
892
|
tool.uv_config_file,
|
|
812
893
|
),
|
|
813
894
|
check=True,
|
|
@@ -847,8 +928,17 @@ def deploy_python_tool(
|
|
|
847
928
|
homepage: str | None = None,
|
|
848
929
|
python: str | None = None,
|
|
849
930
|
indexes: tuple[str, ...] = (),
|
|
931
|
+
default_index: str | None = None,
|
|
850
932
|
find_links: tuple[str, ...] = (),
|
|
933
|
+
no_index: bool = False,
|
|
934
|
+
index_strategy: str | None = None,
|
|
935
|
+
keyring_provider: str | None = None,
|
|
851
936
|
constraints: tuple[str, ...] = (),
|
|
937
|
+
no_cache: bool = False,
|
|
938
|
+
refresh: bool = False,
|
|
939
|
+
refresh_packages: tuple[str, ...] = (),
|
|
940
|
+
force: bool = False,
|
|
941
|
+
reinstall: bool = False,
|
|
852
942
|
uv_config_file: Path | None = None,
|
|
853
943
|
execute_install: bool = False,
|
|
854
944
|
make_default: bool = True,
|
|
@@ -865,8 +955,17 @@ def deploy_python_tool(
|
|
|
865
955
|
homepage: Optional upstream homepage shown in module help.
|
|
866
956
|
python: Optional Python interpreter or version passed to uv.
|
|
867
957
|
indexes: Additional package index URLs passed to uv.
|
|
958
|
+
default_index: Default package index URL passed to uv.
|
|
868
959
|
find_links: Wheelhouse directories or HTML package pages passed to uv.
|
|
960
|
+
no_index: Whether uv should ignore registry indexes.
|
|
961
|
+
index_strategy: Package index strategy passed to uv.
|
|
962
|
+
keyring_provider: Keyring provider passed to uv.
|
|
869
963
|
constraints: Constraint requirement files passed to uv.
|
|
964
|
+
no_cache: Whether uv should avoid reading from or writing to cache.
|
|
965
|
+
refresh: Whether uv should refresh cached data.
|
|
966
|
+
refresh_packages: Packages whose cached data uv should refresh.
|
|
967
|
+
force: Whether uv should replace existing executable entries.
|
|
968
|
+
reinstall: Whether uv should reinstall all packages.
|
|
870
969
|
uv_config_file: Optional uv configuration file passed to `uv tool`.
|
|
871
970
|
execute_install: Whether to run `uv tool install` immediately.
|
|
872
971
|
make_default: Whether to make this version the module default.
|
|
@@ -882,7 +981,23 @@ def deploy_python_tool(
|
|
|
882
981
|
paths = deployment_paths(module_root, prefix, name, version)
|
|
883
982
|
paths.bin_dir.mkdir(parents=True, exist_ok=True)
|
|
884
983
|
tool_dir = paths.install_root / "uv-tools"
|
|
885
|
-
command = uv_install_command(
|
|
984
|
+
command = uv_install_command(
|
|
985
|
+
package,
|
|
986
|
+
python,
|
|
987
|
+
indexes,
|
|
988
|
+
default_index,
|
|
989
|
+
find_links,
|
|
990
|
+
no_index,
|
|
991
|
+
index_strategy,
|
|
992
|
+
keyring_provider,
|
|
993
|
+
constraints,
|
|
994
|
+
no_cache,
|
|
995
|
+
refresh,
|
|
996
|
+
refresh_packages,
|
|
997
|
+
force,
|
|
998
|
+
reinstall,
|
|
999
|
+
uv_config_file,
|
|
1000
|
+
)
|
|
886
1001
|
|
|
887
1002
|
if execute_install:
|
|
888
1003
|
require_executable("uv")
|
|
@@ -56,10 +56,23 @@ def test_deploy_python_command_writes_modulefile() -> None:
|
|
|
56
56
|
"modules",
|
|
57
57
|
"--index",
|
|
58
58
|
"https://packages.example/simple",
|
|
59
|
+
"--default-index",
|
|
60
|
+
"https://default.example/simple",
|
|
59
61
|
"--find-links",
|
|
60
62
|
"/prod/wheels",
|
|
63
|
+
"--no-index",
|
|
64
|
+
"--index-strategy",
|
|
65
|
+
"unsafe-first-match",
|
|
66
|
+
"--keyring-provider",
|
|
67
|
+
"subprocess",
|
|
61
68
|
"--constraints",
|
|
62
69
|
"/prod/constraints.txt",
|
|
70
|
+
"--no-cache",
|
|
71
|
+
"--refresh",
|
|
72
|
+
"--refresh-package",
|
|
73
|
+
"ruff",
|
|
74
|
+
"--force",
|
|
75
|
+
"--reinstall",
|
|
63
76
|
"--uv-config-file",
|
|
64
77
|
"/prod/uv.toml",
|
|
65
78
|
],
|
|
@@ -70,7 +83,16 @@ def test_deploy_python_command_writes_modulefile() -> None:
|
|
|
70
83
|
assert "modulefile: modules/ruff/0.8.0" in result.output
|
|
71
84
|
assert "default version: modules/ruff/.version" in result.output
|
|
72
85
|
assert "--config-file /prod/uv.toml" in modulefile
|
|
86
|
+
assert "--default-index https://default.example/simple" in modulefile
|
|
87
|
+
assert "--no-index" in modulefile
|
|
88
|
+
assert "--index-strategy unsafe-first-match" in modulefile
|
|
89
|
+
assert "--keyring-provider subprocess" in modulefile
|
|
73
90
|
assert "--constraints /prod/constraints.txt" in modulefile
|
|
91
|
+
assert "--no-cache" in modulefile
|
|
92
|
+
assert "--refresh" in modulefile
|
|
93
|
+
assert "--refresh-package ruff" in modulefile
|
|
94
|
+
assert "--force" in modulefile
|
|
95
|
+
assert "--reinstall" in modulefile
|
|
74
96
|
|
|
75
97
|
|
|
76
98
|
def test_deploy_python_command_uses_config_defaults() -> None:
|
|
@@ -463,7 +485,13 @@ name = "ruff"
|
|
|
463
485
|
version = "0.8.0"
|
|
464
486
|
package = "ruff==0.8.0"
|
|
465
487
|
indexes = ["https://packages.example/simple"]
|
|
488
|
+
default_index = "https://default.example/simple"
|
|
489
|
+
no_index = true
|
|
466
490
|
constraints = ["constraints.txt"]
|
|
491
|
+
no_cache = true
|
|
492
|
+
refresh = true
|
|
493
|
+
refresh_packages = ["ruff"]
|
|
494
|
+
force = true
|
|
467
495
|
""".strip(),
|
|
468
496
|
encoding="utf-8",
|
|
469
497
|
)
|
|
@@ -474,7 +502,13 @@ constraints = ["constraints.txt"]
|
|
|
474
502
|
assert "would create install root: tools/dev-tools/2026.05" in result.output
|
|
475
503
|
assert "would install python ruff:" in result.output
|
|
476
504
|
assert "--index https://packages.example/simple" in result.output
|
|
505
|
+
assert "--default-index https://default.example/simple" in result.output
|
|
506
|
+
assert "--no-index" in result.output
|
|
477
507
|
assert "--constraints constraints.txt" in result.output
|
|
508
|
+
assert "--no-cache" in result.output
|
|
509
|
+
assert "--refresh" in result.output
|
|
510
|
+
assert "--refresh-package ruff" in result.output
|
|
511
|
+
assert "--force" in result.output
|
|
478
512
|
assert not install_root_exists
|
|
479
513
|
|
|
480
514
|
|
|
@@ -88,8 +88,17 @@ def test_python_tool_install_command_accepts_indexes() -> None:
|
|
|
88
88
|
assert uv_install_command(
|
|
89
89
|
"internal-tool==1.2.3",
|
|
90
90
|
indexes=("https://packages.example/simple", "https://mirror.example/simple"),
|
|
91
|
+
default_index="https://default.example/simple",
|
|
91
92
|
find_links=("/prod/wheels",),
|
|
93
|
+
no_index=True,
|
|
94
|
+
index_strategy="unsafe-best-match",
|
|
95
|
+
keyring_provider="subprocess",
|
|
92
96
|
constraints=("/prod/constraints.txt",),
|
|
97
|
+
no_cache=True,
|
|
98
|
+
refresh=True,
|
|
99
|
+
refresh_packages=("internal-tool",),
|
|
100
|
+
force=True,
|
|
101
|
+
reinstall=True,
|
|
93
102
|
) == [
|
|
94
103
|
"uv",
|
|
95
104
|
"tool",
|
|
@@ -98,10 +107,23 @@ def test_python_tool_install_command_accepts_indexes() -> None:
|
|
|
98
107
|
"https://packages.example/simple",
|
|
99
108
|
"--index",
|
|
100
109
|
"https://mirror.example/simple",
|
|
110
|
+
"--default-index",
|
|
111
|
+
"https://default.example/simple",
|
|
101
112
|
"--find-links",
|
|
102
113
|
"/prod/wheels",
|
|
114
|
+
"--no-index",
|
|
115
|
+
"--index-strategy",
|
|
116
|
+
"unsafe-best-match",
|
|
117
|
+
"--keyring-provider",
|
|
118
|
+
"subprocess",
|
|
103
119
|
"--constraints",
|
|
104
120
|
"/prod/constraints.txt",
|
|
121
|
+
"--no-cache",
|
|
122
|
+
"--refresh",
|
|
123
|
+
"--refresh-package",
|
|
124
|
+
"internal-tool",
|
|
125
|
+
"--force",
|
|
126
|
+
"--reinstall",
|
|
105
127
|
"internal-tool==1.2.3",
|
|
106
128
|
]
|
|
107
129
|
|
|
@@ -268,16 +290,26 @@ def test_deploy_python_tool_writes_package_sources_to_install_hint(tmp_path: Pat
|
|
|
268
290
|
module_root=tmp_path / "modules",
|
|
269
291
|
prefix=tmp_path / "tools",
|
|
270
292
|
indexes=("https://packages.example/simple",),
|
|
293
|
+
default_index="https://default.example/simple",
|
|
271
294
|
find_links=("/prod/wheels",),
|
|
295
|
+
index_strategy="first-index",
|
|
296
|
+
keyring_provider="disabled",
|
|
272
297
|
constraints=("/prod/constraints.txt",),
|
|
298
|
+
refresh_packages=("internal-tool",),
|
|
299
|
+
force=True,
|
|
273
300
|
uv_config_file=Path("/prod/uv.toml"),
|
|
274
301
|
)
|
|
275
302
|
|
|
276
303
|
modulefile = paths.modulefile.read_text(encoding="utf-8")
|
|
277
304
|
assert "--config-file /prod/uv.toml" in modulefile
|
|
278
305
|
assert "--index https://packages.example/simple" in modulefile
|
|
306
|
+
assert "--default-index https://default.example/simple" in modulefile
|
|
279
307
|
assert "--find-links /prod/wheels" in modulefile
|
|
308
|
+
assert "--index-strategy first-index" in modulefile
|
|
309
|
+
assert "--keyring-provider disabled" in modulefile
|
|
280
310
|
assert "--constraints /prod/constraints.txt" in modulefile
|
|
311
|
+
assert "--refresh-package internal-tool" in modulefile
|
|
312
|
+
assert "--force" in modulefile
|
|
281
313
|
|
|
282
314
|
|
|
283
315
|
def test_deploy_python_tool_writes_url_dependency_to_install_hint(tmp_path: Path) -> None:
|
|
@@ -536,8 +568,17 @@ version = "0.8.0"
|
|
|
536
568
|
package = "ruff==0.8.0"
|
|
537
569
|
python = "3.12"
|
|
538
570
|
indexes = ["https://packages.example/simple"]
|
|
571
|
+
default_index = "https://default.example/simple"
|
|
539
572
|
find_links = ["/prod/wheels"]
|
|
573
|
+
no_index = true
|
|
574
|
+
index_strategy = "unsafe-best-match"
|
|
575
|
+
keyring_provider = "subprocess"
|
|
540
576
|
constraints = ["constraints.txt", "/prod/global-constraints.txt"]
|
|
577
|
+
no_cache = true
|
|
578
|
+
refresh = true
|
|
579
|
+
refresh_packages = ["ruff"]
|
|
580
|
+
force = true
|
|
581
|
+
reinstall = true
|
|
541
582
|
uv_config_file = "uv.toml"
|
|
542
583
|
|
|
543
584
|
[[tools]]
|
|
@@ -557,7 +598,16 @@ script = "scripts/helper"
|
|
|
557
598
|
assert not spec.make_default
|
|
558
599
|
assert spec.tools[0].package == "ruff==0.8.0"
|
|
559
600
|
assert spec.tools[0].indexes == ("https://packages.example/simple",)
|
|
601
|
+
assert spec.tools[0].default_index == "https://default.example/simple"
|
|
602
|
+
assert spec.tools[0].no_index
|
|
603
|
+
assert spec.tools[0].index_strategy == "unsafe-best-match"
|
|
604
|
+
assert spec.tools[0].keyring_provider == "subprocess"
|
|
560
605
|
assert spec.tools[0].constraints == (str(tmp_path / "constraints.txt"), "/prod/global-constraints.txt")
|
|
606
|
+
assert spec.tools[0].no_cache
|
|
607
|
+
assert spec.tools[0].refresh
|
|
608
|
+
assert spec.tools[0].refresh_packages == ("ruff",)
|
|
609
|
+
assert spec.tools[0].force
|
|
610
|
+
assert spec.tools[0].reinstall
|
|
561
611
|
assert spec.tools[0].uv_config_file == tmp_path / "uv.toml"
|
|
562
612
|
assert spec.tools[1].script == tmp_path / "scripts/helper"
|
|
563
613
|
|
|
@@ -666,7 +716,13 @@ type = "python"
|
|
|
666
716
|
name = "ruff"
|
|
667
717
|
package = "ruff==0.8.0"
|
|
668
718
|
python = "3.12"
|
|
719
|
+
default_index = "https://default.example/simple"
|
|
720
|
+
no_index = true
|
|
669
721
|
constraints = ["constraints.txt"]
|
|
722
|
+
no_cache = true
|
|
723
|
+
refresh = true
|
|
724
|
+
refresh_packages = ["ruff"]
|
|
725
|
+
force = true
|
|
670
726
|
uv_config_file = "uv.toml"
|
|
671
727
|
""".strip(),
|
|
672
728
|
encoding="utf-8",
|
|
@@ -687,8 +743,16 @@ uv_config_file = "uv.toml"
|
|
|
687
743
|
"install",
|
|
688
744
|
"--python",
|
|
689
745
|
"3.12",
|
|
746
|
+
"--default-index",
|
|
747
|
+
"https://default.example/simple",
|
|
748
|
+
"--no-index",
|
|
690
749
|
"--constraints",
|
|
691
750
|
str(tmp_path / "constraints.txt"),
|
|
751
|
+
"--no-cache",
|
|
752
|
+
"--refresh",
|
|
753
|
+
"--refresh-package",
|
|
754
|
+
"ruff",
|
|
755
|
+
"--force",
|
|
692
756
|
"ruff==0.8.0",
|
|
693
757
|
]
|
|
694
758
|
assert calls[0][1]["UV_TOOL_DIR"] == str(result.paths.install_root / "uv-tools")
|
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
# Collective Environments
|
|
2
|
-
|
|
3
|
-
Collective environments package several tools behind one modulefile. Use them
|
|
4
|
-
when a shared workflow needs Python CLIs, Rust binaries, and shell scripts to
|
|
5
|
-
appear together from one `module load`.
|
|
6
|
-
|
|
7
|
-
Create a TOML manifest:
|
|
8
|
-
|
|
9
|
-
```toml
|
|
10
|
-
name = "dev-tools"
|
|
11
|
-
version = "2026.05"
|
|
12
|
-
description = "Shared developer command-line tools"
|
|
13
|
-
prefix = "/prod/tools"
|
|
14
|
-
module_root = "/prod/modulefiles"
|
|
15
|
-
default = true
|
|
16
|
-
|
|
17
|
-
[[tools]]
|
|
18
|
-
type = "python"
|
|
19
|
-
name = "ruff"
|
|
20
|
-
version = "0.8.0"
|
|
21
|
-
package = "ruff==0.8.0"
|
|
22
|
-
python = "3.12"
|
|
23
|
-
indexes = ["https://packages.example/simple"]
|
|
24
|
-
find_links = ["/prod/wheels"]
|
|
25
|
-
constraints = ["/prod/constraints/runtime.txt"]
|
|
26
|
-
uv_config_file = "/prod/config/uv.toml"
|
|
27
|
-
|
|
28
|
-
[[tools]]
|
|
29
|
-
type = "rust"
|
|
30
|
-
name = "ripgrep"
|
|
31
|
-
version = "14.1.1"
|
|
32
|
-
binary = "./target/release/rg"
|
|
33
|
-
|
|
34
|
-
[[tools]]
|
|
35
|
-
type = "script"
|
|
36
|
-
name = "lab-helper"
|
|
37
|
-
version = "1.0.0"
|
|
38
|
-
script = "./scripts/lab-helper"
|
|
39
|
-
```
|
|
40
|
-
|
|
41
|
-
Deploy the environment:
|
|
42
|
-
|
|
43
|
-
```sh
|
|
44
|
-
module-manager deploy-env --file dev-tools.toml
|
|
45
|
-
```
|
|
46
|
-
|
|
47
|
-
This writes one shared environment:
|
|
48
|
-
|
|
49
|
-
```text
|
|
50
|
-
/prod/tools/dev-tools/2026.05/bin
|
|
51
|
-
/prod/modulefiles/dev-tools/2026.05
|
|
52
|
-
```
|
|
53
|
-
|
|
54
|
-
The generated modulefile prepends the shared `bin` directory to `PATH`, so all
|
|
55
|
-
tools in the manifest are available after:
|
|
56
|
-
|
|
57
|
-
```sh
|
|
58
|
-
module use /prod/modulefiles
|
|
59
|
-
module load dev-tools
|
|
60
|
-
```
|
|
61
|
-
|
|
62
|
-
Relative `binary` and `script` paths are resolved relative to the manifest file.
|
|
63
|
-
Relative Python `constraints` paths are resolved the same way.
|
|
64
|
-
|
|
65
|
-
## Preview
|
|
66
|
-
|
|
67
|
-
Use `--dry-run` to preview the install, copy, and modulefile actions without
|
|
68
|
-
creating files:
|
|
69
|
-
|
|
70
|
-
```sh
|
|
71
|
-
module-manager deploy-env --file dev-tools.toml --dry-run
|
|
72
|
-
```
|
|
73
|
-
|
|
74
|
-
## Overrides
|
|
75
|
-
|
|
76
|
-
The manifest can define `prefix`, `module_root`, and `default`, but CLI options
|
|
77
|
-
can override them:
|
|
78
|
-
|
|
79
|
-
```sh
|
|
80
|
-
module-manager deploy-env \
|
|
81
|
-
--file dev-tools.toml \
|
|
82
|
-
--prefix /scratch/tools \
|
|
83
|
-
--module-root /scratch/modulefiles \
|
|
84
|
-
--no-default
|
|
85
|
-
```
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|