Skip to content

feat(mise): support selector lockfile updates - #44615

Open
zeitlinger wants to merge 23 commits into
renovatebot:mainfrom
zeitlinger:feat/44418-mise-lockfile-selectors
Open

feat(mise): support selector lockfile updates#44615
zeitlinger wants to merge 23 commits into
renovatebot:mainfrom
zeitlinger:feat/44418-mise-lockfile-selectors

Conversation

@zeitlinger

Copy link
Copy Markdown
Contributor

Changes

  • Treat mise selectors such as latest, lts, partial versions, and vendor-prefixed partial versions as constraints resolved from mise.lock.
  • Generate lockfile-only updates without changing the original mise.toml selector.
  • Implement AST-preserving mise.lock version updates, including vendor prefixes and quote styles.
  • Keep concrete versions on Renovate's normal update path; users can set mise.updatePinnedDependencies: false to preserve exact pins.
  • Add unit coverage and mise manager documentation.

Context

  • This closes an existing Issue, Closes: #
  • This doesn't close an Issue, but I accept the risk that this PR may be closed if maintainers disagree with its opening or implementation

Discussion: #44418

AI assistance disclosure

Did you use AI tools to create any part of this pull request?

  • No — I did not use AI for this contribution.
  • Yes — minimal assistance (e.g., IDE autocomplete, small code completions, grammar fixes).
  • Yes — substantive assistance (AI-generated non-trivial portions of code, tests, or documentation).
  • Yes — other (please describe):

Documentation

  • I have updated the documentation, or
  • No documentation update is required

How I've tested my work

I have verified these changes via:

  • Newly added/modified unit tests, or
  • Code inspection only, or
  • No unit tests, but ran on a real repository, or
  • Both unit tests + ran on a real repository

Focused mise and lookup suites pass (336 tests), along with TypeScript, schema validation, formatting, linting, and documentation fence checks.

@zeitlinger zeitlinger changed the title mise: support selector lockfile updates feat(mise): support selector lockfile updates Jul 15, 2026
@zeitlinger
zeitlinger marked this pull request as ready for review July 15, 2026 16:58
@github-actions
github-actions Bot requested a review from viceice July 15, 2026 16:58
Comment thread lib/modules/manager/mise/extract.ts Outdated
Comment thread lib/workers/repository/process/lookup/index.ts Outdated
Comment thread lib/modules/manager/mise/extract.ts Outdated
Comment thread lib/modules/manager/mise/update-locked.ts Outdated
Comment thread lib/modules/manager/types.ts

@zeitlinger zeitlinger left a comment

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.

Addressed all requested changes:

  • Added a maintenance comment next to the Java LTS list directing future updates to follow the OpenJDK release roadmap.
  • Removed the redundant === true comparison.
  • Refactored the selector-lockfile logic into extractSelectorLockedDependency, leaving extractToolEntry focused on normal extraction and delegation.
  • Removed the invalid TOML literal-string replaceAll escaping while preserving the existing quote style.
  • Added JSDoc for ignoreUnstable.

I also merged the current main branch and resolved the conflict. The focused checks and all GitHub CI checks pass.

Comment thread lib/modules/manager/mise/update-locked.ts Outdated
@jamietanna jamietanna added mend:customer-request Issues requested on behalf of a Mend customer mend:customer-interest A Mend customer has shown interest in this work and removed mend:customer-request Issues requested on behalf of a Mend customer labels Aug 5, 2026
Comment thread lib/workers/repository/process/lookup/index.ts Outdated
Comment thread lib/workers/repository/process/lookup/types.ts
Comment thread lib/modules/manager/types.ts Outdated

@jamietanna jamietanna left a comment

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.

Some findings per Claude Opus 5:

Code review finished. Two blocking bugs, one working-tree security issue, plus minor notes.

## HIGH

