sdkmgr 0.1.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.
- sdkmgr-0.1.0/.gitignore +7 -0
- sdkmgr-0.1.0/CHANGELOG.md +38 -0
- sdkmgr-0.1.0/LICENSE +28 -0
- sdkmgr-0.1.0/PKG-INFO +382 -0
- sdkmgr-0.1.0/README.md +355 -0
- sdkmgr-0.1.0/pyproject.toml +56 -0
- sdkmgr-0.1.0/scripts/check_release.py +72 -0
- sdkmgr-0.1.0/scripts/smoke_android.py +27 -0
- sdkmgr-0.1.0/scripts/smoke_node.py +43 -0
- sdkmgr-0.1.0/sdkmgr/__init__.py +8 -0
- sdkmgr-0.1.0/sdkmgr/__main__.py +3 -0
- sdkmgr-0.1.0/sdkmgr/android.py +290 -0
- sdkmgr-0.1.0/sdkmgr/bootstrap.py +79 -0
- sdkmgr-0.1.0/sdkmgr/cli.py +63 -0
- sdkmgr-0.1.0/sdkmgr/core.py +103 -0
- sdkmgr-0.1.0/sdkmgr/download.py +59 -0
- sdkmgr-0.1.0/sdkmgr/errors.py +10 -0
- sdkmgr-0.1.0/sdkmgr/installation.py +58 -0
- sdkmgr-0.1.0/sdkmgr/java.py +128 -0
- sdkmgr-0.1.0/sdkmgr/mise.py +77 -0
- sdkmgr-0.1.0/sdkmgr/nvm.py +148 -0
- sdkmgr-0.1.0/sdkmgr/providers.py +43 -0
- sdkmgr-0.1.0/sdkmgr/system.py +52 -0
- sdkmgr-0.1.0/tests/conftest.py +18 -0
- sdkmgr-0.1.0/tests/test_android.py +344 -0
- sdkmgr-0.1.0/tests/test_bootstrap.py +89 -0
- sdkmgr-0.1.0/tests/test_download.py +153 -0
- sdkmgr-0.1.0/tests/test_java.py +207 -0
- sdkmgr-0.1.0/tests/test_manager.py +58 -0
- sdkmgr-0.1.0/tests/test_mise.py +186 -0
- sdkmgr-0.1.0/tests/test_nvm.py +215 -0
- sdkmgr-0.1.0/tests/test_smoke_scripts.py +36 -0
- sdkmgr-0.1.0/uv.lock +212 -0
sdkmgr-0.1.0/.gitignore
ADDED
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# Changelog
|
|
2
|
+
|
|
3
|
+
## Unreleased
|
|
4
|
+
|
|
5
|
+
## 0.1.0 — 2026-10-18
|
|
6
|
+
|
|
7
|
+
- Use PyJS's BSD-3-Clause license, copyright, author identity, and community
|
|
8
|
+
metadata for this companion project.
|
|
9
|
+
- Keep failed or interrupted downloads out of their destination paths and
|
|
10
|
+
preserve existing files. Report malformed redirects without tracebacks.
|
|
11
|
+
- List Android packages offline even when command-line tools are absent.
|
|
12
|
+
- Validate wheel and source installations against the packaged tests, with a
|
|
13
|
+
single source for the package and CLI version.
|
|
14
|
+
- Keep the Android installation smoke's bootstrap inside its temporary SDK.
|
|
15
|
+
- Validate original ZIP member names before Windows path normalization or
|
|
16
|
+
NUL truncation can hide malformed archive entries.
|
|
17
|
+
|
|
18
|
+
- Provision Android SDK package sets through Google's `android sdk` CLI,
|
|
19
|
+
reusing existing SDKs and delegating package dependencies to Google.
|
|
20
|
+
- Bootstrap missing Android tools from pinned, checksum-verified archives,
|
|
21
|
+
with explicit download-license consent and safe staged extraction.
|
|
22
|
+
- Add offline Android package listings, explicit installation consent, install
|
|
23
|
+
locking, and diagnostics for partial installations. Disable CLI metrics.
|
|
24
|
+
- Use slash-separated Android package IDs; omit the deprecated `sdkmanager`
|
|
25
|
+
adapter, license-review command, and unnecessary external JDK dependency.
|
|
26
|
+
|
|
27
|
+
- Discover JDK 21 and 25 across environment, system, and package locations.
|
|
28
|
+
- Install JDKs through apt, dnf, pacman, Homebrew, or WinGet after confirmation.
|
|
29
|
+
- Support Arch/Omarchy without refreshing databases or upgrading the system.
|
|
30
|
+
- Prefer existing mise for Node, reusing inactive versions without switching
|
|
31
|
+
global defaults or running project hooks.
|
|
32
|
+
- Defer privileged authentication to the OS and support manual-install retries.
|
|
33
|
+
- Return executable paths and subprocess-local environments.
|
|
34
|
+
- Reuse existing nvm when mise is absent; otherwise bootstrap a pinned,
|
|
35
|
+
checksum-verified mise without editing shell profiles.
|
|
36
|
+
- Replace the direct Node downloader with manager-owned provisioning.
|
|
37
|
+
- Preserve Unix nvm defaults by staging installations before publishing them.
|
|
38
|
+
- Add real-provider CI smoke jobs alongside the isolated unit tests.
|
sdkmgr-0.1.0/LICENSE
ADDED
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
BSD 3-Clause License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025, Lex Berezhny
|
|
4
|
+
|
|
5
|
+
Redistribution and use in source and binary forms, with or without
|
|
6
|
+
modification, are permitted provided that the following conditions are met:
|
|
7
|
+
|
|
8
|
+
1. Redistributions of source code must retain the above copyright notice, this
|
|
9
|
+
list of conditions and the following disclaimer.
|
|
10
|
+
|
|
11
|
+
2. Redistributions in binary form must reproduce the above copyright notice,
|
|
12
|
+
this list of conditions and the following disclaimer in the documentation
|
|
13
|
+
and/or other materials provided with the distribution.
|
|
14
|
+
|
|
15
|
+
3. Neither the name of the copyright holder nor the names of its
|
|
16
|
+
contributors may be used to endorse or promote products derived from
|
|
17
|
+
this software without specific prior written permission.
|
|
18
|
+
|
|
19
|
+
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
20
|
+
AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
21
|
+
IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
|
|
22
|
+
DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE
|
|
23
|
+
FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
|
|
24
|
+
DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR
|
|
25
|
+
SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER
|
|
26
|
+
CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
|
|
27
|
+
OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
|
28
|
+
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
sdkmgr-0.1.0/PKG-INFO
ADDED
|
@@ -0,0 +1,382 @@
|
|
|
1
|
+
Metadata-Version: 2.5
|
|
2
|
+
Name: sdkmgr
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Discover and provision Node, Java, and Android development tools.
|
|
5
|
+
Project-URL: Homepage, https://github.com/pyjs/sdkmgr
|
|
6
|
+
Project-URL: Release notes, https://github.com/pyjs/sdkmgr/releases
|
|
7
|
+
Project-URL: Source, https://github.com/pyjs/sdkmgr
|
|
8
|
+
Project-URL: Tracker, https://github.com/pyjs/sdkmgr/issues
|
|
9
|
+
Project-URL: Discord, https://discord.gg/YktKpQPG7
|
|
10
|
+
Project-URL: X, https://x.com/pyjsdev
|
|
11
|
+
Author-email: Lex Berezhny <lex@damoti.com>
|
|
12
|
+
License-Expression: BSD-3-Clause
|
|
13
|
+
License-File: LICENSE
|
|
14
|
+
Keywords: android,java,node,sdk,toolchain
|
|
15
|
+
Classifier: Development Status :: 3 - Alpha
|
|
16
|
+
Classifier: Intended Audience :: Developers
|
|
17
|
+
Classifier: Operating System :: OS Independent
|
|
18
|
+
Classifier: Programming Language :: Python
|
|
19
|
+
Classifier: Programming Language :: Python :: 3
|
|
20
|
+
Classifier: Programming Language :: Python :: 3 :: Only
|
|
21
|
+
Classifier: Topic :: Software Development :: Build Tools
|
|
22
|
+
Requires-Python: >=3.12
|
|
23
|
+
Requires-Dist: filelock>=3.16
|
|
24
|
+
Requires-Dist: httpx<1,>=0.27
|
|
25
|
+
Requires-Dist: platformdirs>=4
|
|
26
|
+
Description-Content-Type: text/markdown
|
|
27
|
+
|
|
28
|
+
# sdkmgr
|
|
29
|
+
|
|
30
|
+
A Python library and command-line tool for finding development SDKs and
|
|
31
|
+
installing missing tools through established package managers.
|
|
32
|
+
|
|
33
|
+
This is an alpha project. Java provisioning supports **JDK 21 and 25** through
|
|
34
|
+
apt, dnf, pacman, Homebrew, or WinGet.
|
|
35
|
+
There is no JDK fallback,
|
|
36
|
+
vendor requirement for existing installations, or exact patch-version pinning.
|
|
37
|
+
|
|
38
|
+
Node provisioning prefers existing mise, otherwise existing nvm, otherwise
|
|
39
|
+
bootstraps mise. sdkmgr no longer downloads Node archives itself. Android
|
|
40
|
+
provisioning delegates package installation to Google's command-line tools.
|
|
41
|
+
LLVM compilation dependencies follow in a later version.
|
|
42
|
+
|
|
43
|
+
## Install
|
|
44
|
+
|
|
45
|
+
Python 3.12 or newer is required. Install the CLI in an isolated environment:
|
|
46
|
+
|
|
47
|
+
```sh
|
|
48
|
+
uv tool install sdkmgr
|
|
49
|
+
sdkmgr --help
|
|
50
|
+
```
|
|
51
|
+
|
|
52
|
+
For a library dependency, use `pip install sdkmgr` in your environment.
|
|
53
|
+
To try unreleased source, use
|
|
54
|
+
`uv tool install git+https://github.com/pyjs/sdkmgr.git` instead.
|
|
55
|
+
Installing sdkmgr itself does not download any SDK or change tool selections.
|
|
56
|
+
|
|
57
|
+
## Android SDK packages
|
|
58
|
+
|
|
59
|
+
```sh
|
|
60
|
+
sdkmgr android list
|
|
61
|
+
sdkmgr android install platform-tools platforms/android-35 build-tools/35.0.1
|
|
62
|
+
```
|
|
63
|
+
|
|
64
|
+
Android provisioning uses Google's **`android sdk` CLI only**. There is no
|
|
65
|
+
legacy `sdkmanager` adapter or fallback. Package IDs use the CLI's slash
|
|
66
|
+
syntax; sdkmgr translates semicolons only when reading Google's package
|
|
67
|
+
records. Existing SDK packages do not need to be reinstalled when changing
|
|
68
|
+
the management tool.
|
|
69
|
+
|
|
70
|
+
Root selection is `--sdk-root`, then `ANDROID_HOME`/`ANDROID_SDK_ROOT`, then
|
|
71
|
+
the standard user location on Linux, macOS, or Windows. Conflicting environment
|
|
72
|
+
variables require an explicit root. The install command prints the SDK root
|
|
73
|
+
to stdout; progress goes to stderr.
|
|
74
|
+
|
|
75
|
+
Installation reuses registered packages without upgrading them, verifies key
|
|
76
|
+
files for platform/build tools, and delegates dependencies to Google.
|
|
77
|
+
Packages such as `platform-tools` select Google's stable release when absent,
|
|
78
|
+
not a pinned revision. This is not yet a reproducible package lock.
|
|
79
|
+
The `list` command reads local records without network access and does not
|
|
80
|
+
require the Android CLI. A missing SDK root produces an empty listing.
|
|
81
|
+
|
|
82
|
+
Before downloading the CLI or installing missing packages, review Google's
|
|
83
|
+
[SDK terms](https://developer.android.com/studio/terms) and the requested
|
|
84
|
+
packages' licenses, then explicitly confirm acceptance:
|
|
85
|
+
|
|
86
|
+
```sh
|
|
87
|
+
sdkmgr android install --accept-sdk-license platform-tools platforms/android-35
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
This flag acknowledges prior acceptance; sdkmgr never supplies automatic
|
|
91
|
+
affirmative input. Google's new CLI no longer provides the old
|
|
92
|
+
`--licenses` flow, so sdkmgr requires confirmation before invoking installation,
|
|
93
|
+
even if the CLI would proceed without prompting. Reusing already installed
|
|
94
|
+
packages does not require the flag. Failed or incomplete installs are reported
|
|
95
|
+
even when Google's command exits successfully.
|
|
96
|
+
|
|
97
|
+
The CLI bootstrap archive is pinned and SHA-256 verified, extracted in staging,
|
|
98
|
+
then atomically published to a separate command-tools directory. **The launcher
|
|
99
|
+
fetches Google's CLI payload on first use.** That subsequent payload and its
|
|
100
|
+
cache are managed by Google, not pinned or independently verified by sdkmgr.
|
|
101
|
+
Commands launched by sdkmgr include `--no-metrics` and an explicit `--sdk`.
|
|
102
|
+
Discovery and no-op provisioning do not launch the CLI.
|
|
103
|
+
|
|
104
|
+
Existing tools are not replaced. SDK installs are serialized among sdkmgr
|
|
105
|
+
callers; do not run Android Studio's SDK installer concurrently. Interrupted
|
|
106
|
+
installs are rechecked on retry; registered but damaged packages need explicit
|
|
107
|
+
repair. No external JDK is required for provisioning. Obtain one separately
|
|
108
|
+
with `ensure("java", version="21")` when your build needs it.
|
|
109
|
+
|
|
110
|
+
Bootstrap covers Linux x64, macOS x64/ARM64, and Windows x64. Other hosts need
|
|
111
|
+
compatible command-line tools installed separately. Windows/macOS support has
|
|
112
|
+
simulated tests but still needs real installation verification. Linux bootstrap
|
|
113
|
+
and existing SDK reuse are verified locally. See the
|
|
114
|
+
[CLI reference](https://developer.android.com/tools/agents/android-cli).
|
|
115
|
+
|
|
116
|
+
```python
|
|
117
|
+
from sdkmgr import Manager
|
|
118
|
+
|
|
119
|
+
sdk = Manager().ensure_android(
|
|
120
|
+
["platform-tools", "platforms/android-35", "build-tools/35.0.1"],
|
|
121
|
+
accept_sdk_license=True, # Only after reviewing and accepting the terms.
|
|
122
|
+
)
|
|
123
|
+
adb = sdk.executable("adb")
|
|
124
|
+
environment = sdk.environment()
|
|
125
|
+
```
|
|
126
|
+
|
|
127
|
+
The returned environment sets SDK paths without changing your shell. Emulator
|
|
128
|
+
packages and system images must be requested explicitly. Provisioning never
|
|
129
|
+
creates an AVD or starts an emulator.
|
|
130
|
+
|
|
131
|
+
For an opt-in live smoke, after accepting the terms:
|
|
132
|
+
|
|
133
|
+
```sh
|
|
134
|
+
python scripts/smoke_android.py --accept-sdk-license
|
|
135
|
+
```
|
|
136
|
+
|
|
137
|
+
The smoke bootstraps tools and installs API 35 into a temporary SDK, then
|
|
138
|
+
checks a no-op retry and removes that SDK. Google's separate CLI payload cache
|
|
139
|
+
may persist; no packages are added to the user's existing SDK.
|
|
140
|
+
|
|
141
|
+
This bootstraps the CLI if needed, installs API 35 into a temporary SDK,
|
|
142
|
+
verifies it, checks a no-op retry, and removes the temporary packages.
|
|
143
|
+
|
|
144
|
+
## Java from the command line
|
|
145
|
+
|
|
146
|
+
```sh
|
|
147
|
+
sdkmgr install java@21
|
|
148
|
+
sdkmgr install java@25
|
|
149
|
+
sdkmgr which java@21
|
|
150
|
+
sdkmgr list
|
|
151
|
+
```
|
|
152
|
+
|
|
153
|
+
`which` only discovers installations. `install` first looks for a complete,
|
|
154
|
+
working JDK of the requested major version. It accepts maintenance updates
|
|
155
|
+
and does not require Temurin. A runtime without `javac` and `jar` is not enough.
|
|
156
|
+
|
|
157
|
+
Discovery checks `JAVA_HOME`, resolved executables on `PATH`, and platform
|
|
158
|
+
installation locations. macOS also uses `java_home` and Homebrew's prefix;
|
|
159
|
+
Windows also checks registered JDK uninstall locations. It verifies both
|
|
160
|
+
`java` and `javac` with ten-second subprocess timeouts.
|
|
161
|
+
|
|
162
|
+
If the JDK is missing, sdkmgr shows the package-manager command:
|
|
163
|
+
|
|
164
|
+
```text
|
|
165
|
+
JDK installation requires this command:
|
|
166
|
+
|
|
167
|
+
/usr/bin/sudo -- /usr/bin/apt-get install openjdk-21-jdk
|
|
168
|
+
|
|
169
|
+
You can quit, run it yourself, and restart your original command.
|
|
170
|
+
Completed installations will be detected automatically.
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
Press Enter to continue, or type `q` to exit. Authentication is handled by
|
|
174
|
+
sudo or the Windows installer, never by sdkmgr. On typical sudo systems,
|
|
175
|
+
the password requested is your account password, not the root password.
|
|
176
|
+
Package-manager confirmation and license prompts remain visible.
|
|
177
|
+
|
|
178
|
+
If you exit, run the displayed command yourself and restart the command you
|
|
179
|
+
originally ran. Discovery happens again, so sdkmgr proceeds without repeating
|
|
180
|
+
the completed installation. No separate resume flag or checkpoint is needed.
|
|
181
|
+
|
|
182
|
+
Without an interactive terminal, or with `--non-interactive`, missing Java
|
|
183
|
+
produces instructions and exit status **2** without running an installer:
|
|
184
|
+
|
|
185
|
+
```sh
|
|
186
|
+
sdkmgr --non-interactive install java@21
|
|
187
|
+
```
|
|
188
|
+
|
|
189
|
+
Successful `install` and `which` print the executable path to stdout.
|
|
190
|
+
Messages and package-manager output go to stderr. Other expected failures
|
|
191
|
+
return status 1; interruption returns 130.
|
|
192
|
+
|
|
193
|
+
## Package providers
|
|
194
|
+
|
|
195
|
+
| Platform | Installation command, before elevation |
|
|
196
|
+
| --- | --- |
|
|
197
|
+
| Debian/Ubuntu | `apt-get install openjdk-21-jdk` |
|
|
198
|
+
| Fedora/RHEL family | `dnf install java-21-openjdk-devel` |
|
|
199
|
+
| Arch/Omarchy | `pacman -S --needed jdk21-openjdk` |
|
|
200
|
+
| macOS | `brew install openjdk@21` |
|
|
201
|
+
| Windows | `winget install --exact --id Microsoft.OpenJDK.21 --source winget` |
|
|
202
|
+
|
|
203
|
+
Java 25 uses the corresponding `25` package names. Linux commands use sudo
|
|
204
|
+
unless already running as root. Homebrew is never run through sudo by sdkmgr;
|
|
205
|
+
Windows installers handle elevation through the OS.
|
|
206
|
+
|
|
207
|
+
The package manager must already be installed. sdkmgr does not add repositories,
|
|
208
|
+
refresh package indexes, install Homebrew, accept licenses automatically, or
|
|
209
|
+
silently substitute another major version. If the requested package is absent
|
|
210
|
+
from your OS repositories, installation fails with the package-manager output.
|
|
211
|
+
Install an appropriate JDK yourself or use a supported OS release, then retry.
|
|
212
|
+
|
|
213
|
+
Arch and Omarchy require complete system updates; partial upgrades are not
|
|
214
|
+
supported. sdkmgr does not run `pacman -Sy` or perform a whole-system upgrade.
|
|
215
|
+
If packages cannot be installed, use your normal system updater (Omarchy's
|
|
216
|
+
updater on Omarchy), then retry the original sdkmgr command.
|
|
217
|
+
|
|
218
|
+
sdkmgr does not explicitly change Java alternatives, shell profiles, or global
|
|
219
|
+
environment variables. Package-manager installers can have their own system
|
|
220
|
+
side effects. Security updates remain the responsibility of the package manager
|
|
221
|
+
and the user's update policy; `ensure()` does not force upgrades.
|
|
222
|
+
|
|
223
|
+
## Node manager selection
|
|
224
|
+
|
|
225
|
+
```sh
|
|
226
|
+
sdkmgr install node@22.14.0
|
|
227
|
+
sdkmgr which node@22.14.0
|
|
228
|
+
```
|
|
229
|
+
|
|
230
|
+
Selection follows this order:
|
|
231
|
+
|
|
232
|
+
1. Existing mise on `PATH`, in `~/.local/bin`, or previously bootstrapped by
|
|
233
|
+
sdkmgr under its private root.
|
|
234
|
+
2. Existing Unix nvm or nvm-windows.
|
|
235
|
+
3. Bootstrap a pinned mise if neither manager is present.
|
|
236
|
+
|
|
237
|
+
A selected manager's failure does not trigger fallback to another provider.
|
|
238
|
+
`which` and `list` never bootstrap managers or install Node. A standalone Node
|
|
239
|
+
on `PATH` does not bypass this selection order.
|
|
240
|
+
|
|
241
|
+
### Mise
|
|
242
|
+
|
|
243
|
+
When mise is present, sdkmgr prefers it, including
|
|
244
|
+
versions that are installed but not currently active. It requests an exact
|
|
245
|
+
version through mise's built-in Node provider, validates `node --version`, and
|
|
246
|
+
requires the bundled npm and npx executables. A missing version is installed
|
|
247
|
+
in mise's storage, not sdkmgr's cache. No sudo is used.
|
|
248
|
+
|
|
249
|
+
Discovery uses `mise ls --installed --json core:node`; installation uses
|
|
250
|
+
`mise install core:node@VERSION --jobs=1`. sdkmgr does not run `mise use`,
|
|
251
|
+
activate a shell, edit project configuration, or switch global defaults.
|
|
252
|
+
Returned paths bypass shims, and `environment()` selects Node only for the
|
|
253
|
+
subprocess receiving that environment.
|
|
254
|
+
|
|
255
|
+
The adapter requires mise's `--no-config` and `--no-hooks` options. These
|
|
256
|
+
prevent unrelated project configuration or hooks from running. It also
|
|
257
|
+
disables source compilation and the default extra-npm-packages file. Existing
|
|
258
|
+
environment settings, including mise's data directory, remain available.
|
|
259
|
+
Configuration-file-only customizations are intentionally not loaded.
|
|
260
|
+
|
|
261
|
+
A broken or incompatible mise installation produces an error instead of a
|
|
262
|
+
silent fallback to another installer. Upgrade or repair mise and retry.
|
|
263
|
+
Discovery commands have a 30-second timeout; installation has ten minutes and
|
|
264
|
+
sends progress to stderr. `find()` never installs a missing version.
|
|
265
|
+
|
|
266
|
+
Bootstrap downloads mise 2026.9.12 from its official GitHub release and checks
|
|
267
|
+
the binary against a SHA-256 pinned in sdkmgr before executing it. No shell
|
|
268
|
+
installer runs. Linux uses the musl binary; macOS and Windows use their native
|
|
269
|
+
binaries. Both x64 and ARM64 assets are pinned. The executable is installed
|
|
270
|
+
atomically under `ROOT/managers/`, without changing `PATH` or shell profiles.
|
|
271
|
+
Concurrent bootstrap calls share a lock. A corrupt cached binary produces an
|
|
272
|
+
error rather than being silently overwritten.
|
|
273
|
+
|
|
274
|
+
### Nvm
|
|
275
|
+
|
|
276
|
+
Unix discovery checks `NVM_DIR`, `~/.nvm`, `XDG_CONFIG_HOME/nvm`, and common
|
|
277
|
+
Homebrew/Arch script locations. Inactive versions are reusable. Installation
|
|
278
|
+
sources the existing script in a noninteractive Bash subprocess without
|
|
279
|
+
loading shell profiles. It requests a prebuilt Node and skips default extra
|
|
280
|
+
npm packages. To avoid nvm creating a default alias, installation uses a
|
|
281
|
+
temporary `NVM_DIR`; only the validated version is moved into the user's store.
|
|
282
|
+
|
|
283
|
+
On Windows, sdkmgr discovers `nvm.exe` through `PATH` or `NVM_HOME`, then asks
|
|
284
|
+
nvm-windows v1 or v2 for its installation root. It installs missing versions
|
|
285
|
+
without invoking `nvm use`. Existing manager settings still apply, including
|
|
286
|
+
v2's configured extra npm packages. Version 2 automatically activates its first
|
|
287
|
+
Node installation, so an empty v2 store produces manual-install instructions
|
|
288
|
+
and exit status 2. Run the displayed command and retry. sdkmgr does not modify
|
|
289
|
+
registry preferences or undo manager-owned state changes.
|
|
290
|
+
|
|
291
|
+
`--non-interactive` disables prompts for privileged Java installation; ordinary
|
|
292
|
+
Node provisioning and mise bootstrap remain automatic, including in CI.
|
|
293
|
+
|
|
294
|
+
## Python integration
|
|
295
|
+
|
|
296
|
+
```python
|
|
297
|
+
import subprocess
|
|
298
|
+
|
|
299
|
+
from sdkmgr import Manager
|
|
300
|
+
|
|
301
|
+
manager = Manager()
|
|
302
|
+
jdk = manager.ensure("java", version="21")
|
|
303
|
+
|
|
304
|
+
subprocess.run(
|
|
305
|
+
[jdk.executable("javac"), "Example.java"],
|
|
306
|
+
env=jdk.environment(),
|
|
307
|
+
check=True,
|
|
308
|
+
)
|
|
309
|
+
```
|
|
310
|
+
|
|
311
|
+
`environment()` returns a new complete environment with the selected JDK's
|
|
312
|
+
`bin` directory first in `PATH` and its home in `JAVA_HOME`. Pass an explicit
|
|
313
|
+
mapping to `environment(base)` if needed. Neither form mutates `os.environ`.
|
|
314
|
+
`Installation.version` records the detected runtime version, such as
|
|
315
|
+
`21.0.12`, rather than just the requested major.
|
|
316
|
+
|
|
317
|
+
`find("java", version="21")` returns an installation or `None` without
|
|
318
|
+
installing anything. `installed()` returns discovered Java 21/25 installations
|
|
319
|
+
and versions from the selected Node manager. Old prototype Node caches are not
|
|
320
|
+
used or removed.
|
|
321
|
+
|
|
322
|
+
For unattended callers, disable prompting explicitly:
|
|
323
|
+
|
|
324
|
+
```python
|
|
325
|
+
from sdkmgr import InstallationRequired, Manager
|
|
326
|
+
|
|
327
|
+
manager = Manager(interactive=False)
|
|
328
|
+
try:
|
|
329
|
+
jdk = manager.ensure("java", version="21")
|
|
330
|
+
except InstallationRequired as error:
|
|
331
|
+
required_command = error.command
|
|
332
|
+
raise
|
|
333
|
+
```
|
|
334
|
+
|
|
335
|
+
`InstallationRequired.command` is an argument tuple; no shell parsing is needed.
|
|
336
|
+
`InstallationRequired` is an `SDKError`. Library calls never terminate the
|
|
337
|
+
calling process. Even `interactive=True` cannot launch an installer without
|
|
338
|
+
terminal input and terminal stderr. Installer processes inherit stdin;
|
|
339
|
+
sdkmgr never reads, stores, forwards, or logs a password.
|
|
340
|
+
|
|
341
|
+
After an installer reports success, discovery runs again. If its installation
|
|
342
|
+
location is not recognized, set `JAVA_HOME` and retry. Long-running interactive
|
|
343
|
+
package installations are not given an arbitrary timeout while authentication
|
|
344
|
+
or user confirmation may be pending.
|
|
345
|
+
|
|
346
|
+
## Development
|
|
347
|
+
|
|
348
|
+
Python 3.12 or newer is required. Activate the environment after `uv sync`:
|
|
349
|
+
|
|
350
|
+
```sh
|
|
351
|
+
uv sync
|
|
352
|
+
source .venv/bin/activate
|
|
353
|
+
pytest
|
|
354
|
+
ruff check .
|
|
355
|
+
python scripts/check_release.py
|
|
356
|
+
```
|
|
357
|
+
|
|
358
|
+
The default tests use simulated SDKs and installers. They do not install
|
|
359
|
+
packages, ask for passwords, or depend on installed Java, mise, or nvm versions.
|
|
360
|
+
The CI configuration covers Python 3.12 and 3.14 on Linux, macOS, and Windows.
|
|
361
|
+
The release check builds both distributions in temporary storage, audits their
|
|
362
|
+
contents, installs each into a fresh environment, and runs the packaged tests
|
|
363
|
+
outside the checkout. It does not publish anything. `uv build` creates the
|
|
364
|
+
wheel and source archive in `dist/` when you are ready to retain artifacts.
|
|
365
|
+
The Python 3.12/3.14 tests and installed-artifact checks pass on all three
|
|
366
|
+
systems. Real mise/Node smokes pass on Linux, macOS, and Windows; real nvm/Node
|
|
367
|
+
smokes pass on Linux and macOS. Real OS JDK installation still needs
|
|
368
|
+
verification on those platforms.
|
|
369
|
+
Do not run an automatic source formatter.
|
|
370
|
+
|
|
371
|
+
Separate CI jobs exercise real mise bootstrap and Node installation on Linux,
|
|
372
|
+
macOS, and Windows, plus Unix nvm on Linux and macOS. These networked smoke
|
|
373
|
+
tests are outside the default pytest run:
|
|
374
|
+
|
|
375
|
+
```sh
|
|
376
|
+
python scripts/smoke_node.py mise
|
|
377
|
+
python scripts/smoke_node.py nvm
|
|
378
|
+
```
|
|
379
|
+
|
|
380
|
+
The mise smoke uses temporary storage. The nvm smoke installs into the detected
|
|
381
|
+
nvm store: use a disposable `NVM_DIR` when testing missing versions. Native
|
|
382
|
+
nvm-windows installation still needs a real Windows smoke test.
|