Skip to content

fix(aqua): expose main binary when files field is empty and symlink_bins is enabled#8550

Merged
jdx merged 2 commits intojdx:mainfrom
AlexanderTheGrey:fix/aqua-symlink-bins-fallback
Mar 12, 2026
Merged

fix(aqua): expose main binary when files field is empty and symlink_bins is enabled#8550
jdx merged 2 commits intojdx:mainfrom
AlexanderTheGrey:fix/aqua-symlink-bins-fallback

Conversation

@AlexanderTheGrey
Copy link
Copy Markdown
Contributor

Summary

Fix Aqua symlink_bins = true for packages whose registry metadata does not define a files field.

Previously, Aqua could still install the main binary via its existing fallback logic, but symlink generation only used files. For packages without a files entry, that could leave .mise-bins empty, so the installed tool was not exposed as a usable mise-managed command when symlink_bins = true.

This change makes symlink generation use the same inferred main-binary fallback as install, so symlink_bins continues to filter extraneous executables without dropping the primary tool.

Changes

  • Add the inferred main-binary fallback to Aqua symlink source generation
  • Add an e2e regression test for a package without an aqua files entry
  • Clarify the symlink_bins documentation for the no-files fallback case

Result

symlink_bins = true now behaves consistently for Aqua packages both with and without a registry files entry, and still avoids exposing bundled dependencies and other extra executables.

@gemini-code-assist
Copy link
Copy Markdown
Contributor

Summary of Changes

Hello, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request addresses an inconsistency in how Aqua's symlink_bins feature behaves for packages whose registry metadata does not explicitly define a files field. Previously, such packages might not have their main binary exposed via symlinks, despite being correctly installed. The changes ensure that symlink_bins = true consistently exposes the primary tool for all Aqua packages, whether or not they have a files entry, while still preventing extraneous executables from being added to the PATH.

Highlights

  • Aqua Backend Symlink Generation: Implemented fallback logic in the Aqua backend to infer the main binary for symlink generation when a package's registry metadata does not define a files field. This ensures symlink_bins = true correctly exposes the primary tool.
  • End-to-End Testing: Added a new end-to-end regression test to validate the symlink_bins functionality for Aqua packages that lack a files entry in their registry metadata.
  • Documentation Clarification: Updated the symlink_bins documentation to clearly explain its behavior, including the new fallback mechanism for packages without a files field.
Changelog
  • docs/dev-tools/backends/aqua.md
    • Updated the description of symlink_bins to clarify that it exposes only binaries mise intends to expose, rather than every discovered executable.
    • Added a new point explaining that if the Aqua registry does not define a files field, mise falls back to exposing the inferred primary binary.
    • Revised the description of what symlink_bins prevents from being exposed to 'Bundled dependencies and other extra executables'.
  • e2e/backend/test_aqua_symlink_bins
    • Added a new end-to-end test case to verify that symlink_bins=true correctly exposes the main binary for a package (casey/just) that lacks a files entry in its Aqua registry metadata.
  • src/backend/aqua.rs
    • Introduced a conditional block within the srcs function to handle cases where pkg.files is empty.
    • Implemented logic to infer the main binary name from pkg.name or pkg.repo_name when files is empty.
    • Constructed the PathBuf for the inferred binary, including handling Windows .exe extension.
    • Returned a vector containing the inferred binary path as both the source and destination for symlinking, effectively providing a fallback for symlink_bins.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for GitHub and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR fixes a gap in symlink_bins = true behavior for Aqua packages whose registry metadata has no files field. Previously, install() had a fallback that inferred the primary binary name from pkg.name/pkg.repo_name when files was empty, but srcs() — which feeds create_symlink_bin_dir — did not, leaving .mise-bins empty. The fix adds the matching early-return fallback to srcs(), an e2e test with casey/just (a real package without a files entry), and clarified documentation.

