env-module-manager 0.7.0__tar.gz → 0.9.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.
Files changed (37) hide show
  1. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/PKG-INFO +16 -1
  2. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/README.md +15 -0
  3. env_module_manager-0.9.0/docs/collective-environments.md +198 -0
  4. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/configuration.md +6 -0
  5. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/python-tools.md +89 -3
  6. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/reference.md +13 -1
  7. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/module_manager/__init__.py +1 -1
  8. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/module_manager/cli.py +79 -1
  9. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/module_manager/config.py +4 -0
  10. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/module_manager/deploy.py +146 -9
  11. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/pyproject.toml +1 -1
  12. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/tests/test_cli.py +42 -0
  13. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/tests/test_config.py +4 -0
  14. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/tests/test_modulefile.py +88 -3
  15. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/uv.lock +1 -1
  16. env_module_manager-0.7.0/docs/collective-environments.md +0 -85
  17. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/.github/workflows/publish.yml +0 -0
  18. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/.github/workflows/tag.yml +0 -0
  19. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/.github/workflows/test.yml +0 -0
  20. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/.gitignore +0 -0
  21. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/.python-version +0 -0
  22. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/.readthedocs.yaml +0 -0
  23. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/AGENTS.md +0 -0
  24. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/CONTRIBUTING.md +0 -0
  25. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/LICENSE.md +0 -0
  26. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/development.md +0 -0
  27. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/environment-modules.md +0 -0
  28. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/index.md +0 -0
  29. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/installation.md +0 -0
  30. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/rust-tools.md +0 -0
  31. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/shell-tools.md +0 -0
  32. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/stylesheets/extra.css +0 -0
  33. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/docs/uninstall.md +0 -0
  34. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/example.bash +0 -0
  35. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/mkdocs.yml +0 -0
  36. {env_module_manager-0.7.0 → env_module_manager-0.9.0}/module_manager/modulefile.py +0 -0
  37. {env_module_manager-0.7.0 → env_module_manager-0.9.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.7.0
3
+ Version: 0.9.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,12 +73,17 @@ 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 \
78
+ --uv-executable /opt/uv/bin/uv \
77
79
  --prefix /prod/tools \
78
80
  --module-root /prod/modulefiles \
79
81
  --execute-install
80
82
  ```
81
83
 
84
+ Set `MODULE_MANAGER_UV_EXECUTABLE=/opt/uv/bin/uv` to use the same uv path by
85
+ default.
86
+
82
87
  Deploy a Rust binary:
83
88
 
84
89
  ```sh
@@ -94,6 +99,16 @@ Generate a constraints file for a Python CLI:
94
99
  module-manager auto-constraints ruff==0.8.0 --output constraints.txt
95
100
  ```
96
101
 
102
+ Deploy from a direct wheel or Git URL:
103
+
104
+ ```sh
105
+ module-manager deploy-python my-tool 1.0.0 \
106
+ --package 'my-tool @ file:///prod/wheels/my_tool-1.0.0-py3-none-any.whl' \
107
+ --prefix /prod/tools \
108
+ --module-root /prod/modulefiles \
109
+ --execute-install
110
+ ```
111
+
97
112
  Deploy a shell script:
98
113
 
99
114
  ```sh
@@ -41,12 +41,17 @@ 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 \
46
+ --uv-executable /opt/uv/bin/uv \
45
47
  --prefix /prod/tools \
46
48
  --module-root /prod/modulefiles \
47
49
  --execute-install
48
50
  ```
49
51
 
52
+ Set `MODULE_MANAGER_UV_EXECUTABLE=/opt/uv/bin/uv` to use the same uv path by
53
+ default.
54
+
50
55
  Deploy a Rust binary:
51
56
 
52
57
  ```sh
@@ -62,6 +67,16 @@ Generate a constraints file for a Python CLI:
62
67
  module-manager auto-constraints ruff==0.8.0 --output constraints.txt
63
68
  ```
64
69
 
70
+ Deploy from a direct wheel or Git URL:
71
+
72
+ ```sh
73
+ module-manager deploy-python my-tool 1.0.0 \
74
+ --package 'my-tool @ file:///prod/wheels/my_tool-1.0.0-py3-none-any.whl' \
75
+ --prefix /prod/tools \
76
+ --module-root /prod/modulefiles \
77
+ --execute-install
78
+ ```
79
+
65
80
  Deploy a shell script:
66
81
 
67
82
  ```sh
@@ -0,0 +1,198 @@
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
+ uv_executable = "/opt/uv/bin/uv"
36
+
37
+ [[tools]]
38
+ type = "rust"
39
+ name = "ripgrep"
40
+ version = "14.1.1"
41
+ binary = "./target/release/rg"
42
+
43
+ [[tools]]
44
+ type = "script"
45
+ name = "lab-helper"
46
+ version = "1.0.0"
47
+ script = "./scripts/lab-helper"
48
+ ```
49
+
50
+ Deploy the environment:
51
+
52
+ ```sh
53
+ module-manager deploy-env --file dev-tools.toml
54
+ ```
55
+
56
+ Use `--uv-executable` when all Python tools in the manifest should use a
57
+ specific uv executable:
58
+
59
+ ```sh
60
+ module-manager deploy-env --file dev-tools.toml --uv-executable /opt/uv/bin/uv
61
+ ```
62
+
63
+ You can also set the same default with `MODULE_MANAGER_UV_EXECUTABLE`, or set
64
+ `uv_executable` on an individual Python tool in the manifest when only that
65
+ entry needs a different executable.
66
+
67
+ This writes one shared environment:
68
+
69
+ ```text
70
+ /prod/tools/dev-tools/2026.05/bin
71
+ /prod/modulefiles/dev-tools/2026.05
72
+ ```
73
+
74
+ The generated modulefile prepends the shared `bin` directory to `PATH`, so all
75
+ tools in the manifest are available after:
76
+
77
+ ```sh
78
+ module use /prod/modulefiles
79
+ module load dev-tools
80
+ ```
81
+
82
+ Relative `binary` and `script` paths are resolved relative to the manifest file.
83
+ Relative Python `constraints` paths are resolved the same way.
84
+
85
+ ## Multiple Python Tools
86
+
87
+ A collective environment can install several Python CLIs into the same shared
88
+ `bin` directory. Each Python tool can use its own package spec and uv resolver
89
+ options:
90
+
91
+ ```toml
92
+ name = "python-dev-tools"
93
+ version = "2026.05"
94
+ prefix = "/prod/tools"
95
+ module_root = "/prod/modulefiles"
96
+
97
+ [[tools]]
98
+ type = "python"
99
+ name = "ruff"
100
+ version = "0.8.0"
101
+ package = "ruff==0.8.0"
102
+ python = "3.12"
103
+ constraints = ["constraints.txt"]
104
+
105
+ [[tools]]
106
+ type = "python"
107
+ name = "gitconductor"
108
+ version = "0.7.0"
109
+ package = "gitconductor==0.7.0"
110
+ default_index = "https://packages.example/simple"
111
+ constraints = ["constraints.txt"]
112
+ refresh_packages = ["gitconductor"]
113
+
114
+ [[tools]]
115
+ type = "python"
116
+ name = "internal-review"
117
+ version = "1.4.2"
118
+ package = "internal-review @ file:///prod/wheels/internal_review-1.4.2-py3-none-any.whl"
119
+ find_links = ["/prod/wheels"]
120
+ no_index = true
121
+ ```
122
+
123
+ Deploy it with:
124
+
125
+ ```sh
126
+ module-manager deploy-env --file python-dev-tools.toml
127
+ ```
128
+
129
+ After `module load python-dev-tools`, the `ruff`, `gitconductor`, and
130
+ `internal-review` executables are available from one module.
131
+
132
+ ## Python Tools and Bash Scripts
133
+
134
+ Use script entries for Bash helpers that should live alongside Python CLIs:
135
+
136
+ ```toml
137
+ name = "release-tools"
138
+ version = "2026.05"
139
+ description = "Release automation helpers"
140
+ prefix = "/prod/tools"
141
+ module_root = "/prod/modulefiles"
142
+
143
+ [[tools]]
144
+ type = "python"
145
+ name = "gitconductor"
146
+ version = "0.7.0"
147
+ package = "gitconductor==0.7.0"
148
+ constraints = ["constraints.txt"]
149
+
150
+ [[tools]]
151
+ type = "python"
152
+ name = "changelog-builder"
153
+ version = "2.1.0"
154
+ package = "changelog-builder @ git+https://github.com/org/changelog-builder.git@v2.1.0"
155
+
156
+ [[tools]]
157
+ type = "script"
158
+ name = "release-check"
159
+ version = "1.0.0"
160
+ script = "./scripts/release-check.sh"
161
+
162
+ [[tools]]
163
+ type = "script"
164
+ name = "publish-docs"
165
+ version = "1.0.0"
166
+ script = "./scripts/publish-docs.sh"
167
+ ```
168
+
169
+ Deploy it with:
170
+
171
+ ```sh
172
+ module-manager deploy-env --file release-tools.toml
173
+ ```
174
+
175
+ The script files are copied into the shared environment `bin` directory and
176
+ marked executable, so they appear on `PATH` next to the Python entry points.
177
+
178
+ ## Preview
179
+
180
+ Use `--dry-run` to preview the install, copy, and modulefile actions without
181
+ creating files:
182
+
183
+ ```sh
184
+ module-manager deploy-env --file dev-tools.toml --dry-run
185
+ ```
186
+
187
+ ## Overrides
188
+
189
+ The manifest can define `prefix`, `module_root`, and `default`, but CLI options
190
+ can override them:
191
+
192
+ ```sh
193
+ module-manager deploy-env \
194
+ --file dev-tools.toml \
195
+ --prefix /scratch/tools \
196
+ --module-root /scratch/modulefiles \
197
+ --no-default
198
+ ```
@@ -18,6 +18,7 @@ module_root = "/prod/modulefiles"
18
18
  indexes = ["https://packages.example/simple"]
19
19
  find_links = ["/prod/wheels"]
20
20
  uv_config_file = "/prod/config/uv.toml"
21
+ uv_executable = "/opt/uv/bin/uv"
21
22
  ```
22
23
 
23
24
  `prefix` and `module_root` must resolve to different directories. Using the
@@ -37,10 +38,15 @@ export MODULE_MANAGER_PREFIX=/prod/tools
37
38
  export MODULE_MANAGER_MODULE_ROOT=/prod/modulefiles
38
39
  export MODULE_MANAGER_INDEXES=https://packages.example/simple,https://mirror.example/simple
39
40
  export MODULE_MANAGER_FIND_LINKS=/prod/wheels,/prod/more-wheels
41
+ export MODULE_MANAGER_UV_EXECUTABLE=/opt/uv/bin/uv
40
42
  ```
41
43
 
42
44
  `MODULE_MANAGER_CONFIG` can point to an alternate TOML config file.
43
45
 
46
+ `MODULE_MANAGER_UV_EXECUTABLE` sets the `uv` executable used by
47
+ `deploy-python`, `deploy-env`, and `auto-constraints`. CLI `--uv-executable`
48
+ options override it when provided.
49
+
44
50
  There is no `MODULE_MANAGER_*` environment variable for uv configuration files.
45
51
  Use uv's own `UV_CONFIG_FILE` environment variable when you want to set that via
46
52
  the environment.
@@ -11,6 +11,24 @@ Other uv settings, including `UV_CONFIG_FILE`, are preserved.
11
11
  Python package dependency because `module-manager` shells out to the `uv`
12
12
  executable and is intended to be installable by `uv` itself.
13
13
 
14
+ If `uv` is not on `PATH`, or you want a pinned executable, pass
15
+ `--uv-executable`:
16
+
17
+ ```sh
18
+ module-manager deploy-python ruff 0.8.0 \
19
+ --package 'ruff==0.8.0' \
20
+ --uv-executable /opt/uv/bin/uv \
21
+ --prefix /prod/tools \
22
+ --module-root /prod/modulefiles \
23
+ --execute-install
24
+ ```
25
+
26
+ The same default can be set for Python workflows with an environment variable:
27
+
28
+ ```sh
29
+ export MODULE_MANAGER_UV_EXECUTABLE=/opt/uv/bin/uv
30
+ ```
31
+
14
32
  ## Deploy from PyPI
15
33
 
16
34
  ```sh
@@ -34,20 +52,37 @@ unchanged.
34
52
  ```sh
35
53
  module-manager deploy-python gitconductor 0.1.0 \
36
54
  --package gitconductor==0.1.0 \
55
+ --default-index https://packages.example/simple \
37
56
  --index https://packages.example/simple \
38
57
  --prefix /prod/tools \
39
58
  --module-root /prod/modulefiles \
40
59
  --execute-install
41
60
  ```
42
61
 
43
- `--index` may be used more than once.
62
+ `--index` may be used more than once. Use `--default-index` to replace uv's
63
+ default package index, or `--no-index` when resolution should use only direct
64
+ URLs and `--find-links` locations.
44
65
 
45
66
  ## Deploy from a Wheelhouse
46
67
 
68
+ Use `--find-links` when wheels are staged in a directory or simple HTML page:
69
+
70
+ ```sh
71
+ module-manager deploy-python gitconductor 0.1.0 \
72
+ --package gitconductor==0.1.0 \
73
+ --find-links /prod/wheels \
74
+ --prefix /prod/tools \
75
+ --module-root /prod/modulefiles \
76
+ --execute-install
77
+ ```
78
+
79
+ Add `--no-index` to make the install fully wheelhouse-only:
80
+
47
81
  ```sh
48
82
  module-manager deploy-python gitconductor 0.1.0 \
49
83
  --package gitconductor==0.1.0 \
50
84
  --find-links /prod/wheels \
85
+ --no-index \
51
86
  --prefix /prod/tools \
52
87
  --module-root /prod/modulefiles \
53
88
  --execute-install
@@ -55,6 +90,28 @@ module-manager deploy-python gitconductor 0.1.0 \
55
90
 
56
91
  `--find-links` may be used more than once.
57
92
 
93
+ ## Deploy from a Wheel URL
94
+
95
+ Pass a direct wheel URL or file URL as the package spec:
96
+
97
+ ```sh
98
+ module-manager deploy-python gitconductor 0.1.0 \
99
+ --package 'gitconductor @ https://packages.example/files/gitconductor-0.1.0-py3-none-any.whl' \
100
+ --prefix /prod/tools \
101
+ --module-root /prod/modulefiles \
102
+ --execute-install
103
+ ```
104
+
105
+ For a local wheel file:
106
+
107
+ ```sh
108
+ module-manager deploy-python gitconductor 0.1.0 \
109
+ --package 'gitconductor @ file:///prod/wheels/gitconductor-0.1.0-py3-none-any.whl' \
110
+ --prefix /prod/tools \
111
+ --module-root /prod/modulefiles \
112
+ --execute-install
113
+ ```
114
+
58
115
  ## Use Constraint Files
59
116
 
60
117
  Pass requirement constraint files to `uv tool install`:
@@ -70,6 +127,24 @@ module-manager deploy-python gitconductor 0.1.0 \
70
127
 
71
128
  `--constraints` may be used more than once.
72
129
 
130
+ ## Cache and Refresh Controls
131
+
132
+ Pass uv cache controls through when you need fresh index or package metadata:
133
+
134
+ ```sh
135
+ module-manager deploy-python gitconductor 0.1.0 \
136
+ --package gitconductor==0.1.0 \
137
+ --refresh \
138
+ --refresh-package gitconductor \
139
+ --no-cache \
140
+ --prefix /prod/tools \
141
+ --module-root /prod/modulefiles \
142
+ --execute-install
143
+ ```
144
+
145
+ Use `--force` or `--reinstall` when re-running an install into an existing
146
+ versioned tool environment.
147
+
73
148
  ## Generate Constraint Files
74
149
 
75
150
  Use `auto-constraints` to compile a package and its dependencies into a
@@ -78,6 +153,7 @@ constraints file:
78
153
  ```sh
79
154
  module-manager auto-constraints gitconductor==0.7.0 \
80
155
  --index https://packages.example/simple \
156
+ --uv-executable /opt/uv/bin/uv \
81
157
  --output constraints.txt
82
158
  ```
83
159
 
@@ -111,8 +187,8 @@ through the environment.
111
187
 
112
188
  ## Deploy from VCS
113
189
 
114
- `--package` is passed directly to `uv tool install`, so VCS package specs are
115
- supported.
190
+ `--package` is passed directly to `uv tool install`, so Git package specs are
191
+ supported. Pin a tag, branch, or commit to keep deployments reproducible.
116
192
 
117
193
  ```sh
118
194
  module-manager deploy-python mytool 1.0.0 \
@@ -122,6 +198,16 @@ module-manager deploy-python mytool 1.0.0 \
122
198
  --execute-install
123
199
  ```
124
200
 
201
+ For an SSH Git URL:
202
+
203
+ ```sh
204
+ module-manager deploy-python mytool 1.0.0 \
205
+ --package 'git+ssh://git@github.com/org/repo.git@v1.0.0' \
206
+ --prefix /prod/tools \
207
+ --module-root /prod/modulefiles \
208
+ --execute-install
209
+ ```
210
+
125
211
  For a package in a repository subdirectory:
126
212
 
127
213
  ```sh
@@ -49,9 +49,19 @@ 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] \
64
+ [--uv-executable PATH] \
55
65
  [--prefix PATH] \
