Files
renovate/lib/modules/manager/mise/backends.ts
Pieter Willekens 141e136869 feat(manager/mise): support gitlab backend (#44116)
* feat(manager/mise): support gitlab backend

Maps the mise `gitlab` backend (e.g. `gitlab:gitlab-org/cli`) to the gitlab-releases datasource, mirroring the github backend including version_prefix support.

* refactor(manager/mise): use optional instead of Nullish for tool options

TOML has no null type, so MiseToolOptions values are only ever a string or absent. .optional() models that exactly; Nullish only adds value for sources that can emit explicit null.

* fix(manager/mise): use RegExp.escape in gitlab backend

Main switched `createGithubToolConfig` from the `escapeRegExp` util to
`RegExp.escape` and dropped the import, which left the newly added
`createGitlabToolConfig` referencing an undefined symbol after the merge.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Michael Kriese <michael.kriese@mend.io>
Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-09-11 10:04:37 +00:00

294 lines
8.7 KiB
TypeScript

import {
isNonEmptyString,
isString,
isUndefined,
isUrlString,
} from '@sindresorhus/is';
import { regEx } from '../../../util/regex.ts';
import { CrateDatasource } from '../../datasource/crate/index.ts';
import { GitRefsDatasource } from '../../datasource/git-refs/index.ts';
import { GitTagsDatasource } from '../../datasource/git-tags/index.ts';
import { GithubReleasesDatasource } from '../../datasource/github-releases/index.ts';
import { GithubTagsDatasource } from '../../datasource/github-tags/index.ts';
import { GitlabReleasesDatasource } from '../../datasource/gitlab-releases/index.ts';
import { GoDatasource } from '../../datasource/go/index.ts';
import { NpmDatasource } from '../../datasource/npm/index.ts';
import { NugetDatasource } from '../../datasource/nuget/index.ts';
import { normalizePythonDepName } from '../../datasource/pypi/common.ts';
import { PypiDatasource } from '../../datasource/pypi/index.ts';
import { RubygemsDatasource } from '../../datasource/rubygems/index.ts';
import * as semverVersioning from '../../versioning/semver/index.ts';
import type { MiseToolOptions } from './schema.ts';
import type { BackendToolingConfig } from './types.ts';
/**
* Create a tooling config for aqua backend
* @link https://mise.jdx.dev/dev-tools/backends/aqua.html
*/
export function createAquaToolConfig(
name: string,
version: string,
): BackendToolingConfig {
// mise supports http aqua package type but we cannot determine it from the tool name
// An error will be thrown afterwards if the package type is http
// ref: https://github.com/jdx/mise/blob/d1b9749d8f3e13ef705c1ea471d96c5935b79136/src/aqua/aqua_registry.rs#L39-L45
return {
packageName: name,
datasource: GithubTagsDatasource.id,
// Trim the leading 'v' from both the current and extracted version
currentValue: version.replace(regEx(/^v/), ''),
extractVersion: '^v?(?<version>.+)',
};
}
const cargoGitVersionRegex = regEx(/^(?<type>tag|branch|rev):(?<version>.+)$/);
/**
* Create a tooling config for cargo backend
* @link https://mise.jdx.dev/dev-tools/backends/cargo.html
*/
export function createCargoToolConfig(
name: string,
version: string,
): BackendToolingConfig {
if (!isUrlString(name)) {
return {
packageName: name,
datasource: CrateDatasource.id,
// A mise tool version is a concrete version, not a Cargo dependency requirement,
// so the crate datasource default of cargo versioning does not apply
versioning: semverVersioning.id,
};
}
// tag: branch: or rev: is required for git repository url
// e.g. branch:main, tag:0.1.0, rev:abcdef
const matchGroups = cargoGitVersionRegex.exec(version)?.groups;
if (isUndefined(matchGroups)) {
return {
packageName: name,
skipReason: 'invalid-version',
};
}
const { type, version: gitVersion } = matchGroups;
switch (type as 'tag' | 'branch' | 'rev') {
case 'tag':
return {
packageName: name,
datasource: GitTagsDatasource.id,
currentValue: gitVersion,
};
case 'branch':
return {
packageName: name,
datasource: GitRefsDatasource.id,
currentValue: gitVersion,
};
case 'rev':
return {
packageName: name,
datasource: GitRefsDatasource.id,
currentValue: gitVersion,
};
}
}
/**
* Create a tooling config for dotnet backend
* @link https://mise.jdx.dev/dev-tools/backends/dotnet.html
*/
export function createDotnetToolConfig(name: string): BackendToolingConfig {
return {
packageName: name,
datasource: NugetDatasource.id,
};
}
/**
* Create a tooling config for gem backend
* @link https://mise.jdx.dev/dev-tools/backends/gem.html
*/
export function createGemToolConfig(name: string): BackendToolingConfig {
return {
packageName: name,
datasource: RubygemsDatasource.id,
};
}
/**
* Create a tooling config for github backend
* @link https://mise.jdx.dev/dev-tools/backends/github.html
*/
export function createGithubToolConfig(
name: string,
version: string,
toolOptions: MiseToolOptions,
): BackendToolingConfig {
let extractVersion: string | undefined = undefined;
const prefix = toolOptions.version_prefix;
if (isNonEmptyString(prefix)) {
extractVersion = `^${RegExp.escape(prefix)}(?<version>.+)`;
}
return {
packageName: name,
datasource: GithubReleasesDatasource.id,
currentValue: version,
...(extractVersion && { extractVersion }),
};
}
/**
* Create a tooling config for gitlab backend
* @link https://mise.jdx.dev/dev-tools/backends/gitlab.html
*/
export function createGitlabToolConfig(
name: string,
version: string,
toolOptions: MiseToolOptions,
): BackendToolingConfig {
let extractVersion: string | undefined = undefined;
const prefix = toolOptions.version_prefix;
if (isNonEmptyString(prefix)) {
extractVersion = `^${RegExp.escape(prefix)}(?<version>.+)`;
}
return {
packageName: name,
datasource: GitlabReleasesDatasource.id,
currentValue: version,
...(extractVersion && { extractVersion }),
};
}
/**
* Create a tooling config for go backend
* @link https://mise.jdx.dev/dev-tools/backends/go.html
*/
export function createGoToolConfig(name: string): BackendToolingConfig {
return {
packageName: name,
datasource: GoDatasource.id,
};
}
/**
* Create a tooling config for npm backend
* @link https://mise.jdx.dev/dev-tools/backends/npm.html
*/
export function createNpmToolConfig(name: string): BackendToolingConfig {
return {
packageName: name,
datasource: NpmDatasource.id,
};
}
const pipxGitHubRegex = regEx(/^git\+https:\/\/github\.com\/(?<repo>.+)\.git$/);
/**
* Create a tooling config for pipx backend
* @link https://mise.jdx.dev/dev-tools/backends/pipx.html
*/
export function createPipxToolConfig(name: string): BackendToolingConfig {
const isGitSyntax = name.startsWith('git+');
// Does not support zip file url
// Avoid type narrowing to prevent type error
if (!isGitSyntax && (isUrlString as (value: unknown) => boolean)(name)) {
return {
packageName: name,
skipReason: 'unsupported-url',
};
}
if (isGitSyntax || name.includes('/')) {
let repoName: string | undefined;
if (isGitSyntax) {
repoName = pipxGitHubRegex.exec(name)?.groups?.repo;
// If the url is not a github repo, treat the version as a git ref
if (isUndefined(repoName)) {
return {
packageName: name
.replace(regEx(/^git\+/g), '')
.replaceAll(regEx(/\.git$/g), ''),
datasource: GitRefsDatasource.id,
};
}
} else {
repoName = name;
}
return {
packageName: repoName,
datasource: GithubTagsDatasource.id,
};
}
return {
packageName: normalizePythonDepName(name),
datasource: PypiDatasource.id,
};
}
const spmGitHubRegex = regEx(/^https:\/\/github.com\/(?<repo>.+).git$/);
/**
* Create a tooling config for spm backend
* @link https://mise.jdx.dev/dev-tools/backends/spm.html
*/
export function createSpmToolConfig(name: string): BackendToolingConfig {
let repoName: string | undefined;
// Avoid type narrowing to prevent type error
if ((isUrlString as (value: unknown) => boolean)(name)) {
repoName = spmGitHubRegex.exec(name)?.groups?.repo;
// spm backend only supports github repos
if (!repoName) {
return {
packageName: name,
skipReason: 'unsupported-url',
};
}
}
return {
packageName: repoName ?? name,
datasource: GithubReleasesDatasource.id,
};
}
/**
* Create a tooling config for ubi backend
* @link https://mise.jdx.dev/dev-tools/backends/ubi.html
*/
export function createUbiToolConfig(
name: string,
version: string,
toolOptions: MiseToolOptions,
): BackendToolingConfig {
let extractVersion: string | undefined = undefined;
const hasVPrefix = version.startsWith('v');
const setsTagRegex = !hasVPrefix || isString(toolOptions.tag_regex);
if (setsTagRegex) {
// By default, use a regex that matches any tag
let tagRegex = '.+';
// Filter versions by tag_regex if it is specified
// ref: https://mise.jdx.dev/dev-tools/backends/ubi.html#ubi-uses-weird-versions
if (isString(toolOptions.tag_regex)) {
// Remove the leading '^' if it exists to avoid duplication
tagRegex = toolOptions.tag_regex.replace(regEx(/^\^/), '');
if (!hasVPrefix) {
// Remove the leading 'v?' if it exists to avoid duplication
tagRegex = tagRegex.replace(regEx(/^v\??/), '');
}
}
// Trim the 'v' prefix if the current version does not have it
extractVersion = `^${hasVPrefix ? '' : 'v?'}(?<version>${tagRegex})`;
}
return {
packageName: name,
datasource: GithubReleasesDatasource.id,
currentValue: version,
...(isString(extractVersion) ? { extractVersion } : {}),
};
}