From 4a304b8e6db9bfc32049b86717c549eaeb8bc8b9 Mon Sep 17 00:00:00 2001 From: James Brookes <jbrookes@confluent.io> Date: Mon, 8 Jul 2024 10:27:31 -0400 Subject: [PATCH] feat(manager): add mise package manager (#29950) --- docs/usage/node.md | 1 + lib/modules/manager/api.ts | 2 + .../manager/mise/__fixtures__/Mise.1.toml | 19 + lib/modules/manager/mise/extract.spec.ts | 354 ++++++++++++++++++ lib/modules/manager/mise/extract.ts | 107 ++++++ lib/modules/manager/mise/index.ts | 12 + lib/modules/manager/mise/readme.md | 55 +++ lib/modules/manager/mise/schema.ts | 16 + .../manager/mise/upgradeable-tooling.ts | 148 ++++++++ lib/modules/manager/mise/utils.spec.ts | 41 ++ lib/modules/manager/mise/utils.ts | 15 + tools/docs/index.ts | 5 + tools/docs/manager-mise-supported-plugins.ts | 29 ++ 13 files changed, 804 insertions(+) create mode 100644 lib/modules/manager/mise/__fixtures__/Mise.1.toml create mode 100644 lib/modules/manager/mise/extract.spec.ts create mode 100644 lib/modules/manager/mise/extract.ts create mode 100644 lib/modules/manager/mise/index.ts create mode 100644 lib/modules/manager/mise/readme.md create mode 100644 lib/modules/manager/mise/schema.ts create mode 100644 lib/modules/manager/mise/upgradeable-tooling.ts create mode 100644 lib/modules/manager/mise/utils.spec.ts create mode 100644 lib/modules/manager/mise/utils.ts create mode 100644 tools/docs/manager-mise-supported-plugins.ts diff --git a/docs/usage/node.md b/docs/usage/node.md index 5543401526..4fcca5e694 100644 --- a/docs/usage/node.md +++ b/docs/usage/node.md @@ -21,6 +21,7 @@ Renovate can manage the Node.js version in the following files: - The [`.nvmrc`](https://github.com/creationix/nvm#nvmrc) file for the [Node Version Manager](https://github.com/creationix/nvm) - The [`.node-version`](https://github.com/nodenv/nodenv#choosing-the-node-version) file for the [nodenv](https://github.com/nodenv/nodenv) environment manager - The [`.tool-versions`](https://asdf-vm.com/manage/configuration.html#tool-versions) file for the [asdf](https://github.com/asdf-vm/asdf) version manager +- The [`.mise.toml`](https://mise.jdx.dev/configuration.html#mise-toml) file for the [mise](https://github.com/jdx/mise) version manager - The [`node_js`](https://docs.travis-ci.com/user/languages/javascript-with-nodejs/#Specifying-Node.js-versions) field in [`.travis.yml`](https://docs.travis-ci.com/user/customizing-the-build/) ## Configuring which version of npm Renovate uses diff --git a/lib/modules/manager/api.ts b/lib/modules/manager/api.ts index 9616fd86cb..726479f4d0 100644 --- a/lib/modules/manager/api.ts +++ b/lib/modules/manager/api.ts @@ -56,6 +56,7 @@ import * as maven from './maven'; import * as mavenWrapper from './maven-wrapper'; import * as meteor from './meteor'; import * as mint from './mint'; +import * as mise from './mise'; import * as mix from './mix'; import * as nix from './nix'; import * as nodenv from './nodenv'; @@ -153,6 +154,7 @@ api.set('maven', maven); api.set('maven-wrapper', mavenWrapper); api.set('meteor', meteor); api.set('mint', mint); +api.set('mise', mise); api.set('mix', mix); api.set('nix', nix); api.set('nodenv', nodenv); diff --git a/lib/modules/manager/mise/__fixtures__/Mise.1.toml b/lib/modules/manager/mise/__fixtures__/Mise.1.toml new file mode 100644 index 0000000000..667f2d6a9e --- /dev/null +++ b/lib/modules/manager/mise/__fixtures__/Mise.1.toml @@ -0,0 +1,19 @@ +[env] +# supports arbitrary env vars so mise can be used like direnv/dotenv +NODE_ENV = 'production' + +[tools] +# specify single or multiple versions +java = '21.0.2' +erlang = ['23.3', '24.0'] + +# supports everything you can do with .tool-versions currently +node = ['16', 'prefix:20', 'ref:master', 'path:~/.nodes/14'] + +[plugins] +# specify a custom repo URL +# note this will only be used if the plugin does not already exist +python = 'https://github.com/asdf-community/asdf-python' + +[alias.node] # project-local aliases +my_custom_node = '20' diff --git a/lib/modules/manager/mise/extract.spec.ts b/lib/modules/manager/mise/extract.spec.ts new file mode 100644 index 0000000000..67e2edea53 --- /dev/null +++ b/lib/modules/manager/mise/extract.spec.ts @@ -0,0 +1,354 @@ +import { codeBlock } from 'common-tags'; +import { Fixtures } from '../../../../test/fixtures'; +import { extractPackageFile } from '.'; + +jest.mock('../../../util/fs'); + +const miseFilename = '.mise.toml'; + +const mise1toml = Fixtures.get('Mise.1.toml'); + +describe('modules/manager/mise/extract', () => { + describe('extractPackageFile()', () => { + it('returns null for empty', () => { + expect(extractPackageFile('', miseFilename)).toBeNull(); + }); + + it('returns null for invalid TOML', () => { + expect(extractPackageFile('foo', miseFilename)).toBeNull(); + }); + + it('returns null for empty tools section', () => { + const content = codeBlock` + [tools] + `; + expect(extractPackageFile(content, miseFilename)).toBeNull(); + }); + + it('extracts tools - mise core plugins', () => { + const content = codeBlock` + [tools] + erlang = '23.3' + node = '16' + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'erlang', + currentValue: '23.3', + datasource: 'github-tags', + }, + { + depName: 'node', + currentValue: '16', + datasource: 'node-version', + }, + ], + }); + }); + + it('extracts tools - asdf plugins', () => { + const content = codeBlock` + [tools] + terraform = '1.8.0' + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'terraform', + currentValue: '1.8.0', + }, + ], + }); + }); + + it('extracts tools with multiple versions', () => { + const content = codeBlock` + [tools] + erlang = ['23.3', '24.0'] + node = ['16', 'prefix:20', 'ref:master', 'path:~/.nodes/14'] + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'erlang', + currentValue: '23.3', + datasource: 'github-tags', + }, + { + depName: 'node', + currentValue: '16', + datasource: 'node-version', + }, + ], + }); + }); + + it('extracts tools with plugin options', () => { + const content = codeBlock` + [tools] + python = {version='3.11', virtualenv='.venv'} + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'python', + currentValue: '3.11', + }, + ], + }); + }); + + it('provides skipReason for lines with unsupported tooling', () => { + const content = codeBlock` + [tools] + fake-tool = '1.0.0' + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'fake-tool', + skipReason: 'unsupported-datasource', + }, + ], + }); + }); + + it('provides skipReason for missing version - empty string', () => { + const content = codeBlock` + [tools] + python = '' + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'python', + skipReason: 'unspecified-version', + }, + ], + }); + }); + + it('provides skipReason for missing version - missing version in object', () => { + const content = codeBlock` + [tools] + python = {virtualenv='.venv'} + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'python', + skipReason: 'unspecified-version', + }, + ], + }); + }); + + it('provides skipReason for missing version - empty array', () => { + const content = codeBlock` + [tools] + java = '21.0.2' + erlang = [] + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + }, + { + depName: 'erlang', + skipReason: 'unspecified-version', + }, + ], + }); + }); + + it('complete .mise.toml example', () => { + const result = extractPackageFile(mise1toml, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + { + depName: 'erlang', + currentValue: '23.3', + datasource: 'github-tags', + }, + { + depName: 'node', + currentValue: '16', + datasource: 'node-version', + }, + ], + }); + }); + + it('complete example with skip', () => { + const content = codeBlock` + [tools] + java = '21.0.2' + erlang = ['23.3', '24.0'] + terraform = {version='1.8.0'} + fake-tool = '1.6.2' + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + { + depName: 'erlang', + currentValue: '23.3', + datasource: 'github-tags', + }, + { + depName: 'terraform', + currentValue: '1.8.0', + }, + { + depName: 'fake-tool', + skipReason: 'unsupported-datasource', + }, + ], + }); + }); + + it('core java plugin function', () => { + const content = codeBlock` + [tools] + java = "21.0.2" + `; + const result = extractPackageFile(content, miseFilename); + expect(result).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + const content2 = codeBlock` + [tools] + java = "openjdk-21.0.2" + `; + const result2 = extractPackageFile(content2, miseFilename); + expect(result2).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + const content3 = codeBlock` + [tools] + java = "temurin-21.0.2" + `; + const result3 = extractPackageFile(content3, miseFilename); + expect(result3).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + const content4 = codeBlock` + [tools] + java = "zulu-21.0.2" + `; + const result4 = extractPackageFile(content4, miseFilename); + expect(result4).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + const content5 = codeBlock` + [tools] + java = "corretto-21.0.2" + `; + const result5 = extractPackageFile(content5, miseFilename); + expect(result5).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + const content6 = codeBlock` + [tools] + java = "oracle-graalvm-21.0.2" + `; + const result6 = extractPackageFile(content6, miseFilename); + expect(result6).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + const content7 = codeBlock` + [tools] + java = "adoptopenjdk-21.0.2" + `; + const result7 = extractPackageFile(content7, miseFilename); + expect(result7).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '21.0.2', + datasource: 'java-version', + }, + ], + }); + + // Test that fallback to asdf Plugin works + const content8 = codeBlock` + [tools] + java = "adoptopenjdk-jre-16.0.0+36" + `; + const result8 = extractPackageFile(content8, miseFilename); + expect(result8).toMatchObject({ + deps: [ + { + depName: 'java', + currentValue: '16.0.0+36', + datasource: 'java-version', + }, + ], + }); + }); + }); +}); diff --git a/lib/modules/manager/mise/extract.ts b/lib/modules/manager/mise/extract.ts new file mode 100644 index 0000000000..3828795d84 --- /dev/null +++ b/lib/modules/manager/mise/extract.ts @@ -0,0 +1,107 @@ +import is from '@sindresorhus/is'; +import { logger } from '../../../logger'; +import type { ToolingConfig } from '../asdf/upgradeable-tooling'; +import type { PackageDependency, PackageFileContent } from '../types'; +import type { MiseToolSchema } from './schema'; +import { + ToolingDefinition, + asdfTooling, + miseTooling, +} from './upgradeable-tooling'; +import { parseTomlFile } from './utils'; + +export function extractPackageFile( + content: string, + packageFile: string, +): PackageFileContent | null { + logger.trace(`mise.extractPackageFile(${packageFile})`); + + const misefile = parseTomlFile(content, packageFile); + if (!misefile) { + return null; + } + + const deps: PackageDependency[] = []; + const tools = misefile.tools; + + if (tools) { + for (const [name, toolData] of Object.entries(tools)) { + const version = parseVersion(toolData); + const depName = name.trim(); + const toolConfig = getToolConfig(depName, version); + const dep = createDependency(depName, version, toolConfig); + deps.push(dep); + } + } + + return deps.length ? { deps } : null; +} + +function parseVersion(toolData: MiseToolSchema): string | null { + if (is.nonEmptyString(toolData)) { + // Handle the string case + // e.g. 'erlang = "23.3"' + return toolData; + } + if (is.array(toolData, is.string)) { + // Handle the array case + // e.g. 'erlang = ["23.3", "24.0"]' + return toolData.length ? toolData[0] : null; // Get the first version in the array + } + if (is.nonEmptyString(toolData.version)) { + // Handle the object case with a string version + // e.g. 'python = { version = "3.11.2" }' + return toolData.version; + } + return null; // Return null if no version is found +} + +function getToolConfig( + name: string, + version: string | null, +): ToolingConfig | null { + if (version === null) { + return null; // Early return if version is null + } + + // Try to get the config from miseTooling first, then asdfTooling + return ( + getConfigFromTooling(miseTooling, name, version) ?? + getConfigFromTooling(asdfTooling, name, version) + ); +} + +function getConfigFromTooling( + toolingSource: Record<string, ToolingDefinition>, + name: string, + version: string, +): ToolingConfig | null { + const toolDefinition = toolingSource[name]; + if (!toolDefinition) { + return null; + } // Return null if no toolDefinition is found + + return ( + (typeof toolDefinition.config === 'function' + ? toolDefinition.config(version) + : toolDefinition.config) ?? null + ); // Ensure null is returned instead of undefined +} + +function createDependency( + name: string, + version: string | null, + config: ToolingConfig | null, +): PackageDependency { + if (version === null) { + return { depName: name, skipReason: 'unspecified-version' }; + } + if (config === null) { + return { depName: name, skipReason: 'unsupported-datasource' }; + } + return { + depName: name, + currentValue: version, + ...config, + }; +} diff --git a/lib/modules/manager/mise/index.ts b/lib/modules/manager/mise/index.ts new file mode 100644 index 0000000000..d90ebcad5b --- /dev/null +++ b/lib/modules/manager/mise/index.ts @@ -0,0 +1,12 @@ +import { supportedDatasources as asdfSupportedDatasources } from '../asdf'; + +export { extractPackageFile } from './extract'; + +export const displayName = 'mise'; + +export const defaultConfig = { + fileMatch: ['(^|/)\\.mise\\.toml$'], +}; + +// Re-use the asdf datasources, as mise and asdf support the same plugins. +export const supportedDatasources = asdfSupportedDatasources; diff --git a/lib/modules/manager/mise/readme.md b/lib/modules/manager/mise/readme.md new file mode 100644 index 0000000000..11c125b854 --- /dev/null +++ b/lib/modules/manager/mise/readme.md @@ -0,0 +1,55 @@ +Renovate can update the [mise](https://mise.jdx.dev/configuration.html#mise-toml) `.mise.toml` file. + +Renovate's `mise` manager can version these tools: + +<!-- Autogenerate in https://github.com/renovatebot/renovate --> +<!-- Autogenerate end --> + +### Renovate only updates primary versions + +Renovate's `mise` manager is designed to automatically update the _first_ (primary) version listed for each tool in the `mise.toml` file. + +Secondary or fallback versions require manual updates. + +#### Example + +Given a `.mise.toml` entry like: + +```toml +[tools] +erlang = ["23.3", "22.0"] +``` + +Renovate will update `"23.3"` (the primary version) but will not touch `"22.0"` (the fallback version). + +#### Why can Renovate only update primary versions? + +To maintain consistency and reliability, Renovate opts to only manage the _first_ listed version. + +- Fallback versions can often be older versions of a tool that are known to work and are there as a backup. + +This follows the same workflow that Renovate's `asdf` manager uses. + +### Plugin/tool support + +Renovate uses: + +- [mise's plugins](https://github.com/jdx/mise/tree/main/src/plugins/core) +- [asdf's plugins](https://mise.jdx.dev/registry.html) + +to understand and manage tool versioning. + +Support for new tools/plugins needs to be _manually_ added to Renovate's logic. + +#### Adding new tool support + +There are 2 ways to integrate versioning for a new tool: + +- Renovate's `mise` manager: ensure upstream `mise` supports the tool, then add support to the `mise` manager in Renovate +- Renovate's `asdf` manager: improve the `asdf` manager in Renovate, which automatically extends support to `mise` + +If `mise` adds support for more tools via its own [core plugins](https://mise.jdx.dev/plugins.html#core-plugins), you can create a PR to extend Renovate's `mise` manager to add support for the new tooling. + +You may be able to add support for new tooling upstream in the core plugins - create an issue and see if the community agrees whether it belongs there, or if it would be better as an `asdf-` plugin. + +If you are wanting to add support for an existing `asdf-x` plugin to `mise`, you can create a PR to extend Renovate's `asdf` manager, which indirectly helps Renovate's `mise` manager as well. diff --git a/lib/modules/manager/mise/schema.ts b/lib/modules/manager/mise/schema.ts new file mode 100644 index 0000000000..b4614ebff9 --- /dev/null +++ b/lib/modules/manager/mise/schema.ts @@ -0,0 +1,16 @@ +import { z } from 'zod'; +import { Toml } from '../../../util/schema-utils'; + +const MiseToolSchema = z.union([ + z.string(), + z.object({ version: z.string().optional() }), + z.array(z.string()), +]); +export type MiseToolSchema = z.infer<typeof MiseToolSchema>; + +export const MiseFileSchema = z.object({ + tools: z.record(MiseToolSchema), +}); +export type MiseFileSchema = z.infer<typeof MiseFileSchema>; + +export const MiseFileSchemaToml = Toml.pipe(MiseFileSchema); diff --git a/lib/modules/manager/mise/upgradeable-tooling.ts b/lib/modules/manager/mise/upgradeable-tooling.ts new file mode 100644 index 0000000000..f13a4c6736 --- /dev/null +++ b/lib/modules/manager/mise/upgradeable-tooling.ts @@ -0,0 +1,148 @@ +import { GithubReleasesDatasource } from '../../datasource/github-releases'; +import { GithubTagsDatasource } from '../../datasource/github-tags'; +import { JavaVersionDatasource } from '../../datasource/java-version'; +import { NodeVersionDatasource } from '../../datasource/node-version'; +import { RubyVersionDatasource } from '../../datasource/ruby-version'; +import * as regexVersioning from '../../versioning/regex'; +import * as semverVersioning from '../../versioning/semver'; +import { ToolingConfig, upgradeableTooling } from '../asdf/upgradeable-tooling'; + +export interface ToolingDefinition { + config: ToolingConfig; + misePluginUrl?: string; +} + +export const asdfTooling = upgradeableTooling; + +export const miseTooling: Record<string, ToolingDefinition> = { + bun: { + misePluginUrl: 'https://mise.jdx.dev/lang/bun.html', + config: { + packageName: 'oven-sh/bun', + datasource: GithubReleasesDatasource.id, + extractVersion: '^bun-v(?<version>\\S+)', + }, + }, + deno: { + misePluginUrl: 'https://mise.jdx.dev/lang/deno.html', + config: { + packageName: 'denoland/deno', + datasource: GithubReleasesDatasource.id, + extractVersion: '^v(?<version>\\S+)', + }, + }, + erlang: { + misePluginUrl: 'https://mise.jdx.dev/lang/erlang.html', + config: { + packageName: 'erlang/otp', + datasource: GithubTagsDatasource.id, + extractVersion: '^OTP-(?<version>\\S+)', + versioning: `${regexVersioning.id}:^(?<major>\\d+?)\\.(?<minor>\\d+?)(\\.(?<patch>\\d+))?$`, + }, + }, + go: { + misePluginUrl: 'https://mise.jdx.dev/lang/go.html', + config: { + packageName: 'golang/go', + datasource: GithubTagsDatasource.id, + extractVersion: '^go(?<version>\\S+)', + }, + }, + java: { + misePluginUrl: 'https://mise.jdx.dev/lang/java.html', + config: (version) => { + // no prefix is shorthand for openjdk + const versionMatch = version.match(/^(\d\S+)/)?.[1]; + if (versionMatch) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: versionMatch, + }; + } + const openJdkMatches = version.match( + /^openjdk-(?<version>\d\S+)/, + )?.groups; + if (openJdkMatches) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: openJdkMatches.version, + }; + } + const adoptOpenJdkMatches = version.match( + /^adoptopenjdk-(?<version>\d\S+)/, + )?.groups; + if (adoptOpenJdkMatches) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: adoptOpenJdkMatches.version, + }; + } + const temurinJdkMatches = version.match( + /^temurin-(?<version>\d\S+)/, + )?.groups; + if (temurinJdkMatches) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: temurinJdkMatches.version, + }; + } + const correttoJdkMatches = version.match( + /^corretto-(?<version>\d\S+)/, + )?.groups; + if (correttoJdkMatches) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: correttoJdkMatches.version, + }; + } + const zuluJdkMatches = version.match(/^zulu-(?<version>\d\S+)/)?.groups; + if (zuluJdkMatches) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: zuluJdkMatches.version, + }; + } + const oracleGraalvmJdkMatches = version.match( + /^oracle-graalvm-(?<version>\d\S+)/, + )?.groups; + if (oracleGraalvmJdkMatches) { + return { + datasource: JavaVersionDatasource.id, + packageName: 'java-jdk', + currentValue: oracleGraalvmJdkMatches.version, + }; + } + + return undefined; + }, + }, + node: { + misePluginUrl: 'https://mise.jdx.dev/lang/node.html', + config: { + packageName: 'nodejs', + datasource: NodeVersionDatasource.id, + }, + }, + python: { + misePluginUrl: 'https://mise.jdx.dev/lang/python.html', + config: { + packageName: 'python/cpython', + datasource: GithubTagsDatasource.id, + extractVersion: '^v(?<version>\\S+)', + }, + }, + ruby: { + misePluginUrl: 'https://mise.jdx.dev/lang/ruby.html', + config: { + packageName: 'ruby-version', + datasource: RubyVersionDatasource.id, + versioning: semverVersioning.id, + }, + }, +}; diff --git a/lib/modules/manager/mise/utils.spec.ts b/lib/modules/manager/mise/utils.spec.ts new file mode 100644 index 0000000000..38c1594f7b --- /dev/null +++ b/lib/modules/manager/mise/utils.spec.ts @@ -0,0 +1,41 @@ +import { codeBlock } from 'common-tags'; +import { parseTomlFile } from './utils'; + +const miseFilename = '.mise.toml'; + +describe('modules/manager/mise/utils', () => { + describe('parseTomlFile', () => { + it('load and parse successfully', () => { + const fileContent = codeBlock` + [tools] + erlang = '23.3' + node = '16' + `; + const actual = parseTomlFile(fileContent, miseFilename); + expect(actual).toMatchObject({ + tools: { + erlang: '23.3', + node: '16', + }, + }); + }); + + it('invalid toml', () => { + const invalidToml = codeBlock` + clearly: "invalid" "toml" + `; + const actual = parseTomlFile(invalidToml, miseFilename); + expect(actual).toBeNull(); + }); + + it('invalid schema', () => { + const content = codeBlock` + [invalid] + erlang = '23.3' + node = '16' + `; + const actual = parseTomlFile(content, miseFilename); + expect(actual).toBeNull(); + }); + }); +}); diff --git a/lib/modules/manager/mise/utils.ts b/lib/modules/manager/mise/utils.ts new file mode 100644 index 0000000000..8ca8a4c63d --- /dev/null +++ b/lib/modules/manager/mise/utils.ts @@ -0,0 +1,15 @@ +import { logger } from '../../../logger'; +import { MiseFileSchema, MiseFileSchemaToml } from './schema'; + +export function parseTomlFile( + content: string, + packageFile: string, +): MiseFileSchema | null { + const res = MiseFileSchemaToml.safeParse(content); + if (res.success) { + return res.data; + } else { + logger.debug({ err: res.error, packageFile }, 'Error parsing Mise file.'); + return null; + } +} diff --git a/tools/docs/index.ts b/tools/docs/index.ts index 737138b108..b105c7ed2a 100644 --- a/tools/docs/index.ts +++ b/tools/docs/index.ts @@ -7,6 +7,7 @@ import { generateDatasources } from './datasources'; import { getOpenGitHubItems } from './github-query-items'; import { generateManagers } from './manager'; import { generateManagerAsdfSupportedPlugins } from './manager-asdf-supported-plugins'; +import { generateManagerMiseSupportedPlugins } from './manager-mise-supported-plugins'; import { generatePlatforms } from './platforms'; import { generatePresets } from './presets'; import { generateSchema } from './schema'; @@ -46,6 +47,10 @@ export async function generateDocs(): Promise<void> { logger.info('* managers/asdf/supported-plugins'); await generateManagerAsdfSupportedPlugins(dist); + // managers/mise supported plugins + logger.info('* managers/mise/supported-plugins'); + await generateManagerMiseSupportedPlugins(dist); + // presets logger.info('* presets'); await generatePresets(dist); diff --git a/tools/docs/manager-mise-supported-plugins.ts b/tools/docs/manager-mise-supported-plugins.ts new file mode 100644 index 0000000000..f2291f9ab3 --- /dev/null +++ b/tools/docs/manager-mise-supported-plugins.ts @@ -0,0 +1,29 @@ +import { + asdfTooling, + miseTooling, +} from '../../lib/modules/manager/mise/upgradeable-tooling'; +import { readFile, updateFile } from '../utils'; +import { replaceContent } from './utils'; + +function generateMiseTooling(): string { + return Object.entries(miseTooling) + .map(([name, { misePluginUrl }]) => `- [${name} (mise)](${misePluginUrl})`) + .join('\n'); +} + +function generateAsdfTooling(): string { + return Object.entries(asdfTooling) + .map(([name, { asdfPluginUrl }]) => `- [${name} (asdf)](${asdfPluginUrl})`) + .join('\n'); +} + +export async function generateManagerMiseSupportedPlugins( + dist: string, +): Promise<void> { + const indexFileName = `${dist}/modules/manager/mise/index.md`; + let indexContent = await readFile(indexFileName); + // Combine the output of both mise and asdf tooling generation + const combinedTooling = `${generateMiseTooling()}\n${generateAsdfTooling()}`; + indexContent = replaceContent(indexContent, combinedTooling); + await updateFile(indexFileName, indexContent); +} -- GitLab