56
66
  [--module-root PATH] \
57
67
  [--no-default] \
@@ -66,7 +76,8 @@ module-manager auto-constraints PACKAGE \
66
76
  [--python PYTHON] \
67
77
  [--index URL] \
68
78
  [--find-links PATH_OR_URL] \
69
- [--uv-config-file PATH]
79
+ [--uv-config-file PATH] \
80
+ [--uv-executable PATH]
70
81
  ```
71
82
 
72
83
  ## Rust Command
@@ -77,6 +88,7 @@ module-manager deploy-rust NAME VERSION \
77
88
  [--prefix PATH] \
78
89
  [--module-root PATH] \
79
90
  [--no-default] \
91
+ [--uv-executable PATH] \
80
92
  [--dry-run]
81
93
  ```
82
94
 
@@ -1,3 +1,3 @@
1
1
  """Deployment helpers for HPC environment module files."""
2
2
 
3
- __version__ = "0.7.0"
3
+ __version__ = "0.9.0"
@@ -203,23 +203,50 @@ 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,
221
243
  help="uv.toml file passed to uv tool install with --config-file.",
222
244
  )
245
+ @click.option(
246
+ "--uv-executable",
247
+ type=PATH,
248
+ help="uv executable path. Defaults to uv on PATH.",
249
+ )
223
250
  @click.option(
224
251
  "--execute-install",
225
252
  is_flag=True,
@@ -238,9 +265,19 @@ def deploy_python(
238
265
  package: str,
239
266
  python: str | None,
240
267
  indexes: tuple[str, ...],
268
+ default_index: str | None,
241
269
  find_links: tuple[str, ...],
270
+ no_index: bool,
271
+ index_strategy: str | None,
272
+ keyring_provider: str | None,
242
273
  constraints: tuple[str, ...],
274
+ no_cache: bool,
275
+ refresh: bool,
276
+ refresh_packages: tuple[str, ...],
277
+ force: bool,
278
+ reinstall: bool,
243
279
  uv_config_file: Path | None,
280
+ uv_executable: Path | None,
244
281
  execute_install: bool,
245
282
  ) -> None:
246
283
  """Write a modulefile for a [cyan]uv tool install[/cyan] Python CLI.
@@ -257,9 +294,19 @@ def deploy_python(
257
294
  package: Package spec passed to `uv tool install`.
258
295
  python: Optional Python interpreter or version passed to uv.
259
296
  indexes: Additional package index URLs passed to uv.
297
+ default_index: Default package index URL passed to uv.
260
298
  find_links: Wheelhouse directories or HTML package pages passed to uv.
299
+ no_index: Whether uv should ignore registry indexes.
300
+ index_strategy: Package index strategy passed to uv.
301
+ keyring_provider: Keyring provider passed to uv.
261
302
  constraints: Requirements constraint files passed to uv.
303
+ no_cache: Whether uv should avoid reading from or writing to cache.
304
+ refresh: Whether uv should refresh cached data.
305
+ refresh_packages: Packages whose cached data uv should refresh.
306
+ force: Whether uv should replace existing executable entries.
307
+ reinstall: Whether uv should reinstall all packages.
262
308
  uv_config_file: Optional uv configuration file passed to `uv tool`.
309
+ uv_executable: Optional uv executable path.
263
310
  execute_install: Whether to run `uv tool install` immediately.
264
311
 
265
312
  Raises:
@@ -282,9 +329,19 @@ def deploy_python(
282
329
  homepage=homepage,
283
330
  python=python,
284
331
  indexes=indexes or config.indexes,
332
+ default_index=default_index,
285
333
  find_links=find_links or config.find_links,
334
+ no_index=no_index,
335
+ index_strategy=index_strategy,
336
+ keyring_provider=keyring_provider,
286
337
  constraints=constraints,
338
+ no_cache=no_cache,
339
+ refresh=refresh,
340
+ refresh_packages=refresh_packages,
341
+ force=force,
342
+ reinstall=reinstall,
287
343
  uv_config_file=(uv_config_file.expanduser() if uv_config_file else config.uv_config_file),
344
+ uv_executable=(uv_executable.expanduser() if uv_executable else config.uv_executable),
288
345
  execute_install=execute_install,
289
346
  make_default=make_default,
290
347
  )
@@ -325,6 +382,11 @@ def deploy_python(
325
382
  type=PATH,
326
383
  help="uv.toml file passed to uv with --config-file.",
327
384
  )
385
+ @click.option(
386
+ "--uv-executable",
387
+ type=PATH,
388
+ help="uv executable path. Defaults to uv on PATH.",
389
+ )
328
390
  @click.pass_obj
329
391
  def auto_constraints(
330
392
  config: AppConfig,
@@ -334,6 +396,7 @@ def auto_constraints(
334
396
  indexes: tuple[str, ...],
335
397
  find_links: tuple[str, ...],
336
398
  uv_config_file: Path | None,
399
+ uv_executable: Path | None,
337
400
  ) -> None:
338
401
  """Generate a constraints file for a Python package.
339
402
 
@@ -345,6 +408,7 @@ def auto_constraints(
345
408
  indexes: Additional package index URLs passed to uv.
346
409
  find_links: Wheelhouse directories or HTML package pages passed to uv.
347
410
  uv_config_file: Optional uv configuration file passed to uv.
411
+ uv_executable: Optional uv executable path.
348
412
 
349
413
  Raises:
350
414
  click.ClickException: If uv cannot generate constraints.
@@ -358,6 +422,7 @@ def auto_constraints(
358
422
  indexes=indexes or config.indexes,
359
423
  find_links=find_links or config.find_links,
360
424
  uv_config_file=(uv_config_file.expanduser() if uv_config_file else config.uv_config_file),
425
+ uv_executable=(uv_executable.expanduser() if uv_executable else config.uv_executable),
361
426
  )
362
427
  except ConstraintGenerationError as error:
363
428
  raise click.ClickException(str(error)) from error
@@ -543,6 +608,11 @@ def deploy_script(
543
608
  is_flag=True,
544
609
  help="Print actions that would run without creating files.",
545
610
  )
611
+ @click.option(
612
+ "--uv-executable",
613
+ type=PATH,
614
+ help="uv executable path for Python tools. Defaults to uv on PATH.",
615
+ )
546
616
  @click.pass_obj
547
617
  def deploy_env(
548
618
  config: AppConfig,
@@ -551,6 +621,7 @@ def deploy_env(
551
621
  prefix: Path | None,
552
622
  make_default: bool | None,
553
623
  dry_run: bool,
624
+ uv_executable: Path | None,
554
625
  ) -> None:
555
626
  """Deploy a collective environment from a TOML manifest.
556
627
 
@@ -562,6 +633,7 @@ def deploy_env(
562
633
  prefix: Optional install prefix overriding manifest and configuration.
563
634
  make_default: Optional override for manifest default behavior.
564
635
  dry_run: Whether to report actions without mutating the filesystem.
636
+ uv_executable: Optional uv executable path for Python tools.
565
637
 
566
638
  Raises:
567
639
  click.ClickException: If the manifest cannot be parsed.
@@ -588,7 +660,13 @@ def deploy_env(
588
660
  make_default=make_default,
589
661
  )
590
662
 
591
- result = deploy_environment(spec=spec, module_root=resolved_module_root, prefix=resolved_prefix, dry_run=dry_run)
663
+ result = deploy_environment(
664
+ spec=spec,
665
+ module_root=resolved_module_root,
666
+ prefix=resolved_prefix,
667
+ uv_executable=(uv_executable.expanduser() if uv_executable else config.uv_executable),
668
+ dry_run=dry_run,
669
+ )
592
670
  print_environment_result(result, dry_run)
593
671
 
594
672