Key changes:

  • srcs() in src/backend/aqua.rs now returns the inferred binary path as (path, path) when pkg.files.is_empty(), which is correctly handled by the caller: the renaming loop skips identical src/dst pairs, and create_symlink_bin_dir creates the .mise-bins symlink to dst as expected.
  • The e2e regression test is placed outside the Linux-only guard so it exercises all CI platforms.
  • Docs are updated to reflect that symlink_bins exposes the inferred primary binary when no files entry exists, rather than leaving the tool inaccessible.

Confidence Score: 4/5

  • Safe to merge; the fix is logically correct for the stated use case and backed by an e2e test.
  • The core fix is a clean and focused early-return that mirrors the existing install() fallback exactly. The (path, path) tuple is handled correctly throughout the call chain. The e2e test covers the primary regression scenario. One pre-existing edge case remains (non-empty files where every src() call errors) that was noted in previous review threads but is not addressed here.
  • No files require special attention; all changes are well-scoped.

Important Files Changed

Filename Overview
src/backend/aqua.rs Adds early-return fallback in srcs() for empty pkg.files, mirroring install()'s fallback. Logic is correct: (path, path) with src==dst is correctly skipped by the renaming loop and used by create_symlink_bin_dir. Minor gap: non-empty files where all src() calls fail is still not covered.
e2e/backend/test_aqua_symlink_bins Regression test for casey/just (no files entry) correctly placed outside the Linux guard so it runs on all platforms. Uses hardcoded version 1.46.0; the Windows path assertion may need .exe suffix consideration.
docs/dev-tools/backends/aqua.md Documentation updated to clarify the symlink_bins fallback behavior. Changes are accurate and improve comprehension of the feature.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["srcs(pkg, tv) called"] --> B{pkg.files.is_empty?}
    B -- Yes --> C["Compute fallback_name\nfrom pkg.name or pkg.repo_name"]
    C --> D["Build path = install_path / fallback_name"]
    D --> E{Windows &&\ncomplete_windows_ext?}
    E -- Yes --> F["path.with_extension('exe')"]
    E -- No --> G["Return [(path, path)]"]
    F --> G

    B -- No --> H["Map pkg.files to (src, dst) pairs\nusing file.src(pkg, version, os, arch)"]
    H --> I["flatten_ok + unique_by src"]
    I --> J["Return Vec of (src, dst) pairs"]

    G --> K["Caller: install()"]
    J --> K
    K --> L{src == dst?}
    L -- Yes --> M["Skip renaming step"]
    L -- No --> N["Create rename symlink src→dst"]
    M --> O{symlink_bins enabled?}
    N --> O
    O -- Yes --> P["create_symlink_bin_dir\ninstall_path/.mise-bins/bin_name → dst"]
Loading

Last reviewed commit: cb2b66c

Copy link
Copy Markdown
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request correctly fixes an issue where symlink_bins = true did not work for Aqua packages missing a files field. However, the implementation of the inferred binary name fallback introduces a potential path traversal risk by trusting the repo_name from the registry without sanitization. This needs remediation to ensure only the filename component is used when constructing installation paths. Additionally, a small piece of duplicated code should be refactored to improve long-term maintainability.

Comment on lines +1690 to +1694
let fallback_name = pkg
.name
.as_deref()
.and_then(|n| n.split('/').next_back())
.unwrap_or(&pkg.repo_name);
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

security-medium medium

The fallback logic for fallback_name uses pkg.repo_name directly if pkg.name is missing or invalid. This introduces a potential path traversal vulnerability because pkg.repo_name is not sanitized, allowing malicious input to resolve to locations outside the intended installation directory. It is recommended to apply split('/').next_back() sanitization to ensure only the last component of the name is used. Furthermore, this logic for determining the fallback binary name is duplicated in the install function (around line 1565). This duplication could lead to inconsistencies and should be refactored, perhaps by extracting it into a private helper function within impl AquaBackend.

Suggested change
let fallback_name = pkg
.name
.as_deref()
.and_then(|n| n.split('/').next_back())
.unwrap_or(&pkg.repo_name);
let fallback_name = pkg.name.as_deref().and_then(|n| n.split('/').next_back()).unwrap_or(&pkg.repo_name).split('/').next_back().unwrap();

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This does not need to be changed in this PR.

