Files
renovate/lib/modules/manager/proto/extract.spec.ts
Rhuan Barreto e5f067ccce feat(manager): add proto manager for .prototools files (#42908)
* feat(manager): add proto manager for .prototools files

Add a new manager for moonrepo's proto tool version manager.
Proto uses .prototools (TOML) files to pin tool versions at the
directory level. The manager extracts top-level version entries
and maps them to the appropriate Renovate datasources.

Supported built-in tools: bun, deno, go, moon, node, npm, pnpm,
yarn, python, ruby, rust, proto, gh, poetry, uv.

Non-version sections ([settings], [plugins], [tools], [env],
[shell], [backends]) are correctly ignored. Version aliases
(latest, stable, canary, nightly) are skipped with appropriate
skipReason. Unknown tools are reported as unsupported-datasource.

* fix(manager): address proto manager PR review feedback

- Remove deduplicateArray/sort from supportedDatasources, sort manually
- Sort protoTooling entries alphabetically
- Inline createDependency call into deps.push()
- Inline fixture content into test, remove fixture file
2026-04-28 07:04:44 +00:00

306 lines
7.6 KiB
TypeScript

import { codeBlock } from 'common-tags';
import { extractPackageFile } from './index.ts';
vi.mock('../../../util/fs/index.ts');
const protoFilename = '.prototools';
describe('modules/manager/proto/extract', () => {
describe('extractPackageFile()', () => {
it('returns null for empty content', () => {
expect(extractPackageFile('', protoFilename)).toBeNull();
});
it('returns null for invalid TOML', () => {
expect(extractPackageFile('{{invalid', protoFilename)).toBeNull();
});
it('returns null when only config sections exist', () => {
const content = codeBlock`
[settings]
auto-install = true
[env]
DEBUG = "*"
`;
expect(extractPackageFile(content, protoFilename)).toBeNull();
});
it('extracts a single tool version', () => {
const content = codeBlock`
node = "22.14.0"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'node',
currentValue: '22.14.0',
datasource: 'node-version',
packageName: 'node',
},
],
});
});
it('extracts multiple tool versions', () => {
const content = codeBlock`
node = "22.14.0"
bun = "1.2.2"
npm = "11.6.2"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'node',
currentValue: '22.14.0',
datasource: 'node-version',
},
{
depName: 'bun',
currentValue: '1.2.2',
datasource: 'github-releases',
packageName: 'oven-sh/bun',
},
{
depName: 'npm',
currentValue: '11.6.2',
datasource: 'npm',
packageName: 'npm',
},
],
});
});
it('skips non-version sections', () => {
const content = codeBlock`
node = "22.14.0"
[settings]
auto-install = true
[plugins.tools]
my-tool = "https://example.com/plugin.toml"
[tools.node]
bundled-npm = true
[env]
DEBUG = "*"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'node',
currentValue: '22.14.0',
datasource: 'node-version',
},
],
});
expect(result!.deps).toHaveLength(1);
});
it('handles proto self-versioning', () => {
const content = codeBlock`
proto = "0.56.0"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'proto',
currentValue: '0.56.0',
datasource: 'github-releases',
packageName: 'moonrepo/proto',
},
],
});
});
it('handles moon tool', () => {
const content = codeBlock`
moon = "1.30.0"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'moon',
currentValue: '1.30.0',
datasource: 'github-releases',
packageName: 'moonrepo/moon',
},
],
});
});
it('handles uv tool', () => {
const content = codeBlock`
uv = "0.6.0"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'uv',
currentValue: '0.6.0',
datasource: 'github-releases',
packageName: 'astral-sh/uv',
},
],
});
});
it('marks unknown tools as unsupported-datasource', () => {
const content = codeBlock`
unknown-tool = "1.0.0"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'unknown-tool',
currentValue: '1.0.0',
skipReason: 'unsupported-datasource',
},
],
});
});
it('skips alias values like latest', () => {
const content = codeBlock`
node = "latest"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'node',
currentValue: 'latest',
skipReason: 'unsupported-version',
},
],
});
});
it('skips alias value stable', () => {
const content = codeBlock`
rust = "stable"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'rust',
currentValue: 'stable',
skipReason: 'unsupported-version',
},
],
});
});
it('handles partial versions', () => {
const content = codeBlock`
go = "~1.22"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).toMatchObject({
deps: [
{
depName: 'go',
currentValue: '~1.22',
datasource: 'github-tags',
packageName: 'golang/go',
},
],
});
});
it('extracts all supported tools from fixture', () => {
const content = codeBlock`
node = "22.14.0"
bun = "1.2.2"
npm = "11.6.2"
go = "~1.22"
proto = "0.56.0"
[settings]
auto-install = true
detect-strategy = "prefer-prototools"
[plugins.tools]
my-tool = "https://raw.githubusercontent.com/my/tool/master/proto-plugin.toml"
[tools.node]
bundled-npm = true
[env]
DEBUG = "*"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).not.toBeNull();
expect(result!.deps).toHaveLength(5);
expect(result!.deps).toMatchObject([
{
depName: 'node',
currentValue: '22.14.0',
datasource: 'node-version',
},
{
depName: 'bun',
currentValue: '1.2.2',
datasource: 'github-releases',
packageName: 'oven-sh/bun',
},
{
depName: 'npm',
currentValue: '11.6.2',
datasource: 'npm',
packageName: 'npm',
},
{
depName: 'go',
currentValue: '~1.22',
datasource: 'github-tags',
packageName: 'golang/go',
},
{
depName: 'proto',
currentValue: '0.56.0',
datasource: 'github-releases',
packageName: 'moonrepo/proto',
},
]);
});
it('extracts all supported built-in tools', () => {
const content = codeBlock`
bun = "1.2.2"
deno = "2.0.0"
go = "1.22.0"
moon = "1.30.0"
node = "22.14.0"
npm = "11.6.2"
pnpm = "9.0.0"
yarn = "4.0.0"
python = "3.12.0"
ruby = "3.3.0"
rust = "1.80.0"
proto = "0.56.0"
gh = "2.60.0"
poetry = "1.8.0"
uv = "0.6.0"
`;
const result = extractPackageFile(content, protoFilename);
expect(result).not.toBeNull();
expect(result!.deps).toHaveLength(15);
// Verify no deps have skipReason
for (const dep of result!.deps) {
expect(dep.skipReason).toBeUndefined();
}
});
});
});