diff --git a/renovate-config.json b/renovate-config.json index 2e6de2b3..a2915f1c 100644 --- a/renovate-config.json +++ b/renovate-config.json @@ -1,5 +1,7 @@ { "$schema": "https://docs.renovatebot.com/renovate-schema.json", "description": "Renovate onboarding preset for use with LizardByte's repos", - "extends": ["github>LizardByte/.github:renovate-config.json5"] + "extends": [ + "github>LizardByte/.github//renovate/default.json5" + ] } diff --git a/renovate-config.json5 b/renovate-config.json5 deleted file mode 100644 index e0552a4e..00000000 --- a/renovate-config.json5 +++ /dev/null @@ -1,364 +0,0 @@ -{ - // see https://docs.renovatebot.com/configuration-options - "$schema": "https://docs.renovatebot.com/renovate-schema.json", - "branchNameStrict": true, // remove special characters from branch names - "cdnurl": { - "managerFilePatterns": [ - "/(^|/)assets/.*[.]css$/", - ], - }, - // Read the Docs publishes the exact OS and runtime selectors it accepts in this schema. - "customDatasources": { - "readthedocs-os": { - "defaultRegistryUrlTemplate": "https://raw.githubusercontent.com/readthedocs/readthedocs.org/main/readthedocs/rtd_tests/fixtures/spec/v2/schema.json", - "transformTemplates": [ - "($versions := properties.build.properties.os.enum[$match($, /^ubuntu-[0-9]+\\.[0-9]+$/)]; {\"releases\": $map($versions, function($version) { {\"version\": $substringAfter($version, \"ubuntu-\")} }), \"sourceUrl\": \"https://github.com/readthedocs/readthedocs.org\", \"homepage\": \"https://docs.readthedocs.com/platform/stable/config-file/v2.html\"})", - ], - }, - "readthedocs-tools": { - "defaultRegistryUrlTemplate": "https://raw.githubusercontent.com/readthedocs/readthedocs.org/main/readthedocs/rtd_tests/fixtures/spec/v2/schema.json", - "transformTemplates": [ - "($versions := $lookup(properties.build.properties.tools.properties, \"{{{packageName}}}\").enum; $numeric := \"{{{packageName}}}\" = \"nodejs\" ? $versions[$match($, /^[0-9]+$/)] : $versions[$match($, /^[0-9]+\\.[0-9]+$/)]; {\"releases\": $map($numeric, function($version) { {\"version\": $version} }), \"sourceUrl\": \"https://github.com/readthedocs/readthedocs.org\", \"homepage\": \"https://docs.readthedocs.com/platform/stable/config-file/v2.html\"})", - ], - }, - }, - "cloneSubmodules": true, // needed for pyproject.toml that depend on submodules - "cloneSubmodulesFilter": [ - "packaging/linux/flatpak/deps/flatpak-builder-tools", // Sunshine - "third-party/glad", // Sunshine - "third-party/lizardbyte-common", // c/c++ projects - "third-party/sphinx-csharp", // Themerr-jellyfin - ], - "configMigration": true, // allow renovate to open PRs to update config - "description": "Default renovate configuration for LizardByte repositories", - "dependencyDashboard": true, // Creates an issue with a "dashboard" of dependencies - "dependencyDashboardLabels": [ - "dependencies" - ], - "extends": [ - "config:recommended", - "group:monorepos", - "group:recommended", - ":semanticCommits", - ":semanticCommitTypeAll(chore)", - "github>LizardByte/.github//renovate/themerr.json5", - ], - "labels": [ - "dependencies", - "{{categories}}" - ], - lockFileMaintenance: { - enabled: true, - }, - "prConcurrentLimit": 10, - "rebaseWhen": "never", // to reduce CI load, do not auto rebase PRs - "rollbackPrs": true, // create a rollback PR when a dependency is yanked - "schedule": ["after 1am and before 8am"], - "semanticCommitScope": "deps", - "semanticCommitType": "chore", - "semanticCommits": "enabled", - "timezone": "America/New_York", - "updatePinnedDependencies": true, - // beta features - "git-submodules": { - "enabled": false // renovate cannot update to tagged submodules, so keep using dependabot for this - }, - // group/package rules - "packageRules": [ - // Create separate PRs for each major version unless the dependency is already grouped. - { - "matchJsonata": ["groupName = null or $not($exists(groupName))"], - "separateMultipleMajor": true, - }, - // Do not open standalone cdnjs SRI digest updates. Version updates still refresh the paired SRI hash. - { - "matchDatasources": ["cdnjs"], - "matchUpdateTypes": ["digest"], - "enabled": false, - }, - // Conda release candidates are not suitable for pinned build environments. - { - "matchDatasources": ["conda"], - "allowedVersions": "!/rc/i", - }, - // Keep Debian Docker tags on plain codenames or major versions, not dated rebuild tags. - { - "matchDatasources": ["docker"], - "matchPackageNames": [ - "debian", - ], - "matchCurrentValue": "/^(?:[a-z]+|\\d+)(?:-[a-z]+)?$/", - "allowedVersions": "/^(?:[a-z]+|\\d+)(?:-[a-z]+)?$/" - }, - // Group GitHub maintained actions - { - "groupName": "Official GitHub Actions", - "groupSlug": "github-actions", - "matchManagers": [ - "github-actions" - ], - "matchPackageNames": [ - "actions/**", - "github/**" - ], - "separateMultipleMajor": false, - }, - // Group docker actions - { - "groupName": "Docker Actions", - "groupSlug": "docker-actions", - "matchManagers": [ - "github-actions" - ], - "matchPackageNames": [ - "docker/**", - ], - "separateMultipleMajor": false, - }, - // Group dev dependencies - { - "groupName": "devDependencies (non-major)", - "matchDepTypes": [ - "devDependencies", - ], - "matchUpdateTypes": ["patch", "minor"], - "schedule": ["* 1-7 * * 1"], - }, - // Keep npm and GitHub dependency forms of highlight.js on the same version. - { - "groupName": "highlight.js", - "matchPackageNames": [ - "@highlightjs/cdn-assets", - "highlightjs/highlight.js", - ], - }, - // Group Renovate updates in the .github repository and schedule them weekly - { - "groupName": "Renovate", - "groupSlug": "renovate", - "matchPackageNames": [ - "renovate", - ], - "matchRepositories": [ - "LizardByte/.github", - ], - "schedule": ["* 1-7 * * 1"], - "separateMultipleMajor": false, - }, - // Schedule Lucide updates weekly because it releases frequently - { - "matchPackageNames": [ - "lucide", - ], - "schedule": ["* 1-7 * * 1"], - }, - // Rollback PRs miscalculate PEP 440 values for uv-backed pinned dependencies. - { - "matchDatasources": [ - "pypi", - ], - "matchManagers": [ - "pep621", - ], - "rollbackPrs": false, - }, - // Group pytest - { - "groupName": "pytest", - "matchCategories": [ - "python", - ], - "matchPackageNames": [ - "pytest*", - ], - "separateMultipleMajor": false, - }, - // combine oddstr13/jellyfin-plugin-repository-manager and pypi jprm into a single group - { - "groupName": "jprm", - "matchCategories": [ - "ci", - "python", - ], - "matchPackageNames": [ - "oddstr13/jellyfin-plugin-repository-manager", - "jprm", - ], - "separateMultipleMajor": false, - }, - ], - "customManagers": [ - // Numeric runtime versions selected by Read the Docs. - { - "customType": "regex", - "description": "Update Read the Docs Python tool versions", - "managerFilePatterns": [ - "/(^|/)\\.readthedocs\\.ya?ml$/", - ], - "matchStrings": [ - "(?:^|\\r?\\n)[ \\t]+python:[ \\t]*[\"']?(?\\d+\\.\\d+)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", - ], - "datasourceTemplate": "custom.readthedocs-tools", - "depNameTemplate": "python", - "packageNameTemplate": "python", - "versioningTemplate": "semver-coerced", - }, - { - "customType": "regex", - "description": "Update Read the Docs Node.js tool versions", - "managerFilePatterns": [ - "/(^|/)\\.readthedocs\\.ya?ml$/", - ], - "matchStrings": [ - "(?:^|\\r?\\n)[ \\t]+nodejs:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", - ], - "datasourceTemplate": "custom.readthedocs-tools", - "depNameTemplate": "node", - "packageNameTemplate": "nodejs", - "versioningTemplate": "semver-coerced", - }, - { - "customType": "regex", - "description": "Update Read the Docs Rust tool versions", - "managerFilePatterns": [ - "/(^|/)\\.readthedocs\\.ya?ml$/", - ], - "matchStrings": [ - "(?:^|\\r?\\n)[ \\t]+rust:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", - ], - "datasourceTemplate": "custom.readthedocs-tools", - "depNameTemplate": "rust", - "packageNameTemplate": "rust", - "versioningTemplate": "semver-coerced", - }, - { - "customType": "regex", - "description": "Update Read the Docs Go tool versions", - "managerFilePatterns": [ - "/(^|/)\\.readthedocs\\.ya?ml$/", - ], - "matchStrings": [ - "(?:^|\\r?\\n)[ \\t]+golang:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", - ], - "datasourceTemplate": "custom.readthedocs-tools", - "depNameTemplate": "go", - "packageNameTemplate": "golang", - "versioningTemplate": "semver-coerced", - }, - { - "customType": "regex", - "description": "Update Read the Docs Ruby tool versions", - "managerFilePatterns": [ - "/(^|/)\\.readthedocs\\.ya?ml$/", - ], - "matchStrings": [ - "(?:^|\\r?\\n)[ \\t]+ruby:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", - ], - "datasourceTemplate": "custom.readthedocs-tools", - "depNameTemplate": "ruby-version", - "packageNameTemplate": "ruby", - "versioningTemplate": "semver-coerced", - }, - { - "customType": "regex", - "description": "Update Read the Docs Ubuntu build images", - "managerFilePatterns": [ - "/(^|/)\\.readthedocs\\.ya?ml$/", - ], - "matchStrings": [ - "(?:^|\\r?\\n)[ \\t]+os:[ \\t]*[\"']?ubuntu-(?\\d{2}\\.04)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", - ], - "datasourceTemplate": "custom.readthedocs-os", - "packageNameTemplate": "os", - "depNameTemplate": "readthedocs/ubuntu", - "versioningTemplate": "loose", - }, - // Annotated GitHub release, tag, or branch values paired with immutable commits. - { - "customType": "regex", - "description": "Update annotated GitHub values and their jsDelivr commits", - "managerFilePatterns": [ - "/^(?:\\.github/workflows/.*[.]ya?ml|actions/[^/]+/action[.]ya?ml)$/", - ], - "matchStrings": [ - "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:\\s+versioning=(?[^\\s]+?))?\\s+[a-zA-Z0-9_]+?_(?:branch|release|tag|version)=\\\"(?[^\\\"\\s]+)\\\"\\s+[a-zA-Z0-9_]+?_ref=\\\"(?[a-fA-F0-9]{40})\\\"", - ], - "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}", - }, - // cdnjs URLs in Jekyll config files, front matter, data YAML, and simple HTML attrs, with optional adjacent SRI hashes. - { - "customType": "regex", - "managerFilePatterns": [ - "/^(?:_config(?:_[^/]+)?[.]ya?ml|_data/.*[.]ya?ml|_layouts/.*[.]html|[^/]+[.](?:html|md)|blog/.*[.]html|_posts/.*[.]md|docs/.*[.]md|gh-pages-template/(?:_data/.*[.]ya?ml|.*[.](?:html|md)))$/", - ], - "matchStrings": [ - "(?:(?:href|src):\\s*[\"']?|-\\s*[\"']?|(?:href|src)=[\"'])(?:https?:)?//cdnjs\\.cloudflare\\.com/ajax/libs/(?[^/\"'\\s]+)/(?[^/\"'\\s]+)/(?[^\"'\\s?#]+)(?:\\?[^\"'\\s]*)?[\"']?(?:\\s*\\r?\\n\\s*sri:\\s*[\"'](?sha(?:256|384|512)-[^\"']+)[\"'])?", - ], - "datasourceTemplate": "cdnjs", - "packageNameTemplate": "{{depName}}/{{asset}}", - "versioningTemplate": "semver", - }, - // Versioned npm CDN href/src/scalar entries in Jekyll config files, front matter, data YAML, and simple HTML attrs without SRI. - { - "customType": "regex", - "managerFilePatterns": [ - "/^(?:_config(?:_[^/]+)?[.]ya?ml|_data/.*[.]ya?ml|_layouts/.*[.]html|[^/]+[.](?:html|md)|blog/.*[.]html|_posts/.*[.]md|docs/.*[.]md|gh-pages-template/(?:_data/.*[.]ya?ml|.*[.](?:html|md)))$/", - ], - "matchStrings": [ - "(?:(?:href|src|icon_img):\\s*[\"']?|(?:href|src)=[\"'])https://(?:cdn\\.jsdelivr\\.net/npm|unpkg\\.com)/(?(?:@[^@/\"'\\s]+/)?[^@/\"'\\s]+)@(?[^/\"'\\s?#]+)", - "(?:^|\\r?\\n)[ \\t]*-[ \\t]*[\"']?https://(?:cdn\\.jsdelivr\\.net/npm|unpkg\\.com)/(?(?:@[^@/\"'\\s]+/)?[^@/\"'\\s]+)@(?[^/\"'\\s?#]+)", - ], - "datasourceTemplate": "npm", - "versioningTemplate": "npm", - }, - // Versioned npm CDN URLs in selected source/data files without SRI. - { - "customType": "regex", - "managerFilePatterns": [ - "/^(?:_includes/.*[.]html|apps/.*[.]json|assets/.*[.]js|dockle[.]toml|docs/source/conf[.]py|jsdoc[.]config[.]js)$/", - ], - "matchStrings": [ - "https://(?:cdn\\.jsdelivr\\.net/npm|unpkg\\.com)/(?(?:@[^@/\"'\\s]+/)?[^@/\"'\\s]+)@(?v?\\d[^/\"'\\s?#]*)", - ], - "datasourceTemplate": "npm", - "versioningTemplate": "npm", - }, - // Annotated YAML version fields such as _data/licenses.yml in LizardByte.github.io - { - "customType": "regex", - "managerFilePatterns": [ - "/^(?:_data/.*[.]ya?ml|gh-pages-template/_data/.*[.]ya?ml)$/", - ], - "matchStrings": [ - "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:(?:[ \\t]+|\\s*\\r?\\n\\s*#\\s*)packageName=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n\\s*#\\s*)versioning=(?[^\\s]+?))?\\s+version:\\s*[\"']?(?[^\"'\\s]+)[\"']?", - ], - "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}", - }, - // Pinned conda-forge dependencies in Conda environment files. - { - "customType": "regex", - "description": "Update pinned conda-forge dependencies in Conda environment files", - "managerFilePatterns": [ - "/(^|/)environment[.]ya?ml$/", - ], - "matchStrings": [ - "\\r?\\n(?[ \\t]{2})-[ \\t]*(?[A-Za-z0-9_.-]+)[ \\t]*={1,2}[ \\t]*(?[^=\\s#][^\\s#]*)", - ], - "currentValueTemplate": "=={{{currentValue}}}", - "datasourceTemplate": "conda", - "packageNameTemplate": "conda-forge/{{{depName}}}", - "versioningTemplate": "conda", - "autoReplaceStringTemplate": "\n{{{indentation}}}- {{{depName}}}=={{{newVersion}}}", - }, - // CPM Package Lock - { - "customType": "regex", - "managerFilePatterns": ["/(^|\/)package-lock.cmake$/"], - "matchStrings": [ - "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)packageName=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)versioning=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)extractVersion=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)registryUrl=(?[^\\s]+?))?\\s+set\\([A-Za-z0-9_]+?_(?:VERSION|TAG)\\s+\\\"?(?[^\\\"\\s\\)]+)\\\"?\\s*\\)", - "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)packageName=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)versioning=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)extractVersion=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)registryUrl=(?[^\\s]+?))?\\s+set\\([A-Za-z0-9_]+?_(?:VERSION|TAG)\\s+\\\"?(?[^\\\"\\s\\)]+)\\\"?\\s*\\)\\s+set\\([A-Za-z0-9_]+?_SHA256\\s+(?[a-fA-F0-9]{64})\\s*\\)", - ], - "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}" - }, - ], -} diff --git a/renovate/cdn.json5 b/renovate/cdn.json5 new file mode 100644 index 00000000..321dc105 --- /dev/null +++ b/renovate/cdn.json5 @@ -0,0 +1,64 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "CDN dependency extraction, digest policy, and update schedules", + "cdnurl": { + "managerFilePatterns": [ + "/(^|/)assets/.*[.]css$/", + ], + }, + "packageRules": [ + // Do not open standalone cdnjs SRI digest updates. Version updates still refresh the paired SRI hash. + { + "matchDatasources": ["cdnjs"], + "matchUpdateTypes": ["digest"], + "enabled": false, + }, + // Schedule Lucide updates weekly because it releases frequently + { + "matchPackageNames": [ + "lucide", + ], + "schedule": ["* 1-7 * * 1"], + }, + ], + "customManagers": [ + // cdnjs URLs in Jekyll config files, front matter, data YAML, and simple HTML attrs, with optional adjacent SRI hashes. + { + "customType": "regex", + "managerFilePatterns": [ + "/^(?:_config(?:_[^/]+)?[.]ya?ml|_data/.*[.]ya?ml|_layouts/.*[.]html|[^/]+[.](?:html|md)|blog/.*[.]html|_posts/.*[.]md|docs/.*[.]md|gh-pages-template/(?:_data/.*[.]ya?ml|.*[.](?:html|md)))$/", + ], + "matchStrings": [ + "(?:(?:href|src):\\s*[\"']?|-\\s*[\"']?|(?:href|src)=[\"'])(?:https?:)?//cdnjs\\.cloudflare\\.com/ajax/libs/(?[^/\"'\\s]+)/(?[^/\"'\\s]+)/(?[^\"'\\s?#]+)(?:\\?[^\"'\\s]*)?[\"']?(?:\\s*\\r?\\n\\s*sri:\\s*[\"'](?sha(?:256|384|512)-[^\"']+)[\"'])?", + ], + "datasourceTemplate": "cdnjs", + "packageNameTemplate": "{{depName}}/{{asset}}", + "versioningTemplate": "semver", + }, + // Versioned npm CDN href/src/scalar entries in Jekyll config files, front matter, data YAML, and simple HTML attrs without SRI. + { + "customType": "regex", + "managerFilePatterns": [ + "/^(?:_config(?:_[^/]+)?[.]ya?ml|_data/.*[.]ya?ml|_layouts/.*[.]html|[^/]+[.](?:html|md)|blog/.*[.]html|_posts/.*[.]md|docs/.*[.]md|gh-pages-template/(?:_data/.*[.]ya?ml|.*[.](?:html|md)))$/", + ], + "matchStrings": [ + "(?:(?:href|src|icon_img):\\s*[\"']?|(?:href|src)=[\"'])https://(?:cdn\\.jsdelivr\\.net/npm|unpkg\\.com)/(?(?:@[^@/\"'\\s]+/)?[^@/\"'\\s]+)@(?[^/\"'\\s?#]+)", + "(?:^|\\r?\\n)[ \\t]*-[ \\t]*[\"']?https://(?:cdn\\.jsdelivr\\.net/npm|unpkg\\.com)/(?(?:@[^@/\"'\\s]+/)?[^@/\"'\\s]+)@(?[^/\"'\\s?#]+)", + ], + "datasourceTemplate": "npm", + "versioningTemplate": "npm", + }, + // Versioned npm CDN URLs in selected source/data files without SRI. + { + "customType": "regex", + "managerFilePatterns": [ + "/^(?:_includes/.*[.]html|apps/.*[.]json|assets/.*[.]js|dockle[.]toml|docs/source/conf[.]py|jsdoc[.]config[.]js)$/", + ], + "matchStrings": [ + "https://(?:cdn\\.jsdelivr\\.net/npm|unpkg\\.com)/(?(?:@[^@/\"'\\s]+/)?[^@/\"'\\s]+)@(?v?\\d[^/\"'\\s?#]*)", + ], + "datasourceTemplate": "npm", + "versioningTemplate": "npm", + }, + ], +} diff --git a/renovate/cmake.json5 b/renovate/cmake.json5 new file mode 100644 index 00000000..4248cc8e --- /dev/null +++ b/renovate/cmake.json5 @@ -0,0 +1,16 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Annotated CPM package lock dependencies", + "customManagers": [ + // CPM Package Lock + { + "customType": "regex", + "managerFilePatterns": ["/(^|\/)package-lock.cmake$/"], + "matchStrings": [ + "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)packageName=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)versioning=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)extractVersion=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)registryUrl=(?[^\\s]+?))?\\s+set\\([A-Za-z0-9_]+?_(?:VERSION|TAG)\\s+\\\"?(?[^\\\"\\s\\)]+)\\\"?\\s*\\)", + "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)packageName=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)versioning=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)extractVersion=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n#\\s*)registryUrl=(?[^\\s]+?))?\\s+set\\([A-Za-z0-9_]+?_(?:VERSION|TAG)\\s+\\\"?(?[^\\\"\\s\\)]+)\\\"?\\s*\\)\\s+set\\([A-Za-z0-9_]+?_SHA256\\s+(?[a-fA-F0-9]{64})\\s*\\)", + ], + "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}" + }, + ], +} diff --git a/renovate/common.json5 b/renovate/common.json5 new file mode 100644 index 00000000..690c4c96 --- /dev/null +++ b/renovate/common.json5 @@ -0,0 +1,44 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Common Renovate defaults for LizardByte repositories", + "branchNameStrict": true, // remove special characters from branch names + "cloneSubmodules": true, // needed for pyproject.toml that depend on submodules + "cloneSubmodulesFilter": [ + "packaging/linux/flatpak/deps/flatpak-builder-tools", // Sunshine and Koko + "third-party/glad", // Sunshine + "third-party/lizardbyte-common", // c/c++ projects + "third-party/sphinx-csharp", // Themerr + ], + "configMigration": true, // allow renovate to open PRs to update config + "dependencyDashboard": true, // Creates an issue with a "dashboard" of dependencies + "dependencyDashboardLabels": [ + "dependencies" + ], + "labels": [ + "dependencies", + "{{categories}}" + ], + lockFileMaintenance: { + enabled: true, + }, + "prConcurrentLimit": 10, + "rebaseWhen": "never", // to reduce CI load, do not auto rebase PRs + "rollbackPrs": true, // create a rollback PR when a dependency is yanked + "schedule": ["after 1am and before 8am"], + "semanticCommitScope": "deps", + "semanticCommitType": "chore", + "semanticCommits": "enabled", + "timezone": "America/New_York", + "updatePinnedDependencies": true, + // beta features + "git-submodules": { + "enabled": false // renovate cannot update to tagged submodules, so keep using dependabot for this + }, + "packageRules": [ + // Create separate PRs for each major version unless the dependency is already grouped. + { + "matchJsonata": ["groupName = null or $not($exists(groupName))"], + "separateMultipleMajor": true, + }, + ], +} diff --git a/renovate/conda.json5 b/renovate/conda.json5 new file mode 100644 index 00000000..a29fce56 --- /dev/null +++ b/renovate/conda.json5 @@ -0,0 +1,35 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Conda environment dependencies and stable Python releases", + "packageRules": [ + // Conda release candidates are not suitable for pinned build environments. + { + "matchDatasources": ["conda"], + "allowedVersions": "!/rc/i", + }, + // Conda treats Python prereleases as stable; allow only final Python versions. + { + "matchDatasources": ["conda"], + "matchDepNames": ["python"], + "allowedVersions": "/^\\d+\\.\\d+\\.\\d+$/", + }, + ], + "customManagers": [ + // Pinned conda-forge dependencies in Conda environment files. + { + "customType": "regex", + "description": "Update pinned conda-forge dependencies in Conda environment files", + "managerFilePatterns": [ + "/(^|/)environment[.]ya?ml$/", + ], + "matchStrings": [ + "\\r?\\n(?[ \\t]{2})-[ \\t]*(?[A-Za-z0-9_.-]+)[ \\t]*={1,2}[ \\t]*(?[^=\\s#][^\\s#]*)", + ], + "currentValueTemplate": "=={{{currentValue}}}", + "datasourceTemplate": "conda", + "packageNameTemplate": "conda-forge/{{{depName}}}", + "versioningTemplate": "conda", + "autoReplaceStringTemplate": "\n{{{indentation}}}- {{{depName}}}=={{{newVersion}}}", + }, + ], +} diff --git a/renovate/default.json5 b/renovate/default.json5 new file mode 100644 index 00000000..7184e7b8 --- /dev/null +++ b/renovate/default.json5 @@ -0,0 +1,23 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Default renovate configuration for LizardByte repositories", + "extends": [ + "config:recommended", + "group:monorepos", + "group:recommended", + ":semanticCommits", + ":semanticCommitTypeAll(chore)", + "github>LizardByte/.github//renovate/themerr.json5", + // Apply common defaults before ecosystem grouping overrides. + "github>LizardByte/.github//renovate/common.json5", + "github>LizardByte/.github//renovate/conda.json5", + "github>LizardByte/.github//renovate/docker.json5", + "github>LizardByte/.github//renovate/github-actions.json5", + "github>LizardByte/.github//renovate/npm.json5", + "github>LizardByte/.github//renovate/cdn.json5", + "github>LizardByte/.github//renovate/python.json5", + "github>LizardByte/.github//renovate/readthedocs.json5", + "github>LizardByte/.github//renovate/cmake.json5", + "github>LizardByte/.github//renovate/jekyll.json5", + ], +} diff --git a/renovate/docker.json5 b/renovate/docker.json5 new file mode 100644 index 00000000..5e9efc23 --- /dev/null +++ b/renovate/docker.json5 @@ -0,0 +1,15 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Docker image tag policy", + "packageRules": [ + // Keep Debian Docker tags on plain codenames or major versions, not dated rebuild tags. + { + "matchDatasources": ["docker"], + "matchPackageNames": [ + "debian", + ], + "matchCurrentValue": "/^(?:[a-z]+|\\d+)(?:-[a-z]+)?$/", + "allowedVersions": "/^(?:[a-z]+|\\d+)(?:-[a-z]+)?$/" + }, + ], +} diff --git a/renovate/github-actions.json5 b/renovate/github-actions.json5 new file mode 100644 index 00000000..4d69b558 --- /dev/null +++ b/renovate/github-actions.json5 @@ -0,0 +1,45 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "GitHub Actions groups and annotated GitHub references", + "packageRules": [ + // Group GitHub maintained actions + { + "groupName": "Official GitHub Actions", + "groupSlug": "github-actions", + "matchManagers": [ + "github-actions" + ], + "matchPackageNames": [ + "actions/**", + "github/**" + ], + "separateMultipleMajor": false, + }, + // Group docker actions + { + "groupName": "Docker Actions", + "groupSlug": "docker-actions", + "matchManagers": [ + "github-actions" + ], + "matchPackageNames": [ + "docker/**", + ], + "separateMultipleMajor": false, + }, + ], + "customManagers": [ + // Annotated GitHub release, tag, or branch values paired with immutable commits. + { + "customType": "regex", + "description": "Update annotated GitHub values and their jsDelivr commits", + "managerFilePatterns": [ + "/^(?:\\.github/workflows/.*[.]ya?ml|actions/[^/]+/action[.]ya?ml)$/", + ], + "matchStrings": [ + "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:\\s+versioning=(?[^\\s]+?))?\\s+[a-zA-Z0-9_]+?_(?:branch|release|tag|version)=\\\"(?[^\\\"\\s]+)\\\"\\s+[a-zA-Z0-9_]+?_ref=\\\"(?[a-fA-F0-9]{40})\\\"", + ], + "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{else}}semver{{/if}}", + }, + ], +} diff --git a/renovate/jekyll.json5 b/renovate/jekyll.json5 new file mode 100644 index 00000000..236c7829 --- /dev/null +++ b/renovate/jekyll.json5 @@ -0,0 +1,17 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Annotated dependency versions in Jekyll data files", + "customManagers": [ + // Annotated YAML version fields such as _data/licenses.yml in LizardByte.github.io + { + "customType": "regex", + "managerFilePatterns": [ + "/^(?:_data/.*[.]ya?ml|gh-pages-template/_data/.*[.]ya?ml)$/", + ], + "matchStrings": [ + "# renovate: datasource=(?[a-zA-Z0-9-._]+?) depName=(?[^\\s]+?)(?:(?:[ \\t]+|\\s*\\r?\\n\\s*#\\s*)packageName=(?[^\\s]+?))?(?:(?:[ \\t]+|\\s*\\r?\\n\\s*#\\s*)versioning=(?[^\\s]+?))?\\s+version:\\s*[\"']?(?[^\"'\\s]+)[\"']?", + ], + "versioningTemplate": "{{#if versioning}}{{{versioning}}}{{/if}}", + }, + ], +} diff --git a/renovate/npm.json5 b/renovate/npm.json5 new file mode 100644 index 00000000..0c93046d --- /dev/null +++ b/renovate/npm.json5 @@ -0,0 +1,36 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "npm dependency groups and update schedules", + "packageRules": [ + // Group dev dependencies + { + "groupName": "devDependencies (non-major)", + "matchDepTypes": [ + "devDependencies", + ], + "matchUpdateTypes": ["patch", "minor"], + "schedule": ["* 1-7 * * 1"], + }, + // Keep npm and GitHub dependency forms of highlight.js on the same version. + { + "groupName": "highlight.js", + "matchPackageNames": [ + "@highlightjs/cdn-assets", + "highlightjs/highlight.js", + ], + }, + // Group Renovate updates in the .github repository and schedule them weekly + { + "groupName": "Renovate", + "groupSlug": "renovate", + "matchPackageNames": [ + "renovate", + ], + "matchRepositories": [ + "LizardByte/.github", + ], + "schedule": ["* 1-7 * * 1"], + "separateMultipleMajor": false, + }, + ], +} diff --git a/renovate/python.json5 b/renovate/python.json5 new file mode 100644 index 00000000..bd22d7a5 --- /dev/null +++ b/renovate/python.json5 @@ -0,0 +1,40 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Python dependency groups and uv rollback policy", + "packageRules": [ + // Rollback PRs miscalculate PEP 440 values for uv-backed pinned dependencies. + { + "matchDatasources": [ + "pypi", + ], + "matchManagers": [ + "pep621", + ], + "rollbackPrs": false, + }, + // Group pytest + { + "groupName": "pytest", + "matchCategories": [ + "python", + ], + "matchPackageNames": [ + "pytest*", + ], + "separateMultipleMajor": false, + }, + // combine oddstr13/jellyfin-plugin-repository-manager and pypi jprm into a single group + { + "groupName": "jprm", + "matchCategories": [ + "ci", + "python", + ], + "matchPackageNames": [ + "oddstr13/jellyfin-plugin-repository-manager", + "jprm", + ], + "separateMultipleMajor": false, + }, + ], +} diff --git a/renovate/readthedocs.json5 b/renovate/readthedocs.json5 new file mode 100644 index 00000000..9e9b50ad --- /dev/null +++ b/renovate/readthedocs.json5 @@ -0,0 +1,106 @@ +{ + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "description": "Read the Docs supported runtime selectors and build images", + // Read the Docs publishes the exact OS and runtime selectors it accepts in this schema. + "customDatasources": { + "readthedocs-os": { + "defaultRegistryUrlTemplate": "https://raw.githubusercontent.com/readthedocs/readthedocs.org/main/readthedocs/rtd_tests/fixtures/spec/v2/schema.json", + "transformTemplates": [ + "($versions := properties.build.properties.os.enum[$match($, /^ubuntu-[0-9]+\\.[0-9]+$/)]; {\"releases\": $map($versions, function($version) { {\"version\": $substringAfter($version, \"ubuntu-\")} }), \"sourceUrl\": \"https://github.com/readthedocs/readthedocs.org\", \"homepage\": \"https://docs.readthedocs.com/platform/stable/config-file/v2.html\"})", + ], + }, + "readthedocs-tools": { + "defaultRegistryUrlTemplate": "https://raw.githubusercontent.com/readthedocs/readthedocs.org/main/readthedocs/rtd_tests/fixtures/spec/v2/schema.json", + "transformTemplates": [ + "($versions := $lookup(properties.build.properties.tools.properties, \"{{{packageName}}}\").enum; $numeric := \"{{{packageName}}}\" = \"nodejs\" ? $versions[$match($, /^[0-9]+$/)] : $versions[$match($, /^[0-9]+\\.[0-9]+$/)]; {\"releases\": $map($numeric, function($version) { {\"version\": $version} }), \"sourceUrl\": \"https://github.com/readthedocs/readthedocs.org\", \"homepage\": \"https://docs.readthedocs.com/platform/stable/config-file/v2.html\"})", + ], + }, + }, + "customManagers": [ + // Numeric runtime versions selected by Read the Docs. + { + "customType": "regex", + "description": "Update Read the Docs Python tool versions", + "managerFilePatterns": [ + "/(^|/)\\.readthedocs\\.ya?ml$/", + ], + "matchStrings": [ + "(?:^|\\r?\\n)[ \\t]+python:[ \\t]*[\"']?(?\\d+\\.\\d+)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", + ], + "datasourceTemplate": "custom.readthedocs-tools", + "depNameTemplate": "python", + "packageNameTemplate": "python", + "versioningTemplate": "semver-coerced", + }, + { + "customType": "regex", + "description": "Update Read the Docs Node.js tool versions", + "managerFilePatterns": [ + "/(^|/)\\.readthedocs\\.ya?ml$/", + ], + "matchStrings": [ + "(?:^|\\r?\\n)[ \\t]+nodejs:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", + ], + "datasourceTemplate": "custom.readthedocs-tools", + "depNameTemplate": "node", + "packageNameTemplate": "nodejs", + "versioningTemplate": "semver-coerced", + }, + { + "customType": "regex", + "description": "Update Read the Docs Rust tool versions", + "managerFilePatterns": [ + "/(^|/)\\.readthedocs\\.ya?ml$/", + ], + "matchStrings": [ + "(?:^|\\r?\\n)[ \\t]+rust:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", + ], + "datasourceTemplate": "custom.readthedocs-tools", + "depNameTemplate": "rust", + "packageNameTemplate": "rust", + "versioningTemplate": "semver-coerced", + }, + { + "customType": "regex", + "description": "Update Read the Docs Go tool versions", + "managerFilePatterns": [ + "/(^|/)\\.readthedocs\\.ya?ml$/", + ], + "matchStrings": [ + "(?:^|\\r?\\n)[ \\t]+golang:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", + ], + "datasourceTemplate": "custom.readthedocs-tools", + "depNameTemplate": "go", + "packageNameTemplate": "golang", + "versioningTemplate": "semver-coerced", + }, + { + "customType": "regex", + "description": "Update Read the Docs Ruby tool versions", + "managerFilePatterns": [ + "/(^|/)\\.readthedocs\\.ya?ml$/", + ], + "matchStrings": [ + "(?:^|\\r?\\n)[ \\t]+ruby:[ \\t]*[\"']?(?\\d+(?:\\.\\d+)*)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", + ], + "datasourceTemplate": "custom.readthedocs-tools", + "depNameTemplate": "ruby-version", + "packageNameTemplate": "ruby", + "versioningTemplate": "semver-coerced", + }, + { + "customType": "regex", + "description": "Update Read the Docs Ubuntu build images", + "managerFilePatterns": [ + "/(^|/)\\.readthedocs\\.ya?ml$/", + ], + "matchStrings": [ + "(?:^|\\r?\\n)[ \\t]+os:[ \\t]*[\"']?ubuntu-(?\\d{2}\\.04)[\"']?[ \\t]*(?:#[^\\r\\n]*)?(?:\\r?\\n|$)", + ], + "datasourceTemplate": "custom.readthedocs-os", + "packageNameTemplate": "os", + "depNameTemplate": "readthedocs/ubuntu", + "versioningTemplate": "loose", + }, + ], +} diff --git a/tests/renovate-config.test.mjs b/tests/renovate-config.test.mjs index 4bd2a7fb..ad730e78 100644 --- a/tests/renovate-config.test.mjs +++ b/tests/renovate-config.test.mjs @@ -1,6 +1,6 @@ import assert from 'node:assert/strict'; import fs from 'node:fs'; -import test from 'node:test'; +import test, {mock} from 'node:test'; import JSON5 from 'json5'; import {api as condaVersioning} from 'renovate/dist/modules/versioning/conda/index.js'; @@ -14,15 +14,38 @@ import {getExpression} from 'renovate/dist/util/jsonata.js'; import {compile} from 'renovate/dist/util/template/index.js'; import {applyPackageRules} from 'renovate/dist/util/package-rules/index.js'; import {filterVersions} from 'renovate/dist/workers/repository/process/lookup/filter.js'; -import {mergeChildConfig} from 'renovate/dist/config/utils.js'; -import {presetSources, resolveConfigPresets} from 'renovate/dist/config/presets/index.js'; -import {init as initPresetCache, set as setPresetCache} from 'renovate/dist/util/cache/memory/index.js'; +import {GlobalConfig} from 'renovate/dist/config/global.js'; +import {resolveConfigPresets} from 'renovate/dist/config/presets/index.js'; +import {init as initPresetCache} from 'renovate/dist/util/cache/memory/index.js'; +import {GithubHttp} from 'renovate/dist/util/http/github.js'; -const mainConfig = JSON5.parse(fs.readFileSync('renovate-config.json5', 'utf8')); -const themerrConfig = JSON5.parse(fs.readFileSync('renovate/themerr.json5', 'utf8')); -const renovateConfig = mergeChildConfig(themerrConfig, mainConfig); +const rootConfig = JSON.parse(fs.readFileSync('renovate-config.json', 'utf8')); +const {config: renovateConfig} = await resolveLocalPresets(rootConfig); + +async function resolveLocalPresets(inputConfig) { + // Exercise Renovate's real source loaders and filename resolution using local files. + initPresetCache(); + GlobalConfig.set({platform: 'github', endpoint: 'https://api.github.com/'}); + const presetFiles = new Set([ + 'renovate-config.json', + ...fs.readdirSync('renovate').filter(name => name.endsWith('.json5')) + .map(name => `renovate/${name}`), + ]); + const request = mock.method(GithubHttp.prototype, 'getJsonUnchecked', async (url, options = {}) => { + const resolvedUrl = new URL(url, options.baseUrl ?? 'https://api.github.com/').href; + const match = /^https:\/\/api\.github\.com\/repos\/LizardByte\/\.github\/contents\/(.+)$/.exec(resolvedUrl); + assert.ok(match && presetFiles.has(match[1]), `Unexpected preset request: ${resolvedUrl}`); + return {body: {content: fs.readFileSync(match[1]).toString('base64')}}; + }); + + try { + return await resolveConfigPresets(inputConfig); + } finally { + request.mock.restore(); + } +} const githubRefManager = renovateConfig.customManagers.find( - manager => manager.description === 'Update annotated GitHub values and their jsDelivr commits', + manager => manager.description?.includes('Update annotated GitHub values and their jsDelivr commits'), ); const jekyllNpmCdnManager = renovateConfig.customManagers.find( manager => manager.datasourceTemplate === 'npm' @@ -37,10 +60,10 @@ const condaEnvironmentManager = renovateConfig.customManagers.find( && manager.managerFilePatterns.some(pattern => pattern.includes('environment')), ); const readTheDocsToolManagers = renovateConfig.customManagers.filter( - manager => manager.description?.endsWith('tool versions'), + manager => manager.datasourceTemplate === 'custom.readthedocs-tools', ); const readTheDocsOsManager = renovateConfig.customManagers.find( - manager => manager.description === 'Update Read the Docs Ubuntu build images', + manager => manager.description?.includes('Update Read the Docs Ubuntu build images'), ); function matchesManagerFilePattern(fileName, patterns) { @@ -260,7 +283,7 @@ dependencies: test('excludes release candidates from Conda updates', async () => { const [dependency] = extractCondaDependencies( 'environment.yml', - '\ndependencies:\n - python==3.13.15\n', + '\ndependencies:\n - doxygen==1.2.3\n', ); const resolved = await applyPackageRules({ ...dependency, @@ -271,15 +294,54 @@ test('excludes release candidates from Conda updates', async () => { assert.deepEqual( filterVersions( resolved, - '3.13.15', + '1.2.3', undefined, - [{version: '3.14.2'}, {version: '3.15.0rc2'}, {version: '3.15.0RC3'}], + [{version: '1.3.0'}, {version: '1.4.0rc2'}, {version: '1.4.0RC3'}], condaVersioning, ).map(release => release.version), - ['3.14.2'], + ['1.3.0'], ); }); +test('allows only stable Python releases in Conda environments', async () => { + const releases = [ + '3.13.16', + '3.14.2', + '3.15.0a1', + '3.15.0b1', + '3.15.0rc2', + '3.15.0RC3', + '3.15.0.dev1', + '3.15.0', + '3.16.0a1', + '3.16.0b1', + '3.16.0rc1', + '3.16.0.dev1', + ].map(version => ({version})); + + for (const [currentVersion, expectedVersions] of [ + ['3.13.15', ['3.13.16', '3.14.2', '3.15.0']], + ['3.15.0rc1', ['3.15.0']], + ]) { + const [dependency] = extractCondaDependencies( + 'environment.yml', + `\ndependencies:\n - python==${currentVersion}\n`, + ); + const resolved = await applyPackageRules({ + ...dependency, + ignoreUnstable: true, + packageRules: renovateConfig.packageRules, + }); + + assert.deepEqual( + filterVersions(resolved, currentVersion, undefined, releases, condaVersioning) + .map(release => release.version), + expectedVersions, + currentVersion, + ); + } +}); + test('extracts numeric Read the Docs runtime versions', () => { const fileName = '.readthedocs.yaml'; const content = ` @@ -622,7 +684,7 @@ test('extracts cdnjs imports from asset stylesheets', () => { test('extracts Jellyfin runtime targets without updating Themerr SDK and EF baselines', () => { const manager = renovateConfig.customManagers.find( - value => value.description === 'Update Themerr Jellyfin runtime validation targets', + value => value.description?.includes('Update Themerr Jellyfin runtime validation targets'), ); const fileName = 'src/jellyfin/compatibility.props'; assert.ok(matchesManagerFilePattern(fileName, manager.managerFilePatterns)); @@ -650,7 +712,7 @@ test('extracts Jellyfin runtime targets without updating Themerr SDK and EF base test('limits Themerr Jellyfin runtime updates to stable hotfixes in their series', async () => { const manager = renovateConfig.customManagers.find( - value => value.description === 'Update Themerr Jellyfin runtime validation targets', + value => value.description?.includes('Update Themerr Jellyfin runtime validation targets'), ); for (const [currentValue, releases, expected] of [ [ @@ -737,35 +799,75 @@ test('pins Jellyfin SDK and EF only in Themerr connector projects', async () => }); -test('resolves the root onboarding preset through the root main and Themerr JSON5 presets', async context => { - const rootConfig = JSON.parse(fs.readFileSync('renovate-config.json', 'utf8')); - const mainPreset = 'github>LizardByte/.github:renovate-config.json5'; - const themerrPreset = 'github>LizardByte/.github//renovate/themerr.json5'; - assert.deepEqual(rootConfig.extends, [mainPreset]); - assert.ok(mainConfig.extends.includes(themerrPreset)); - assert.ok(mainConfig.customManagers.every(manager => manager.depNameTemplate !== 'jellyfin/jellyfin')); - assert.ok(mainConfig.packageRules.every(rule => !rule.matchRepositories?.includes('LizardByte/Themerr'))); +test('resolves existing consumer entry points through all split presets', async () => { + const repositoryConfig = JSON.parse(fs.readFileSync('renovate.json', 'utf8')); + for (const inputConfig of [ + rootConfig, + repositoryConfig, + {extends: ['github>LizardByte/.github:renovate-config']}, + ]) { + const {config} = await resolveLocalPresets(inputConfig); + assert.deepEqual(config.packageRules, renovateConfig.packageRules); + assert.deepEqual(config.customManagers, renovateConfig.customManagers); + assert.deepEqual(config.customDatasources, renovateConfig.customDatasources); + assert.equal(config.timezone, 'America/New_York'); + assert.equal(config.forkProcessing, inputConfig.forkProcessing); + } +}); - // Resolve the actual preset chain with local contents; unexpected network fetches fail the test. - initPresetCache(); - setPresetCache(`preset:${mainPreset}`, mainConfig); - setPresetCache(`preset:${themerrPreset}`, themerrConfig); - context.mock.method(presetSources.github, 'load', async () => { - throw new Error('Preset test attempted a network fetch.'); - }); - const {config, visitedPresets} = await resolveConfigPresets(rootConfig, undefined, undefined, [], false); - assert.deepEqual(visitedPresets.merged, [ - mainPreset, - themerrPreset, - ]); - const manager = config.customManagers.find(value => value.depNameTemplate === 'jellyfin/jellyfin'); - assert.ok(manager, 'Expected the Themerr runtime manager in the resolved main preset'); - const resolved = await applyPackageRules({ - repository: 'LizardByte/Themerr', - packageFile: 'src/jellyfin/compatibility.props', - packageName: 'jellyfin/jellyfin', - currentValue: '12.2', - packageRules: config.packageRules, - }); - assert.equal(resolved.allowedVersions, '/^12\\.[0-9]+$/'); +test('preserves grouping and schedule overrides after resolving the split presets', async () => { + for (const [dependency, expected] of [ + [ + {packageName: 'example-tool'}, + {groupName: undefined, separateMultipleMajor: true}, + ], + [ + {packageName: 'example-tool', groupName: 'Existing group', separateMultipleMajor: false}, + {groupName: 'Existing group', separateMultipleMajor: false}, + ], + [ + {packageName: 'actions/checkout', manager: 'github-actions'}, + {groupName: 'Official GitHub Actions', separateMultipleMajor: false}, + ], + [ + {packageName: 'docker/build-push-action', manager: 'github-actions'}, + {groupName: 'Docker Actions', separateMultipleMajor: false}, + ], + [ + {packageName: 'example-tool', depType: 'devDependencies', updateType: 'minor'}, + {groupName: 'devDependencies (non-major)', separateMultipleMajor: true, schedule: ['* 1-7 * * 1']}, + ], + [ + {packageName: '@highlightjs/cdn-assets', depType: 'devDependencies', updateType: 'minor'}, + {groupName: 'highlight.js', separateMultipleMajor: true, schedule: ['* 1-7 * * 1']}, + ], + [ + {packageName: 'renovate', repository: 'LizardByte/.github', depType: 'devDependencies', updateType: 'minor'}, + {groupName: 'Renovate', separateMultipleMajor: false, schedule: ['* 1-7 * * 1']}, + ], + [ + {packageName: 'renovate', repository: 'LizardByte/dockle'}, + {groupName: undefined, separateMultipleMajor: true}, + ], + [ + {packageName: 'lucide'}, + {groupName: undefined, separateMultipleMajor: true, schedule: ['* 1-7 * * 1']}, + ], + [ + {packageName: 'pytest', categories: ['python']}, + {groupName: 'pytest', separateMultipleMajor: false}, + ], + [ + {packageName: 'jprm', categories: ['python']}, + {groupName: 'jprm', separateMultipleMajor: false}, + ], + ]) { + const resolved = await applyPackageRules({ + ...dependency, + packageRules: renovateConfig.packageRules, + }); + for (const [key, value] of Object.entries(expected)) { + assert.deepEqual(resolved[key], value, `${dependency.packageName}: ${key}`); + } + } });