This PR fixes the symlink_bins regression for the empty-files case. The fallback logic was not introduced here; install() already uses the same fallback logic.

Any hardening or deduplication of that fallback should be handled separately, since it affects both install() and srcs().

@AlexanderTheGrey AlexanderTheGrey changed the title fix(aqua): expose inferred main binary when files field is missing and symlink_bins is enabled fix(aqua): expose inferred main binary when files field is empty and symlink_bins is enabled Mar 11, 2026
@AlexanderTheGrey AlexanderTheGrey force-pushed the fix/aqua-symlink-bins-fallback branch from ee55e91 to cb2b66c Compare March 11, 2026 16:04
@AlexanderTheGrey AlexanderTheGrey changed the title fix(aqua): expose inferred main binary when files field is empty and symlink_bins is enabled fix(aqua): expose main binary when files field is empty and symlink_bins is enabled Mar 11, 2026
@jdx jdx merged commit 9092085 into jdx:main Mar 12, 2026
36 checks passed
mise-en-dev added a commit that referenced this pull request Mar 13, 2026
### 🚀 Features

- **(github)** use release latest endpoint to get latest release by
@roele in [#8516](#8516)
- **(install)** add shared and system install directories by @jdx in
[#8581](#8581)
- **(vfox)** add provenance metadata to lockfile for tool plugins by
@malept in [#8544](#8544)

### 🐛 Bug Fixes

- **(aqua)** expose main binary when files field is empty and
symlink_bins is enabled by @AlexanderTheGrey in
[#8550](#8550)
- **(env)** redact secrets in `mise set` listing and task-specific env
by @jdx in [#8583](#8583)
- **(prepare)** install config tools before running prepare steps by
@jdx in [#8582](#8582)
- **(task)** allow ctrl-c to interrupt tool downloads during `mise run`
by @jdx in [#8571](#8571)
- **(tasks)** add file task header parser support for spaces around = by
@roele in [#8574](#8574)

### 📚 Documentation

- **(task)** add property description for interactive by @roele in
[#8562](#8562)
- add missing `</bold>` closing tag by @muzimuzhi in
[#8564](#8564)
- rebrand site with new chef logo and warm culinary palette by @jdx in
[#8587](#8587)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:alpine docker digest to de4657e by
@renovate[bot] in [#8577](#8577)
- update ghcr.io/jdx/mise:copr docker digest to eef29a2 by
@renovate[bot] in [#8578](#8578)
- update ghcr.io/jdx/mise:rpm docker digest to 5a96587 by @renovate[bot]
in [#8580](#8580)
- update ghcr.io/jdx/mise:deb docker digest to 464cf7c by @renovate[bot]
in [#8579](#8579)

### 📦 Registry

- fix flatc version test mismatch by @jdx in
[#8588](#8588)

### Chore

- **(registry)** skip spark test-tool by @jdx in
[#8572](#8572)

### New Contributors

- @AlexanderTheGrey made their first contribution in
[#8550](#8550)

## 📦 Aqua Registry Updates

#### New Packages (6)

- [`bahdotsh/mdterm`](https://github.com/bahdotsh/mdterm)
-
[`callumalpass/mdbase-lsp`](https://github.com/callumalpass/mdbase-lsp)
- [`facebook/ktfmt`](https://github.com/facebook/ktfmt)
- [`gurgeous/tennis`](https://github.com/gurgeous/tennis)
-
[`tektoncd/pipelines-as-code`](https://github.com/tektoncd/pipelines-as-code)
- [`weedonandscott/trolley`](https://github.com/weedonandscott/trolley)

#### Updated Packages (2)

- [`apple/container`](https://github.com/apple/container)
- [`cocogitto/cocogitto`](https://github.com/cocogitto/cocogitto)
fragon10 pushed a commit to fragon10/mise that referenced this pull request Mar 27, 2026
…ins is enabled (jdx#8550)

## Summary

Fix Aqua `symlink_bins = true` for packages whose registry metadata does
not define a `files` field.

Previously, Aqua could still install the main binary via its existing
fallback logic, but symlink generation only used `files`. For packages
without a `files` entry, that could leave `.mise-bins` empty, so the
installed tool was not exposed as a usable mise-managed command when
`symlink_bins = true`.

This change makes symlink generation use the same inferred main-binary
fallback as install, so `symlink_bins` continues to filter extraneous
executables without dropping the primary tool.

## Changes

- Add the inferred main-binary fallback to Aqua symlink source
generation
- Add an e2e regression test for a package without an aqua `files` entry
- Clarify the `symlink_bins` documentation for the no-`files` fallback
case

## Result

`symlink_bins = true` now behaves consistently for Aqua packages both
with and without a registry `files` entry, and still avoids exposing
bundled dependencies and other extra executables.
fragon10 pushed a commit to fragon10/mise that referenced this pull request Mar 27, 2026
### 🚀 Features

- **(github)** use release latest endpoint to get latest release by
@roele in [jdx#8516](jdx#8516)
- **(install)** add shared and system install directories by @jdx in
[jdx#8581](jdx#8581)
- **(vfox)** add provenance metadata to lockfile for tool plugins by
@malept in [jdx#8544](jdx#8544)

### 🐛 Bug Fixes

- **(aqua)** expose main binary when files field is empty and
symlink_bins is enabled by @AlexanderTheGrey in
[jdx#8550](jdx#8550)
- **(env)** redact secrets in `mise set` listing and task-specific env
by @jdx in [jdx#8583](jdx#8583)
- **(prepare)** install config tools before running prepare steps by
@jdx in [jdx#8582](jdx#8582)
- **(task)** allow ctrl-c to interrupt tool downloads during `mise run`
by @jdx in [jdx#8571](jdx#8571)
- **(tasks)** add file task header parser support for spaces around = by
@roele in [jdx#8574](jdx#8574)

### 📚 Documentation

- **(task)** add property description for interactive by @roele in
[jdx#8562](jdx#8562)
- add missing `</bold>` closing tag by @muzimuzhi in
[jdx#8564](jdx#8564)
- rebrand site with new chef logo and warm culinary palette by @jdx in
[jdx#8587](jdx#8587)

### 📦️ Dependency Updates

- update ghcr.io/jdx/mise:alpine docker digest to de4657e by
@renovate[bot] in [jdx#8577](jdx#8577)
- update ghcr.io/jdx/mise:copr docker digest to eef29a2 by
@renovate[bot] in [jdx#8578](jdx#8578)
- update ghcr.io/jdx/mise:rpm docker digest to 5a96587 by @renovate[bot]
in [jdx#8580](jdx#8580)
- update ghcr.io/jdx/mise:deb docker digest to 464cf7c by @renovate[bot]
in [jdx#8579](jdx#8579)

### 📦 Registry

- fix flatc version test mismatch by @jdx in
[jdx#8588](jdx#8588)

### Chore

- **(registry)** skip spark test-tool by @jdx in
[jdx#8572](jdx#8572)

### New Contributors

- @AlexanderTheGrey made their first contribution in
[jdx#8550](jdx#8550)

## 📦 Aqua Registry Updates

#### New Packages (6)

- [`bahdotsh/mdterm`](https://github.com/bahdotsh/mdterm)
-
[`callumalpass/mdbase-lsp`](https://github.com/callumalpass/mdbase-lsp)
- [`facebook/ktfmt`](https://github.com/facebook/ktfmt)
- [`gurgeous/tennis`](https://github.com/gurgeous/tennis)
-
[`tektoncd/pipelines-as-code`](https://github.com/tektoncd/pipelines-as-code)
- [`weedonandscott/trolley`](https://github.com/weedonandscott/trolley)

#### Updated Packages (2)

- [`apple/container`](https://github.com/apple/container)
- [`cocogitto/cocogitto`](https://github.com/cocogitto/cocogitto)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants