Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
23 commits
Select commit Hold shift + click to select a range
978a8a9
mise: support selector lockfile updates
zeitlinger Jul 15, 2026
f59efcd
chore: merge main into selector lockfile branch
zeitlinger Jul 27, 2026
1d6393d
chore: merge main and address review feedback
zeitlinger Jul 28, 2026
adc7c5f
fix(mise): handle empty lockfile AST
zeitlinger Aug 17, 2026
577cf53
test(mise): use valid empty TOML AST fixture
zeitlinger Aug 18, 2026
f537ccd
fix(mise): support current regex utility
zeitlinger Aug 18, 2026
ddf5a5e
chore: merge main into selector lockfile branch
zeitlinger Aug 18, 2026
3a7ccd9
fix: clarify lockfile-only lookup handling
zeitlinger Aug 18, 2026
80a207c
fix(mise): handle selector lockfile updates correctly
zeitlinger Aug 18, 2026
436a034
test(mise): cover selector lockfile review fixes
zeitlinger Aug 20, 2026
b2180ea
fix(mise): scope selector artifact refresh
zeitlinger Aug 21, 2026
86c4afa
chore: retrigger CI
zeitlinger Aug 21, 2026
d2ceff9
fix(mise): remove unreachable lockfile guard
zeitlinger Aug 21, 2026
033a4ad
refactor(lookup): clarify lockfile-only value handling
zeitlinger Aug 21, 2026
30e32d5
refactor(mise): use shared TOML utilities
zeitlinger Aug 21, 2026
f9b6d67
fix(mise): handle selector lockfile edge cases
zeitlinger Aug 21, 2026
fa3da38
fix(mise): satisfy selector CI coverage
zeitlinger Aug 21, 2026
d1b8567
fix(mise): require dependency names
zeitlinger Aug 21, 2026
d4810af
fix(mise): preserve configured dependency names
zeitlinger Aug 21, 2026
cb42fa8
fix(mise): address selector lockfile review feedback
zeitlinger Aug 24, 2026
47eb74a
Merge remote-tracking branch 'origin/main' into feat/44418-mise-lockf…
zeitlinger Aug 26, 2026
9916f40
test(mise): cover major selector lockfile updates
zeitlinger Aug 26, 2026
8e36bfb
chore(static-data): auto-update Java LTS versions
jamietanna Aug 26, 2026
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
1 change: 1 addition & 0 deletions lib/data/java-version-lts.json
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
[8, 11, 17, 21, 25]
8 changes: 8 additions & 0 deletions lib/modules/datasource/java-version/schema.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,3 +10,11 @@ export const AdoptiumJavaResponse = z.object({
});

export type AdoptiumJavaResponse = z.infer<typeof AdoptiumJavaResponse>;

export const AdoptiumAvailableReleases = z.object({
available_lts_releases: z.array(z.number()),
});

export type AdoptiumAvailableReleases = z.infer<
typeof AdoptiumAvailableReleases
>;
194 changes: 192 additions & 2 deletions lib/modules/manager/mise/artifacts.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,41 @@ describe('modules/manager/mise/artifacts', () => {
]);
});

it('refreshes metadata from the updated in-memory lockfile', async () => {
const updatedLockFile =
'[[tools.node]]\nversion = "22.0.0"\nplatforms = { linux = { checksum = "new" } }\n';
const refreshedLockFile =
'[[tools.node]]\nversion = "22.0.0"\nplatforms = { linux = { checksum = "refreshed" } }\n';
fs.readLocalFile.mockResolvedValueOnce(refreshedLockFile);
const execSnapshots = mockExecAll();

const res = await updateArtifacts({
packageFileName: 'mise.toml',
updatedDeps: [{ depName: 'node' }],
newPackageFileContent: '[tools]\nnode = "22"\n',
newLockFileContent: updatedLockFile,
config,
});

expect(fs.writeLocalFile).toHaveBeenCalledWith(
'mise.lock',
updatedLockFile,
);
expect(res).toEqual([
{
file: {
type: 'addition',
path: 'mise.lock',
contents: expect.stringContaining('checksum = "refreshed"'),
},
},
]);
expect(execSnapshots).toMatchObject([
{ cmd: trustCmd },
{ cmd: updateToolCmd },
]);
});

it('returns artifactError on exec failure with combined output', async () => {
fs.readLocalFile.mockResolvedValueOnce('existing content');
const error = new Error('exec error');
Expand Down Expand Up @@ -697,7 +732,7 @@ version = "3.10.17"
expect(res).toEqual({ status: 'already-updated' });
});

it('returns unsupported when version does not match', () => {
it('updates the lockfile when version changes', () => {
const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
Expand All @@ -707,7 +742,102 @@ version = "3.10.17"
newVersion: '22.0.0',
});

expect(res).toEqual({ status: 'unsupported' });
expect(res).toMatchObject({
status: 'updated',
files: {
'mise.lock': expect.stringContaining('version = "22.0.0"'),
},
});
});

it('keeps the package file in the update set for artifact refresh', () => {
const res = updateLockedDependency({
packageFile: 'mise.toml',
packageFileContent: '[[tools.node]]\nversion = "22"\n',
lockFile: 'mise.lock',
lockFileContent,
depName: 'node',
currentVersion: '20.10.0',
newVersion: '22.0.0',
});

expect(res).toMatchObject({
status: 'updated',
files: {
'mise.toml': '[[tools.node]]\nversion = "22"\n',
'mise.lock': expect.stringContaining('version = "22.0.0"'),
},
});
});

it('does not add a null package file to the update set', () => {
const res = updateLockedDependency({
packageFile: 'mise.toml',
packageFileContent: null as never,
lockFile: 'mise.lock',
lockFileContent,
depName: 'node',
currentVersion: '20.10.0',
newVersion: '22.0.0',
});

expect(res).toMatchObject({
status: 'updated',
files: {
'mise.lock': expect.stringContaining('version = "22.0.0"'),
},
});
expect(res.files).not.toHaveProperty('mise.toml');
});

it('preserves a vendor prefix in the lockfile version', () => {
const javaLockFileContent = `
[[tools.java]]
version = 'temurin-25.0.3+9.0.LTS'
backend = 'core:java'
`;

const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
lockFileContent: javaLockFileContent,
depName: 'java',
currentVersion: '25.0.3+9.0.LTS',
newVersion: '25.0.4+8.0.LTS',
});

expect(res).toMatchObject({
status: 'updated',
files: {
'mise.lock': expect.stringContaining(
"version = 'temurin-25.0.4+8.0.LTS'",
),
},
});
});

it('supports quoted version keys and ignores other nested keys', () => {
const quotedKeyLockFileContent = `
[[tools.node]]
foo.bar = "ignored"
"version" = "20.11.0"
`;

const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
lockFileContent: quotedKeyLockFileContent,
depName: 'node',
currentVersion: '20.10.0',
newVersion: '20.12.0',
});

expect(res).toMatchObject({
status: 'updated',
files: {
'mise.lock': expect.stringContaining('"version" = "20.12.0"'),
},
});
});

it('returns unsupported when tool not in lock file', () => {
Expand Down Expand Up @@ -749,6 +879,36 @@ version = "3.10.17"
expect(res).toEqual({ status: 'unsupported' });
});

it('returns unsupported when the lockfile schema is invalid', () => {
const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
lockFileContent: 'foo = "bar"',
depName: 'node',
currentVersion: '20.10.0',
newVersion: '20.12.0',
});

expect(res).toEqual({ status: 'unsupported' });
});

it('returns unsupported when the lock entry has no array-table AST node', () => {
const inlineLockFileContent = `
tools = { node = [{ version = "20.11.0" }] }
`;

const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
lockFileContent: inlineLockFileContent,
depName: 'node',
currentVersion: '20.10.0',
newVersion: '20.12.0',
});

expect(res).toEqual({ status: 'unsupported' });
});

it('returns unsupported when depName is undefined', () => {
const res = updateLockedDependency({
packageFile: 'mise.toml',
Expand All @@ -762,6 +922,36 @@ version = "3.10.17"
expect(res).toEqual({ status: 'unsupported' });
});

it('returns unsupported when the lock entry lookup cannot be resolved', () => {
vi.spyOn(lockfile, 'getLockedVersion').mockReturnValueOnce('20.11.0');

const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
lockFileContent,
depName: 'ruby',
currentVersion: '3.2.0',
newVersion: '3.3.0',
});

expect(res).toEqual({ status: 'unsupported' });
});

it('returns unsupported when a prefixed lock entry has no short-name match', () => {
vi.spyOn(lockfile, 'getLockedVersion').mockReturnValueOnce('20.11.0');

const res = updateLockedDependency({
packageFile: 'mise.toml',
lockFile: 'mise.lock',
lockFileContent,
depName: 'core:ruby',
currentVersion: '3.2.0',
newVersion: '3.3.0',
});

expect(res).toEqual({ status: 'unsupported' });
});

it('returns update-failed in case of errors', () => {
vi.spyOn(lockfile, 'getLockedVersion').mockImplementationOnce(() => {
throw new Error('unexpected error');
Expand Down
20 changes: 15 additions & 5 deletions lib/modules/manager/mise/artifacts.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@ import type {
ExtraEnv,
ToolConstraint,
} from '../../../util/exec/types.ts';
import { readLocalFile } from '../../../util/fs/index.ts';
import { readLocalFile, writeLocalFile } from '../../../util/fs/index.ts';
import * as hostRules from '../../../util/host-rules.ts';
import { regEx } from '../../../util/regex.ts';
import { api as miseVersioning } from '../../versioning/semver/index.ts';
Expand Down Expand Up @@ -108,10 +108,13 @@ function getMiseLockToolConstraints(
export async function updateArtifacts({
packageFileName,
updatedDeps,
newPackageFileContent,
newLockFileContent,
config,
}: UpdateArtifact): Promise<UpdateArtifactsResult[] | null> {
const lockFileName = getLockFileName(packageFileName);
const existingLockFileContent = await readLocalFile(lockFileName, 'utf8');
const existingLockFileContent =
newLockFileContent ?? (await readLocalFile(lockFileName, 'utf8'));
if (!existingLockFileContent) {
logger.debug({ lockFileName }, 'No mise lock file found');
return null;
Expand Down Expand Up @@ -206,9 +209,16 @@ export async function updateArtifacts({
: [`mise trust ${quote(upath.basename(packageFileName))}`, lockCmd];

try {
await writeLocalFile(packageFileName, newPackageFileContent);
if (newLockFileContent) {
await writeLocalFile(lockFileName, newLockFileContent);
}
await exec(commands, execOptions);
const newLockFileContent = await readLocalFile(lockFileName, 'utf8');
if (!newLockFileContent || existingLockFileContent === newLockFileContent) {
const refreshedLockFileContent = await readLocalFile(lockFileName, 'utf8');
if (
!refreshedLockFileContent ||
existingLockFileContent === refreshedLockFileContent
) {
return null;
}

Expand All @@ -218,7 +228,7 @@ export async function updateArtifacts({
file: {
type: 'addition',
path: lockFileName,
contents: newLockFileContent,
contents: refreshedLockFileContent,
},
},
];
Expand Down
6 changes: 5 additions & 1 deletion lib/modules/manager/mise/backends.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -114,14 +114,16 @@ describe('modules/manager/mise/backends', () => {
packageName: 'BurntSushi/ripgrep',
datasource: 'github-releases',
currentValue: '14.1.1',
extractVersion: '^v?(?<version>.+)',
});
});

it('should not set extractVersion if the version has leading v', () => {
it('should normalize a leading v in the version', () => {
expect(createGithubToolConfig('cli/cli', 'v2.64.0', {})).toStrictEqual({
packageName: 'cli/cli',
datasource: 'github-releases',
currentValue: 'v2.64.0',
extractVersion: '^v?(?<version>.+)',
});
});

Expand Down Expand Up @@ -158,6 +160,7 @@ describe('modules/manager/mise/backends', () => {
packageName: 'some/repo',
datasource: 'github-releases',
currentValue: '1.0.0',
extractVersion: '^v?(?<version>.+)',
});
});

Expand All @@ -168,6 +171,7 @@ describe('modules/manager/mise/backends', () => {
packageName: 'some/repo',
datasource: 'github-releases',
currentValue: 'v1.0.0',
extractVersion: '^v?(?<version>.+)',
});
});

Expand Down
4 changes: 4 additions & 0 deletions lib/modules/manager/mise/backends.ts
Original file line number Diff line number Diff line change
Expand Up @@ -133,6 +133,10 @@ export function createGithubToolConfig(

if (isNonEmptyString(prefix)) {
extractVersion = `^${RegExp.escape(prefix)}(?<version>.+)`;
} else {
// GitHub release tags commonly use a leading `v`, while mise lockfiles
// store the normalized version. Accept both forms from the datasource.
extractVersion = '^v?(?<version>.+)';
}

return {
Expand Down
Loading