Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
11 changes: 11 additions & 0 deletions docs/cli/generate/tool-stub.md
Original file line number Diff line number Diff line change
Expand Up @@ -56,6 +56,10 @@ HTTP backend type to use

**Default:** `http`

### `--lock`

Resolve and embed lockfile data (exact version + platform URLs/checksums) into an existing stub file for reproducible installs without runtime API calls

### `--platform-bin… <PLATFORM_BIN>`

Platform-specific binary paths in the format platform:path
Expand Down Expand Up @@ -130,4 +134,11 @@ $ mise generate tool-stub ./bin/tool --url "https://example.com/tool.tar.gz" --b

Generate a bootstrap stub with a pinned mise version:
$ mise generate tool-stub ./bin/tool --url "https://example.com/tool.tar.gz" --bootstrap --bootstrap-version 2025.1.0

Lock an existing tool stub with pinned version and platform URLs/checksums:
$ mise generate tool-stub ./bin/node --lock

Bump the version in a locked stub:
$ mise generate tool-stub ./bin/node --lock --version 22
# Resolves the latest node 22.x, pins it, and updates platform URLs/checksums
```
48 changes: 48 additions & 0 deletions docs/dev-tools/tool-stubs.md
Original file line number Diff line number Diff line change
Expand Up @@ -154,6 +154,7 @@ The generator will preserve existing configuration and merge new platforms into
- `--platform-url URL` - Add platform-specific URL with auto-detected platform from URL filename
- `--platform-bin PLATFORM:PATH` - Set platform-specific binary path
- `--skip-download` - Skip downloading for faster generation (no checksums or binary detection)
- `--lock` - Resolve and embed lockfile data (pinned version + platform URLs/checksums) into an existing stub

### Supported Archive Formats

Expand Down Expand Up @@ -221,6 +222,53 @@ tool = "github:cli/cli"
version = "latest"
```

### Locked Tool Stub

```bash
#!/usr/bin/env -S mise tool-stub

tool = "node"
version = "20.18.1"
bin = "node"

[lock.platforms.linux-x64]
url = "https://nodejs.org/dist/v20.18.1/node-v20.18.1-linux-x64.tar.xz"
checksum = "sha256:abc123..."

[lock.platforms.macos-arm64]
url = "https://nodejs.org/dist/v20.18.1/node-v20.18.1-darwin-arm64.tar.gz"
checksum = "sha256:def456..."
```

The `[lock]` section is generated by `mise generate tool-stub --lock` and provides
reproducible downloads with checksum verification. The tool/version fields are still
used for backend resolution, while lock data provides the download shortcuts.

::: tip
Locking is especially useful for avoiding GitHub API rate limits when users don't have a `GITHUB_TOKEN` set. With locked stubs, tools can be installed without any API calls at runtime.
:::

#### Locking a Stub

```bash
# Create a stub with a fuzzy version
mise generate tool-stub ./bin/node --version 20

# Lock it to pin the exact version and add platform URLs/checksums
mise generate tool-stub ./bin/node --lock
```

This resolves the version, fetches URLs for all common platforms (linux-x64, linux-arm64, macos-x64, macos-arm64, windows-x64), and writes them into a `[lock]` section in the stub.

#### Bumping a Locked Version

To bump the version of a locked stub, pass `--version` along with `--lock`:

```bash
# Bump to the latest node 22.x and re-lock
mise generate tool-stub ./bin/node --lock --version 22
```

### HTTP Backend with Platform Support

