Skip to content

feat(github): use release latest endpoint to get latest release#8516

Merged
jdx merged 3 commits intojdx:mainfrom
roele:issues/8470
Mar 12, 2026
Merged

feat(github): use release latest endpoint to get latest release#8516
jdx merged 3 commits intojdx:mainfrom
roele:issues/8470

Conversation

@roele
Copy link
Copy Markdown
Contributor

@roele roele commented Mar 8, 2026

Use release/latest endpoint to fetch latest release. This is especially useful for repositories with lots of pre-releases where mise would need to fetch all pages (via undocumented MISE_LIST_ALL_VERSIONS) to get the latest release.

Note: GitLab does not have such an endpoint and we use current behaviour.

@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 enhances the mise tool's ability to efficiently retrieve the latest stable release for GitHub and Forgejo repositories. By integrating direct API calls to their /releases/latest endpoints, the system now bypasses the need to fetch and process potentially large lists of releases, leading to faster and more resource-friendly operations. This change primarily benefits users interacting with repositories that frequently publish pre-releases, ensuring that the latest stable version is identified quickly and accurately.

Highlights

  • Optimized Latest Release Fetching: Implemented a new latest_stable_version method within the UnifiedGitBackend to leverage dedicated /releases/latest API endpoints for GitHub and Forgejo. This significantly improves performance by avoiding the need to paginate through all releases, especially for repositories with numerous pre-releases.
  • Dedicated API Endpoints: Added specific get_release_latest functions for both GitHub and Forgejo APIs, which directly query their respective /releases/latest endpoints. These functions include caching mechanisms to further reduce redundant API calls.
  • GitLab Handling: Explicitly noted that GitLab does not offer a similar /releases/latest endpoint, so the existing method for fetching the latest version will continue to be used for GitLab repositories.
  • End-to-End Testing: Introduced a new assert_not_empty helper function in e2e/assert.sh and added a corresponding end-to-end test case in e2e/backend/test_github to verify the correct functionality of fetching the latest GitHub release using the new optimized approach.
Changelog
  • e2e/assert.sh
    • Added assert_not_empty function to check if a command's output is not empty.
  • e2e/backend/test_github
    • Added a test case to verify that mise latest github:unikraft/kraftkit returns a non-empty result, confirming the new latest release fetching logic.
  • src/backend/github.rs
    • Implemented latest_stable_version for UnifiedGitBackend to use /releases/latest endpoints for GitHub and Forgejo.
    • Included fallback logic to the existing latest_version method if the dedicated endpoint fails or for GitLab.
  • src/forgejo.rs
    • Added get_release_latest and get_release_latest_ functions to fetch the latest Forgejo release using the /releases/latest API endpoint.
    • Integrated caching for Forgejo latest release lookups.
  • src/github.rs
    • Added get_release_latest and get_release_latest_ functions to fetch the latest GitHub release using the /releases/latest API endpoint.
    • Integrated caching for GitHub latest release lookups.
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.

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 optimizes fetching the latest release from GitHub and Forgejo by using the /releases/latest endpoint. While this is a good improvement, it introduces an insecure caching pattern in the GitHub backend and utilizes an existing vulnerable credential-handling function in the Forgejo backend. Specifically, the GitHub cache key lacks the API URL, leading to collisions between different GitHub instances, and the Forgejo backend leaks enterprise tokens to unauthorized hosts due to insufficient host verification in get_headers. The PR also includes a new implementation for latest_stable_version, corresponding API client functions, and an e2e test, with well-handled fallback mechanisms.

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 optimizes fetching the latest release from GitHub and Forgejo using the /releases/latest endpoint. However, it introduces several security concerns, including a risk of sensitive credential leakage (SSRF) due to user-controlled api_url and automatic token attachment, potential cache collisions in GitHub Enterprise due to missing api_url in the cache key, and path traversal vulnerabilities from unsanitized repository names in URL construction. Additionally, there's a caching bug affecting GitHub Enterprise users and an opportunity to refactor duplicated code for better maintainability.

@roele
Copy link
Copy Markdown
Contributor Author

roele commented Mar 8, 2026

@jdx Looks like there is an upstream issue with e2e/backend/test_aqua_github_attestations and the latest version of aqua:goreleaser/goreleaser. How come latest version is 2.14.2 if this is not merged yet (aquaproj/aqua-registry#49986)?

  ✖ mise use aqua:goreleaser/goreleaser@2.14.1
mise Cannot parse Rekor public key with id cf1199155bddd051268d1f16ac5c0c75c009f6fb5a63f4177f8e18d7051e3fa0: Pkcs8 spki error : Ecdsa-P256 from der bytes to public key failed: unknown/unsupported algorithm OID: 1.2.840.10045.2.1
aqua:goreleaser/goreleaser@2.14.1 extract goreleaser_Darwin_all.tar.gz                                                                     ✔
mise ~/Downloads/mise/mise.toml tools: aqua:goreleaser/goreleaser@2.14.1

  ❯ mise use aqua:goreleaser/goreleaser@2.14.2
aqua:goreleaser/goreleaser@2.14.2 verify GitHub artifact attestations                                                                      ◡
mise ERROR Failed to install aqua:goreleaser/goreleaser@2.14.2: No GitHub artifact attestations found for aqua:goreleaser/goreleaser@2.14.2, but they are expected per aqua registry configuration
mise ERROR Run with --verbose or MISE_VERBOSE=1 for more information

@jdx
Copy link
Copy Markdown
Owner

jdx commented Mar 8, 2026

we don't use the versions in aqua-registry at all

@risu729
Copy link
Copy Markdown
Contributor

risu729 commented Mar 8, 2026

aqua even doesn't use the PR to update the latest versions. Those PRs just notifies shunsuke the incompatibility of aqua registry for the latest version.
pkg.yml files are purely for testing.

@roele roele marked this pull request as ready for review March 10, 2026 18:38
Copilot AI review requested due to automatic review settings March 10, 2026 18:38
@greptile-apps
Copy link
Copy Markdown
Contributor

greptile-apps bot commented Mar 10, 2026

Greptile Summary

This PR optimizes mise latest for GitHub and Forgejo backends by using the /releases/latest API endpoint instead of fetching all paginated releases — a significant improvement for repositories with many pre-releases. A new latest_stable_version override is added to UnifiedGitBackend that hits the fast endpoint and falls back to the paginated path only when needed (e.g. when the globally-latest release doesn't match a configured version_prefix, or when the endpoint returns an error). GitLab, which lacks an equivalent endpoint, is short-circuited back to the existing paginated behavior.

Key changes:

  • src/backend/github.rs: New latest_stable_version implementation routes GitHub and Forgejo repos to the /releases/latest endpoint, filters by version_prefix, and falls back to the paginated path when necessary.
  • src/forgejo.rs: Fixes get_release_ to correctly call GET /repos/{repo}/releases/latest when tag == "latest", rather than the incorrect /releases/tags/latest that would always 404.
  • e2e/assert.sh: Adds the assert_not_empty test helper, consistently modeled on the existing assert_empty.
  • e2e/backend/test_github: Adds a smoke test for the new fast path. The test uses an external third-party repository (unikraft/kraftkit) which could become a flaky CI dependency if that project changes its release cadence or repo structure.

Confidence Score: 4/5

  • Safe to merge with the test reliability concern noted; the core logic is correct and well-guarded by version_prefix filtering and fallback.
  • The primary logic change (using /releases/latest endpoint) is correct for both GitHub and Forgejo, with proper filtering and a safe fallback. The Forgejo endpoint fix is accurate. The only concern is the e2e test anchoring to an external repo, which is a reliability risk for CI rather than a correctness issue.
  • The e2e/backend/test_github test should be updated to use a controlled repository to prevent future CI flakiness.

Important Files Changed

Filename Overview
src/backend/github.rs Adds latest_stable_version override that uses the GitHub/Forgejo /releases/latest endpoint, with correct version_prefix filtering and fallback to the paginated path. The early return for GitLab correctly delegates to the paginated path. Logic is sound, though the GitLab short-circuit adds a small extra overhead on every call before delegating.
src/forgejo.rs Fixes get_release_ to route tag == "latest" to the correct /repos/{repo}/releases/latest endpoint (rather than the erroneous /releases/tags/latest). The change is minimal and correct; Forgejo/Gitea's /releases/latest endpoint returns the latest non-draft, non-prerelease release.
e2e/backend/test_github Adds a smoke test for the new /releases/latest path using an external third-party repo (unikraft/kraftkit). The assertion verifies only that output is non-empty, making it fragile against both repo changes and any regression in the fallback behavior.
e2e/assert.sh Adds assert_not_empty helper, consistent with the existing assert_empty pattern. Implementation is correct.

Flowchart

%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A["latest_stable_version(config)"] --> B{offline mode?}
    B -- yes --> C["return Ok(None)"]
    B -- no --> D{backend type?}
    D -- GitLab --> E["return latest_version(config, 'latest')"]
    D -- Forgejo --> F["forgejo::get_release_for_url(api_url, repo, 'latest')"]
    D -- GitHub --> G["github::get_release_for_url(api_url, repo, 'latest')"]
    F --> H{HTTP GET /releases/latest}
    G --> H
    H -- OK --> I["tag_name from response"]
    H -- Err --> J["debug log, None"]
    I --> K{version_prefix match?}
    J --> K
    K -- matches / no prefix --> L["strip_version_prefix → return Ok(Some(version))"]
    K -- no match --> M["fallback: latest_version(config, 'latest')"]
    M --> N["paginated list_remote_versions"]
Loading

Last reviewed commit: bff5c4a

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR updates the GitHub/Forgejo backend’s “latest” resolution to use the /releases/latest API endpoint so mise latest can find the newest stable release without paging through release lists dominated by pre-releases (while keeping GitLab behavior unchanged).

Changes:

  • Add get_release_latest helpers in src/github.rs and src/forgejo.rs to call /repos/{repo}/releases/latest with caching.
  • Override UnifiedGitBackend::latest_stable_version to prefer the latest-release endpoint for GitHub/Forgejo and fall back to the existing behavior for GitLab (and on failure).
  • Add an e2e assertion and an assert_not_empty helper for validating mise latest returns output.

Reviewed changes

Copilot reviewed 5 out of 5 changed files in this pull request and generated 5 comments.

Show a summary per file
File Description
src/github.rs Adds a cached helper for fetching the latest release via /releases/latest.
src/forgejo.rs Adds a cached helper for fetching the latest release via /releases/latest.
src/backend/github.rs Implements a GitHub/Forgejo-specific latest_stable_version using the latest-release endpoint with fallback.
e2e/backend/test_github Adds an e2e check ensuring mise latest returns a non-empty result.
e2e/assert.sh Adds assert_not_empty helper for e2e tests.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@jdx
Copy link
Copy Markdown
Owner

jdx commented Mar 10, 2026

A few things I noticed:

  1. version_prefix not respectedlatest_stable_version doesn't filter by version_prefix from opts before accepting the /releases/latest result. If a user has version_prefix = "v1" and the repo's latest release is v2.0.0, this will return the wrong version. list_version_info already does this filtering — the new method should too, falling back to the existing path when the prefix doesn't match.

  2. Redundant with existing codeget_release_ in src/github.rs already special-cases tag == "latest" to hit /releases/latest with the same cache key. The new get_release_latest functions duplicate that. Could just call get_release_for_url(api_url, repo, "latest") instead.

  3. Errors silently swallowed.ok() discards all API errors including rate limits, then falls back to the more expensive paginated path which could make rate limiting worse. Would be better to at least log a warning, or only fall back on 404s.

  4. No offline mode check — Other remote-version code paths short-circuit on Settings::get().offline(), but this doesn't.

This comment was generated by Claude Code.

@jdx
Copy link
Copy Markdown
Owner

jdx commented Mar 10, 2026

The Forgejo path in get_release_latest won't actually hit the right endpoint. It calls get_release_for_url(api_url, repo, "latest") which goes through get_release_ in src/forgejo.rs:

async fn get_release_(api_url: &str, repo: &str, tag: &str) -> Result<ForgejoRelease> {
    let url = format!("{api_url}/repos/{repo}/releases/tags/{tag}");

This constructs /repos/{repo}/releases/tags/latest (looking for a tag literally named "latest") instead of /repos/{repo}/releases/latest. The GitHub side works because get_release_ in src/github.rs has a tag == "latest" special case, but the Forgejo side doesn't.

I confirmed that Forgejo does support /repos/{owner}/{repo}/releases/latest (tested against Codeberg's API). The fix would be to add the same special case to forgejo::get_release_:

async fn get_release_(api_url: &str, repo: &str, tag: &str) -> Result<ForgejoRelease> {
    let url = if tag == "latest" {
        format!("{api_url}/repos/{repo}/releases/latest")
    } else {
        format!("{api_url}/repos/{repo}/releases/tags/{tag}")
    };

Without this, the Forgejo optimization silently 404s and falls back to the paginated path every time.

This comment was generated by Claude Code.

@roele roele force-pushed the issues/8470 branch 2 times, most recently from 2258d7f to 85586c9 Compare March 10, 2026 20:34
#!/usr/bin/env bash

# Test use of release/latest endpoint for GitHub backend
assert_not_empty "mise latest github:unikraft/kraftkit"
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.

Test depends on a third-party external repository

unikraft/kraftkit is an external project owned by the Unikraft community. If this repository is deleted, renamed, archived, transfers ownership, or stops publishing stable (non-prerelease) releases, this test will begin to fail in CI with no action taken by the mise project.

Consider replacing it with a repository under jdx/ (or another org controlled by mise contributors) that is guaranteed to always have stable releases. For example, jdx/mise itself always has stable releases and would be a more stable anchor for this test:

Suggested change
assert_not_empty "mise latest github:unikraft/kraftkit"
assert_not_empty "mise latest github:jdx/mise"

This also aligns the test with the existing test fixtures already used elsewhere in test_github (e.g. github:jdx/mise-test-fixtures).

@jdx jdx merged commit 5d2ee35 into jdx:main Mar 12, 2026
34 checks passed
@roele roele deleted the issues/8470 branch March 13, 2026 06:32
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
…8516)

Use `release/latest` endpoint to fetch latest release. This is
especially useful for repositories with lots of pre-releases where mise
would need to fetch all pages (via undocumented
`MISE_LIST_ALL_VERSIONS`) to get the latest release.

Note: GitLab does not have such an endpoint and we use current
behaviour.

---------

Co-authored-by: mise-en-dev <release@mise.jdx.dev>
Co-authored-by: autofix-ci[bot] <114827586+autofix-ci[bot]@users.noreply.github.com>
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.

5 participants