diff --git a/extract-docker-images.js b/extract-docker-images.js new file mode 100644 index 00000000..ad3e1620 --- /dev/null +++ b/extract-docker-images.js @@ -0,0 +1,57 @@ +const fs = require('fs'); +const yaml = require('js-yaml'); + +/** + * Recursively traverse the object and extract all image entries. + * @param {object} obj - The YAML object to traverse. + * @param {Array} path - Key path for generating service names. + * @returns {Array} - List of {depName, currentValue, service}. + */ +function extractImages(obj, path = []) { + const result = []; + + if (typeof obj !== 'object' || obj === null) return result; + + // Resolve merged objects (anchors or << merges) + let mergedObj = {}; + if (obj['<<']) { + const merge = obj['<<']; + if (Array.isArray(merge)) { + merge.forEach(m => Object.assign(mergedObj, m)); + } else if (typeof merge === 'object') { + Object.assign(mergedObj, merge); + } + } + // Merge top-level keys last to override anchor defaults + Object.assign(mergedObj, obj); + + // If an image key exists, capture it + if (mergedObj.image) { + const [depName, currentValue] = mergedObj.image.split(':'); + result.push({ + depName: depName, + currentValue: currentValue || 'latest', + service: path.join('/') || 'root' + }); + } + + // Recursively traverse all keys + for (const [key, value] of Object.entries(mergedObj)) { + // Skip the << key since we've already processed it + if (key === '<<') continue; + result.push(...extractImages(value, [...path, key])); + } + + return result; +}; + +module.exports = function extractDockerImages(fileContent) { + try { + // Parse YAML with anchors preserved + const doc = yaml.load(fileContent, { schema: yaml.DEFAULT_FULL_SCHEMA }); + return extractImages(doc); + } catch (err) { + console.error('Failed parsing docker-compose.yml:', err); + return []; + } +}; diff --git a/renovate.json b/renovate.json index 7190a60b..dd6dbd97 100644 --- a/renovate.json +++ b/renovate.json @@ -1,3 +1,51 @@ { - "$schema": "https://docs.renovatebot.com/renovate-schema.json" + "$schema": "https://docs.renovatebot.com/renovate-schema.json", + "extends": ["config:recommended"], + "prHourlyLimit": 2, + "prConcurrentLimit": 5, + "dependencyDashboard": true, + "labels": ["dependencies", "renovate"], + "schedule": ["before 6am on monday"], + + "docker": { + "enabled": true + }, + + "packageRules": [ + { + "matchDatasources": ["docker"], + "matchPackageNames": ["clickhouse/clickhouse-server"], + "enabled": false + }, + { + "matchDatasources": ["docker"], + "matchPackagePatterns": [".*-alpine$"], + "allowedVersions": "/.*-alpine$/" + }, + { + "matchDatasources": ["docker"], + "matchPackagePatterns": ["^.*:latest$"], + "enabled": false + } + ], + + "regexManagers": [], + + "packageFiles": [ + { + "fileMatch": ["docker-compose\\.ya?ml$"], + "manager": "regex", + "extractVersion": "custom", + "datasourceTemplate": "docker", + "depNameTemplate": "{{service | split(\"/\") | last}}/{{depName}}", + "versioningTemplate": "docker", + "extractor": "file:extract-docker-images.js" + } + ], + + "branchNameStrict": true, + "branchPrefix": "renovate/", + "commitMessageAction": "Update", + "commitMessageTopic": "{{service | split(\"/\") | last}}/{{depName}}", + "prTitle": "Update {{service | split(\"/\") | last}}/{{depName}}: {{currentValue}} → {{newVersion}}" }