**1. `lib/modules/manager/mise/extract.ts:104` — lockfile lookup uses the raw TOML key instead of the sanitised `depName`** (regression vs `main`, verified empirically)

Old code looked up `dep.depName` (options-in-name stripped via `optionInToolNameRegex`, trimmed). New code passes the raw `Object.entries` key. So for `"ubi:cli/cli[exe=gh]" = "2"` with a `[[tools."ubi:cli/cli"]]` lock entry:

- `main``lockedVersion: '2.63.0'`
- branch → no `lockedVersion`, dep never enters the new selector path, so Renovate rewrites `mise.toml` from `2` to a concrete version and **destroys the user's selector**

The backend-prefix fallback doesn't rescue it (only strips up to the first `:`). Quoted keys with surrounding whitespace (`" node " = "20"`) regress the same way. Fix: derive `depName` (trim + strip `[...]`) before the lookup, or move the lookup inside `extractToolEntry` after `depName` exists.

**2. `lib/modules/manager/mise/update-locked.ts:137` — returning `updated` silently disables the `mise lock` artifact refresh**

The success branch in `get-updated.ts` puts only the lock file into `updatedFileContents` and leaves `mise.toml` out of both maps. `getManagersForPackageFiles` matches against `managerPackageFiles['mise'] = {'mise.toml'}`, so `mise.lock` doesn't match and `updateArtifacts` never runs. Previously the `unsupported` return put `mise.toml` into `nonUpdatedFileContents`, which *did* trigger it.

Result: `node = "lts"` at `22.14.0` → PR writes `version = "22.15.0"` while every `[tools.node.platforms.*]` entry keeps 22.14.0's checksum/URL, and `mise install` fails checksum verification. `MiseLockTool` models `platforms` explicitly, so this is the normal lock-file shape. Either strip the stale `platforms` table or keep emitting the unchanged package file so artifacts still run.


## LOW

4. `extract.ts:298` — Java `lts` `allowedVersions` regex is `(?:\.|-|$)` but the partial-selector `precisionPattern` is `(?:\.|-|\+|$)`; a release like `25+36` gets filtered. Looks accidental.
5. `extract.ts:305``partialSelectorRegex` matches mise's non-version specifiers (`ref:main2`, `path:/opt/tools/1.2`), producing bogus `allowedVersions`. The `lockedVersion === version` guard rescues it in practice, so latent rather than broken — an explicit reject-list for `ref:`/`path:`/`sub-N:` would make the guard's load-bearing role explicit.
6. `extract.ts:356``currentRawValue` has no consumer for mise (only `deno` and `npm` read it). Dead metadata.

## Checked and fine

`generate.ts:59` newValue short-circuit (`isLockfileUpdate` still set at line 113, survives the `res.updates` filter); `index.ts:270` is-pinned and `index.ts:565` isCompatible bypasses; dep-level `allowedVersions`/`ignoreUnstable` reaching `filterVersions` with user `packageRules` still winning via the later `pre-lookup` pass; `RegExp.escape` output through RE2; `formatLockedVersion` vendor-prefix reattachment; `astTableForTool`/`getVersionKeyValue` degrading to `unsupported` rather than a wrong edit.

@zeitlinger

Copy link
Copy Markdown
Contributor Author

Addressed the actionable findings from the review:

  • Normalize and trim mise tool names, including stripping inline options, before lockfile lookup. Added coverage for quoted/whitespace-padded ubi: names.
  • Preserve the unchanged package file in the non-updated set when a lockfile-only update changes only the lockfile, so the manager artifact refresh runs and platform checksum metadata is regenerated.
  • Allow + in Java LTS versions.
  • Reject mise ref:, path:, and sub-N: selectors from partial-version handling.
  • Removed unused mise currentRawValue metadata.

Focused checks/type-check/tests pass, and all current GitHub checks are green. The review preamble mentions a working-tree security issue but does not identify a separate file, behavior, or reproduction beyond the findings above.

Comment thread lib/modules/manager/mise/artifacts.spec.ts Outdated
Comment thread lib/modules/manager/mise/artifacts.spec.ts Outdated
Comment thread lib/modules/manager/mise/artifacts.spec.ts Outdated
Comment thread lib/modules/manager/mise/artifacts.spec.ts Outdated
Comment thread lib/modules/manager/mise/extract.ts Outdated
Comment thread lib/workers/repository/process/lookup/index.ts
Comment thread lib/workers/repository/process/lookup/index.ts
Comment thread lib/workers/repository/update/branch/get-updated.ts Outdated
@jamietanna

jamietanna commented Aug 20, 2026

Copy link
Copy Markdown
Contributor

@zeitlinger

Copy link
Copy Markdown
Contributor Author

Please remember:

This is in the PR body - but I admit that I sometimes forget about the commenting policy

Yes — substantive assistance (AI-generated non-trivial portions of code, tests, or documentation).

@jamietanna

Copy link
Copy Markdown
Contributor

Yeah we added a new section to the PR template explicitly to cover PR comments, because it's hard to know if I'm talking to a human or an agent at times, and if there are issues, I don't know if the human's gonna step in or what :)

Comment thread lib/workers/repository/process/lookup/generate.ts Outdated
Comment thread lib/modules/manager/mise/update-locked.ts Outdated
Comment thread lib/modules/manager/mise/update-locked.ts Outdated

@jamietanna jamietanna left a comment

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.

A few additional things that Claude Opus 5 has caught (from d2ceff9):

⏺ Code review(high · 5 findings)
  ⎿  lib/modules/manager/mise/update-locked.ts
       ● 126 [correctness] The hand-written lockfile version replacement
                           leaves the entry's `platforms` checksum/size/url
                           pointing at the previous version, and the
                           artifact refresh the comment relies on never
                           sees the updated content.
       ●  70 [correctness] `astTableForTool` parses with `tomlVersion:
                           '1.0'` while the schema path (lib/util/toml.ts)
                           parses with `'1.1'`, so TOML 1.1-only syntax
                           passes validation then throws in parseTOML.
     lib/modules/manager/mise/extract.ts
       ● 110 [correctness] The lockfile lookup key changed from the dep's
                           final `depName` to the raw tool name, so tools
                           whose tooling config overrides `depName`
                           silently lose their `lockedVersion`.
       ● 326 [correctness] The generated `allowedVersions` regex is
                           anchored to the raw datasource version but only
                           tolerates a prefix present in the mise selector,
                           so github-backed tools with `v`-prefixed tags
                           match nothing.
       ● 327 [correctness] Pinning `allowedVersions` to the selector's
                           major means partial selectors can never receive
                           a major update in mise.toml again.


Review done — 5 findings reported above (3 high, 1 medium, 1 low). `pnpm vitest run lib/modules/manager/mise` passes (267 tests).

Two things outside the findings list:

**Noted but not filed:**
- `lookup/index.ts:563` disables `isCompatible` entirely for `isLockfileOnly` deps. Inert today (no mise tooling uses a versioning scheme with a compatibility group), but it's a broad escape hatch in shared code, and the mise case already compares against a concrete locked version.
- `extract.ts:305`'s hardcoded Java LTS list needs manual maintenance per the OpenJDK roadmap.
- `update-locked.ts:132`'s `!== undefined` check doesn't screen out the `null` that `get-updated.ts` can pass via `packageFileContent!`.

Finding 1 (`update-locked.ts:126`) is the one I'd fix first — the stale-checksum path produces a lockfile that fails `mise install`, and it directly contradicts the premise in the comment added by `b2180eae3c`.

@zeitlinger

Copy link
Copy Markdown
Contributor Author

A few additional things that Claude Opus 5 has caught

Here's the answer:

Thanks for the thorough review. I addressed the actionable findings in f9b6d673f1:

  • The mise artifact pipeline now receives the updated mise.lock content and writes it before running mise lock, so platform checksums and metadata are regenerated for the new version. The handoff is gated to the mise manager, with regression coverage across update generation and artifact refresh.
  • Both schema and AST parsing now use TOML 1.1.
  • Lockfile lookup now happens after tooling configuration is applied and uses the final overridden depName, with asdf:nodejs → node coverage.
  • Generated selector constraints now account for prefixes from locked versions, including GitHub-style v tags.
  • Nullable packageFileContent is now handled safely.

Partial selectors intentionally remain lockfile-only and constrained to their declared major/minor range, so Renovate updates mise.lock without rewriting the selector in mise.toml.

I left the broader isCompatible observation unchanged because the current mise behavior requires crossing that compatibility boundary, and only mise currently sets isLockfileOnly. The Java LTS roadmap comment also remains in place; I did not attempt to create an issue, in accordance with the repository’s contribution rules.

Targeted checks passed, including 539 focused tests across 20 files.

@jamietanna

Copy link
Copy Markdown
Contributor

Thanks - a couple of issues Opus still believes need to be fixed:


lib/modules/manager/mise/extract.ts
  ● 313 [correctness] The `effectivePrefix = prefix || lockedPrefix` fallback only recovers a version prefix that the lockfile itself carries, so github-backed tools whose lockfile stores a bare version generate a regex matching no real tag.
  ● 322 [correctness] Pinning `allowedVersions` to the selector's major means a partial selector can never receive a major update in mise.toml again.
lib/modules/manager/mise/artifacts.ts
  ● 116 [correctness] When the `mise lock` refresh cannot run, the hand-edited lockfile is still committed carrying the previous version's `platforms` checksum/size/url.
  ● 212 [correctness] `updateArtifacts` writes `newLockFileContent` to disk but never writes `newPackageFileContent`, so the concrete-version path runs `mise lock` against a stale mise.toml.
lib/workers/repository/update/branch/get-updated.ts
  ●  72 [reuse]       `getMiseUpdatedLockFileContent` hardcodes `if (manager !== 'mise') return undefined` in generic branch-worker code.
lib/util/toml.ts
  ●  10 [reuse]       `parse()` still inlines `parseTOML(input, { tomlVersion: '1.1' })` rather than delegating to the `parseTOMLDocument()` helper defined beside it.

┌───────────────────────────────────────┬────────────────────────────────────────────────┐
│             Prior finding             │                 Changed since                  │
├───────────────────────────────────────┼────────────────────────────────────────────────┤
│ F1 lockfile key uses raw tool name    │ extract.ts                                     │
├───────────────────────────────────────┼────────────────────────────────────────────────┤
│ F2 allowedVersions ignores v prefixes │ extract.ts                                     │
├───────────────────────────────────────┼────────────────────────────────────────────────┤
│ F3 stale platform checksums           │ update-locked.ts, artifacts.ts, get-updated.ts │
├───────────────────────────────────────┼────────────────────────────────────────────────┤
│ F5 TOML 1.0/1.1 mismatch              │ toml.ts                                        │
└───────────────────────────────────────┴────────────────────────────────────────────────┘


Re-reviewed at `d4810afe01`. 6 findings filed above — all carried over; **nothing new in this delta.**

**Nothing has moved on the six open items.** `artifacts.ts`, `get-updated.ts`, and `toml.ts` are byte-identical across the delta, which I verified independently, so N3/N4/N5/N6 are untouched by construction rather than by re-argument. F2 and F4 live in `extract.ts`, which *did* change — but not in the `effectivePrefix` or precision-pattern logic, and F2 was re-probed empirically to confirm it still reproduces.

**F2 is the one thing I'd still fix before merge.** It's the only high-severity item left, and its failure mode is silent: affected deps just stop appearing in PRs. Given `createGithubToolConfig` sets no `extractVersion`, the fix is probably to set one for github-backed tools rather than to keep widening the prefix guess.