```bash
Expand Down
23 changes: 17 additions & 6 deletions e2e/backend/test_http_upgrade
Original file line number Diff line number Diff line change
Expand Up @@ -14,12 +14,23 @@ mkdir -p "$VERSION_LIST_DIR"
# Create version list file with two versions
echo -e "1.0.0\n2.0.0" >"$VERSION_LIST_DIR/versions.txt"

# Start Python HTTP server in background
(cd "$VERSION_LIST_DIR" && python3 -m http.server 18123 &>/dev/null) &
# Start Python HTTP server on a random available port
HTTP_PORT_FILE="$TMPDIR/http_server_port"
python3 -c "
import http.server, socketserver, threading, os
s = socketserver.TCPServer(('', 0), lambda *a: http.server.SimpleHTTPRequestHandler(*a, directory='$VERSION_LIST_DIR'))
with open('$HTTP_PORT_FILE', 'w') as f:
f.write(str(s.server_address[1]))
s.serve_forever()
" &
HTTP_SERVER_PID=$!

# Wait for server to start
sleep 1
# Wait for server to start and write port file
for _i in {1..20}; do
if [[ -f $HTTP_PORT_FILE ]]; then break; fi
sleep 0.1
done
HTTP_PORT=$(cat "$HTTP_PORT_FILE")

# Cleanup function
cleanup() {
Expand All @@ -28,7 +39,7 @@ cleanup() {
trap cleanup EXIT

# Verify server is running
if ! curl -s http://localhost:18123/versions.txt | grep -q "1.0.0"; then
if ! curl -s "http://localhost:$HTTP_PORT/versions.txt" | grep -q "1.0.0"; then
fail "HTTP server failed to start"
fi

Expand All @@ -38,7 +49,7 @@ cat <<EOF >mise.toml
[tools."http:hello-upgrade"]
version = "1.0.0"
url = "https://mise.jdx.dev/test-fixtures/hello-world-{{version}}.tar.gz"
version_list_url = "http://localhost:18123/versions.txt"
version_list_url = "http://localhost:$HTTP_PORT/versions.txt"
bin_path = "hello-world-1.0.0/bin"
postinstall = "chmod +x \$MISE_TOOL_INSTALL_PATH/hello-world-1.0.0/bin/hello-world"
EOF
Expand Down
64 changes: 64 additions & 0 deletions e2e/generate/test_generate_tool_stub_lock
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
#!/usr/bin/env bash
# shellcheck disable=SC2103

# Test mise generate tool-stub --lock functionality

# Disable GPG verification to avoid test failures
export MISE_GPG_VERIFY=false

# Create test project directory
mkdir -p generate_tool_stub_lock_test/bin
cd generate_tool_stub_lock_test

# Test 1: Lock a non-HTTP tool stub (github:jdx/tiny)
cat >bin/tiny <<'EOF'
#!/usr/bin/env -S mise tool-stub

tool = "github:jdx/tiny"
version = "1"
bin = "bin/tiny"
EOF
chmod +x bin/tiny

assert_succeed "mise generate tool-stub bin/tiny --lock"

# Verify version was pinned to an exact version (not just "1")
assert_not_contains "cat bin/tiny" 'version = "1"'
assert_contains "cat bin/tiny" 'version = "1.'

# Verify [lock.platforms.*] sections were added
assert_contains "cat bin/tiny" "[lock.platforms."
assert_contains "cat bin/tiny" "url ="
assert_contains "cat bin/tiny" "checksum ="

# Verify the shebang is preserved
assert_contains "cat bin/tiny" "#!/usr/bin/env -S mise tool-stub"

# Verify the tool field is preserved
assert_contains "cat bin/tiny" 'tool = "github:jdx/tiny"'

# Test 2: Bump version with --lock --version
assert_succeed "mise generate tool-stub bin/tiny --lock --version 2"

# Verify version was bumped
assert_contains "cat bin/tiny" 'version = "2.'
assert_not_contains "cat bin/tiny" 'version = "1.'

# Verify lock sections still present after bump
assert_contains "cat bin/tiny" "[lock.platforms."
assert_contains "cat bin/tiny" "url ="

# Test 3: Re-lock preserves version (idempotent)
locked_version=$(grep 'version = ' bin/tiny | head -1 | sed 's/.*"\(.*\)".*/\1/')
assert_succeed "mise generate tool-stub bin/tiny --lock"

# Version should be the same after re-locking
assert_contains "cat bin/tiny" "version = \"$locked_version\""

# Test 4: Lock fails on non-existent file
assert_fail "mise generate tool-stub bin/nonexistent --lock"

# Test 5: Execute the locked stub to verify it works
assert_contains "mise tool-stub bin/tiny" "tiny v2."

echo "All tool-stub lock tests passed!"
26 changes: 26 additions & 0 deletions e2e/plugins/test_env_plugin_shadow_warning
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
#!/usr/bin/env bash

# Test: env-only vfox plugin shadowing a registry entry emits a warning at install time

# Create a git repo that looks like a vfox env plugin
repo_dir="$TMPDIR/vfox-jq-env"
mkdir -p "$repo_dir/hooks"
cat >"$repo_dir/metadata.lua" <<'SCRIPT'
PLUGIN = {}
PLUGIN.name = "jq"
PLUGIN.version = "0.0.1"
PLUGIN.description = "test env plugin"
SCRIPT
cat >"$repo_dir/hooks/mise_env.lua" <<'SCRIPT'
function PLUGIN:MiseEnv(ctx)
return {
{ key = "JQ_TEST", value = "1" }
}
end
SCRIPT
git -C "$repo_dir" init -q
git -C "$repo_dir" add .
git -C "$repo_dir" commit -q -m "init"

# Install the env plugin with name "jq" (which exists in the registry)
assert_contains "mise plugin install jq file://$repo_dir 2>&1" "shadowing"
4 changes: 4 additions & 0 deletions e2e/run_test
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,10 @@ within_isolated_env() {
CARGO_LLVM_COV="${CARGO_LLVM_COV:-}" \
CARGO_LLVM_COV_SHOW_ENV="${CARGO_LLVM_COV_SHOW_ENV:-}" \
CARGO_LLVM_COV_TARGET_DIR="${CARGO_LLVM_COV_TARGET_DIR:-}" \
GIT_AUTHOR_NAME="test" \
GIT_AUTHOR_EMAIL="test@test.com" \
GIT_COMMITTER_NAME="test" \
GIT_COMMITTER_EMAIL="test@test.com" \
GITHUB_ACTION="${GITHUB_ACTION:-}" \
GITHUB_TOKEN="${GITHUB_TOKEN:-}" \
GOPROXY="${GOPROXY:-}" \
Expand Down
3 changes: 3 additions & 0 deletions man/man1/mise.1
Original file line number Diff line number Diff line change
Expand Up @@ -1155,6 +1155,9 @@ HTTP backend type to use
\fIDefault: \fRhttp
.RE
.TP
\fB\-\-lock\fR
Resolve and embed lockfile data (exact version + platform URLs/checksums) into an existing stub file for reproducible installs without runtime API calls
.TP
\fB\-\-platform\-bin\fR \fI<PLATFORM_BIN>\fR
Platform\-specific binary paths in the format platform:path

Expand Down
3 changes: 2 additions & 1 deletion mise.usage.kdl
Original file line number Diff line number Diff line change
Expand Up @@ -377,7 +377,7 @@ cmd generate subcommand_required=#true help="Generate files for various tools/se
}
cmd tool-stub help="Generate a tool stub for HTTP-based tools" {
long_help "Generate a tool stub for HTTP-based tools\n\nThis command generates tool stubs that can automatically download and execute\ntools from HTTP URLs. It can detect checksums, file sizes, and binary paths\nautomatically by downloading and analyzing the tool.\n\nWhen generating stubs with platform-specific URLs, the command will append new\nplatforms to existing stub files rather than overwriting them. This allows you\nto incrementally build cross-platform tool stubs."
after_long_help "Examples:\n\n Generate a tool stub for a single URL:\n $ mise generate tool-stub ./bin/gh --url \"https://github.com/cli/cli/releases/download/v2.336.0/gh_2.336.0_linux_amd64.tar.gz\"\n\n Generate a tool stub with platform-specific URLs:\n $ mise generate tool-stub ./bin/rg \\\n --platform-url linux-x64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-x86_64-unknown-linux-musl.tar.gz \\\n --platform-url darwin-arm64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-aarch64-apple-darwin.tar.gz\n\n Append additional platforms to an existing stub:\n $ mise generate tool-stub ./bin/rg \\\n --platform-url linux-x64:https://example.com/rg-linux.tar.gz\n $ mise generate tool-stub ./bin/rg \\\n --platform-url darwin-arm64:https://example.com/rg-darwin.tar.gz\n # The stub now contains both platforms\n\n Use auto-detection for platform from URL:\n $ mise generate tool-stub ./bin/node \\\n --platform-url https://nodejs.org/dist/v22.17.1/node-v22.17.1-darwin-arm64.tar.gz\n # Platform 'macos-arm64' will be auto-detected from the URL\n\n Generate with platform-specific binary paths:\n $ mise generate tool-stub ./bin/tool \\\n --platform-url linux-x64:https://example.com/tool-linux.tar.gz \\\n --platform-url windows-x64:https://example.com/tool-windows.zip \\\n --platform-bin windows-x64:tool.exe\n\n Generate without downloading (faster):\n $ mise generate tool-stub ./bin/tool --url \"https://example.com/tool.tar.gz\" --skip-download\n\n Fetch checksums for an existing stub:\n $ mise generate tool-stub ./bin/jq --fetch\n # This will read the existing stub and download files to fill in any missing checksums/sizes\n\n Generate a bootstrap stub that installs mise if needed:\n $ mise generate tool-stub ./bin/tool --url \"https://example.com/tool.tar.gz\" --bootstrap\n # The stub will check for mise and install it automatically before running the tool\n\n Generate a bootstrap stub with a pinned mise version:\n $ mise generate tool-stub ./bin/tool --url \"https://example.com/tool.tar.gz\" --bootstrap --bootstrap-version 2025.1.0\n"
after_long_help "Examples:\n\n Generate a tool stub for a single URL:\n $ mise generate tool-stub ./bin/gh --url \"https://github.com/cli/cli/releases/download/v2.336.0/gh_2.336.0_linux_amd64.tar.gz\"\n\n Generate a tool stub with platform-specific URLs:\n $ mise generate tool-stub ./bin/rg \\\n --platform-url linux-x64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-x86_64-unknown-linux-musl.tar.gz \\\n --platform-url darwin-arm64:https://github.com/BurntSushi/ripgrep/releases/download/14.0.3/ripgrep-14.0.3-aarch64-apple-darwin.tar.gz\n\n Append additional platforms to an existing stub:\n $ mise generate tool-stub ./bin/rg \\\n --platform-url linux-x64:https://example.com/rg-linux.tar.gz\n $ mise generate tool-stub ./bin/rg \\\n --platform-url darwin-arm64:https://example.com/rg-darwin.tar.gz\n # The stub now contains both platforms\n\n Use auto-detection for platform from URL:\n $ mise generate tool-stub ./bin/node \\\n --platform-url https://nodejs.org/dist/v22.17.1/node-v22.17.1-darwin-arm64.tar.gz\n # Platform 'macos-arm64' will be auto-detected from the URL\n\n Generate with platform-specific binary paths:\n $ mise generate tool-stub ./bin/tool \\\n --platform-url linux-x64:https://example.com/tool-linux.tar.gz \\\n --platform-url windows-x64:https://example.com/tool-windows.zip \\\n --platform-bin windows-x64:tool.exe\n\n Generate without downloading (faster):\n $ mise generate tool-stub ./bin/tool --url \"https://example.com/tool.tar.gz\" --skip-download\n\n Fetch checksums for an existing stub:\n $ mise generate tool-stub ./bin/jq --fetch\n # This will read the existing stub and download files to fill in any missing checksums/sizes\n\n Generate a bootstrap stub that installs mise if needed:\n $ mise generate tool-stub ./bin/tool --url \"https://example.com/tool.tar.gz\" --bootstrap\n # The stub will check for mise and install it automatically before running the tool\n\n Generate a bootstrap stub with a pinned mise version:\n $ mise generate tool-stub ./bin/tool --url \"https://example.com/tool.tar.gz\" --bootstrap --bootstrap-version 2025.1.0\n\n Lock an existing tool stub with pinned version and platform URLs/checksums:\n $ mise generate tool-stub ./bin/node --lock\n\n Bump the version in a locked stub:\n $ mise generate tool-stub ./bin/node --lock --version 22\n # Resolves the latest node 22.x, pins it, and updates platform URLs/checksums\n"
flag "-b --bin" help="Binary path within the extracted archive" {
long_help "Binary path within the extracted archive\n\nIf not specified and the archive is downloaded, will auto-detect the most likely binary"
arg <BIN>
Expand All @@ -395,6 +395,7 @@ cmd generate subcommand_required=#true help="Generate files for various tools/se
flag --http help="HTTP backend type to use" default=http {
arg <HTTP>
}
flag --lock help="Resolve and embed lockfile data (exact version + platform URLs/checksums) into an existing stub file for reproducible installs without runtime API calls"
flag --platform-bin help="Platform-specific binary paths in the format platform:path" var=#true {
long_help "Platform-specific binary paths in the format platform:path\n\nExamples: --platform-bin windows-x64:tool.exe --platform-bin linux-x64:bin/tool"
arg <PLATFORM_BIN>
Expand Down
Loading
Loading