One nit the agent flagged but didn't file, which I agree isn't worth a finding: `expect.objectContaining({ newLockFileContent: undefined })` also passes when the key is absent entirely, so it's a weak assertion form — harmless here since the field is always passed explicitly.

Mostly interested in 313, 322, 116 and 212

@zeitlinger

Copy link
Copy Markdown
Contributor Author

Thanks - a couple of issues Opus still believes need to be fixed:

addressed

@jamietanna

Copy link
Copy Markdown
Contributor

@zeitlinger mind merging main into this PR? 🙏🏼 It'll make sure that it's looking OK based on recent changes in main

(I don't have access)

@zeitlinger

Copy link
Copy Markdown
Contributor Author

@zeitlinger mind merging main into this PR? 🙏🏼 It'll make sure that it's looking OK based on recent changes in main

(I don't have access)

done - and also gave access

@jamietanna jamietanna left a comment

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.

Claude Opus 5 is still seeing some findings that might not be fixed:

⏺ Code review(review · 9 findings)
lib/modules/manager/mise/extract.ts
  ● 320 [correctness]     getSelectorConfig only allows a `v?` prefix in `allowedVersions` when the datasource is github-releases, so numeric selectors on tools whose datasource emits prefixed raw versions (notably `node`) filter out every release.
  ● 298 [maintainability] The `lts` selector for java relies on a hardcoded allowedVersions list `/^(?:8|11|17|21|25)(?:\.|-|\+|$)/`, guarded only by a code comment.
lib/modules/manager/mise/update-locked.ts
  ●  38 [correctness]     formatLockedVersion returns `newVersion` verbatim when the current locked value has no prefix, but the lockfile-only path deliberately bypasses `versioningApi.getNewValue()` — which is where node versioning strips the `v` — so a prefixed release string lands in mise.lock as-is.
  ●  81 [correctness]     astTableForTool hard-codes `resolvedKey[2] === 0`, so with a multi-version tool the rewrite always targets the first lock entry regardless of which one corresponds to the extracted dep.
lib/workers/repository/update/branch/get-updated.ts
  ● 390 [correctness]     removeUpdatedLockFileChanges is manager-agnostic: on any artifact error it splices out every lockfile entry written by updateLockedDependency, which can leave nothing to commit (mise) or commit a package-file change without its lock change (npm).
lib/modules/manager/mise/backends.ts
  ● 139 [correctness]     The new unconditional `'^v?(?<version>.+)'` extractVersion for `github:` tools is unnecessary for concrete versions (semver.getNewValue already handles the bare case) and regresses pins that intentionally carry a `v`, contradicting the sibling ubi helper which preserves it.
lib/workers/repository/process/lookup/generate.ts
  ●  58 [correctness]     The `isLockfileOnly` newValue override also requires `rangeStrategy === 'update-lockfile'`, but packageRules run after the dep object is merged in fetch.ts, so a rule can change rangeStrategy while leaving isLockfileOnly true — dropping into the else branch.
lib/modules/manager/mise/artifacts.ts
  ● 217 [correctness]     When mise rewrites the lockfile, the refreshed content is added to `updatedArtifacts` while `updatedPackageFiles` still carries the pre-refresh content for the same path, so the commit file list holds two conflicting entries for one path.
do.sh
  ●   1 [hygiene]         Three unrelated untracked files sit in the worktree — `do.sh` (contains `export RENOVATE_TOKEN=...` plus an Azure DevOps endpoint/repo), `a.js` (an unrelated example config), and `docs/usage/key-concepts/update-types.md` (an unrelated docs page).

So we don't have a hardcoded set of data in the mise manager.

Co-authored-by: Claude Sonnet 5 <jamie.tanna+claude-code@mend.io>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

mend:customer-interest A Mend customer has shown interest in this work

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants