ããã«ã¡ã¯ãã¿ã€ããŒã®ããŒã¿ãšã³ãžãã¢ãªã³ã°éšDSã°ã«ãŒãã§MLOpsãæ
åœããŠããYukitomoã§ãã ååã®èšäºã§ã¯ãPython + uvã®ã¢ãã¬ãç°å¢ã«ãããŠãRenovateãçšããŠäŸåé¢ä¿æŽæ°ã®ãã«ãªã¯ãšã¹ããå¶åŸ¡ããããã®åºæ¬èšèšã玹ä»ããŸããã ãã®åŸãå®éã«éçšããŠã¿ããšãRenovateã®ç®¡ç察象ã¯Pythonã®äŸåããã±ãŒãžã ãã§ã¯åãŸããŸããã§ããã devcontainer.json ãCloud Runã®ãããã§ã¹ããªã©ãžå¯Ÿè±¡ãåºããã«ã€ããŠèšå®ãè€éã«ãªããMend.ioçµç±ã§åãããŠçµæãåŸ
ã€ã ãã§ã¯ãæå³éãã«åããªãèšå®ã®åå ã远ãã«ãããªããŸããã ããã§ä»åã¯ãååããã®æŽæ°ãšããŠã以äžã®4ç¹ã玹ä»ããŸãã Renovateèšå®ãããŒã«ã«ãCI/CDã§æ€èšŒã§ããããã«ããããš æ€èšŒæé ãAIãšãŒãžã§ã³ãçšã¹ãã«ãšããŠæŽåããããš devcontainer.json ãCloud Runã®ãããã§ã¹ããã¢ããªã±ãŒã·ã§ã³åäœã®ãã«ãªã¯ãšã¹ãã«ãŸãšããèšå® ã°ã«ãŒãã³ã°ãããæŽæ°ã§ã察象ãšãªããã¹ãŠã®ãã¡ã€ã«ã bumpVersions ã§æŽæ°ããèšå® ãã®èšäºã®æ³å®èªè
Renovateèšå®ãç¶ç¶çã«æ¹åã»ãããã°ãããæ¹ Mend.ioçµç±ã§RenovateãéçšããŠãããèšå®å€æŽã®æ€èšŒã«èª²é¡ãæããŠããæ¹ ãã©ã€ããŒããªGoogle Cloud Artifact Registryã䜿ãã¢ãã¬ãã§RenovateãéçšããŠããæ¹ AIãšãŒãžã§ã³ãã«Renovateèšå®ã®èª¿æ»ã»ä¿®æ£ã»dry runãä»»ãããæ¹ èŠçŽïŒTL;DRïŒ Mend.ioãžåæ ããåã«ãããŒã«ã«ãCI/CDã§Renovateã®config validationãšdry runãå®è¡ã§ããããã«ãã æ€èšŒæé ãAIãšãŒãžã§ã³ãçšã¹ãã«ã«ãŸãšããèª¿æ»æé ãšå®è¡ç°å¢ãæãã managerããšã«ç°ãªã packageFileDir ãæ£èŠåããã¢ããªã±ãŒã·ã§ã³åäœã§æŽæ°ããŸãšãã ã°ã«ãŒãã³ã°ããããã«ãªã¯ãšã¹ãã§ã¯ã upgrades ã«å«ãŸãããã£ã¬ã¯ããªãåæãããã¹ãŠã® pyproject.toml / uv.lock ã bumpVersions ã§æŽæ°ãã åæãšããã¢ãã¬ãæ§æ ä»åã®å
容ã説æãããããæ¬¡ã®ãããªç°¡ç¥åããã¢ãã¬ããæ³å®ããŸãã . âââ .renovaterc.json5 âââ group-x â âââ project-a â â âââ .devcontainer â â â âââ devcontainer.json â â âââ kustomize â â â âââ base â â â âââ overlays â â âââ pyproject.toml â â âââ uv.lock â âââ project-b â âââ pyproject.toml â âââ uv.lock âââ group-y âââ ... èšäºäžã§æ±ãèšå®ã®å
šäœåã¯æ¬¡ã®éãã§ãã説æãç°¡æœã«ãããããå®éã®èšå®ããé¢ä¿ããéšåã ããæãåºããŠããŸãã { extends : [ "config:recommended" ] , devcontainer : { managerFilePatterns : [ "/(?:^|/)\\.devcontainer/(?:[^/]+/)?devcontainer\\.json$/" , "/(?:^|/)\\.devcontainer\\.json$/" , ] , } , packageRules : [ { matchFileNames : [ "group-x/**" ] , groupName : "group-x" , groupSlug : "group-x" , additionalBranchPrefix : "{{packageFileDir}}/" , } , { matchFileNames : [ "**/devcontainer.json" ] , additionalBranchPrefix : "{{replace \"/\\.devcontainer\" \"\" packageFileDir}}/" , } , { matchFileNames : [ "**/kustomize/**" ] , additionalBranchPrefix : "{{{replace '/kustomize/.*' '' packageFileDir}}}/" , } , { matchUpdateTypes : [ "major" , "minor" , "patch" ] , bumpVersions : [ { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/pyproject\\.toml$/" , ] , matchStrings : [ "version\\s*=\\s*\"(?<version>[^\"]+)\"" ] , } , { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/uv\\.lock$/" , ] , matchStrings : [ "name = \"[^\"]+\"\\nversion = \"(?<version>[^\"]+)\"\\nsource = \\{ (?:editable|virtual) = \"\\.\" \\}" , ] , } , ] , } , ] , } AIãšãŒãžã§ã³ãçšã¹ãã«ã¯ç¬ç«ãããªããžããªã§ç®¡çããŠããããããã®ãã¡ã€ã«æ§æã¯åŸè¿°ã®2ç« ã§èª¬æããŸãã 1. Renovateèšå®ã¯ããŒã«ã«ãCI/CDã§æ€èšŒããŠãããããã€ãã äžè¬çãªãœãããŠã§ã¢éçºã§ã¯ã倿Žãã¡ã€ã³ãã©ã³ãã«å
¥ããåã«ããŒã«ã«ãCI/CDã§æ€èšŒããŸããRenovateã®èšå®ãåãããã«æ±ãããããããŒã«ã«ã§config validationãšdry runãå®è¡ããã¹ã¯ãªãããçšæããŸããããã®ã¹ã¯ãªããã¯ã2ç« ã§èª¬æããAIãšãŒãžã§ã³ãçšã¹ãã«ã«å«ããŠããŸãã å®éã®ã¹ã¯ãªããã¯ããå°ãè€éã§ãããGoogle Cloud Artifact RegistryãGitHubãžã®èªèšŒæ
å ±ãç°å¢å€æ°ãžèšå®ããäžã§ãåºå®ããŒãžã§ã³ã®Renovateãå®è¡ããã®ããã€ã³ãã§ãã RENOVATE_HOST_RULES="$(gcloud auth print-access-token | jq -Rc '[ {matchHost:"asia-northeast1-docker.pkg.dev",hostType:"docker",username:"oauth2accesstoken",password:.}, {matchHost:"asia-northeast1-python.pkg.dev",username:"oauth2accesstoken",password:.} ]')" \ GITHUB_COM_TOKEN="$(gh auth token)" \ RENOVATE_CONFIG_FILE=.renovaterc.json5 \ npx --yes --package "renovate@${RENOVATE_VERSION}" renovate-config-validator --strict # ãŸãã¯ãåæ§ã«ç°å¢å€æ°ãèšå®ããŠdry runãå®è¡ # TIER = extract | lookup | full npx --yes --package "renovate@${RENOVATE_VERSION}" renovate --platform=local --dry-run="$TIER" Renovateã®èšå®é
ç®ã¯å€å²ã«ããããŸããMend.ioçµç±ã§å®è¡ããŠãããšãäžèŠãšã©ãŒãªãçµäºããŠããŠããäžéšã®èšå®ãæå³éãã«é©çšãããŠããªãããšããããŸããã Mend.ioåŽã®ç°å¢å€æ°ãå€ããŠã¡ã€ã³ãã©ã³ã以å€ã§åäœããã詳现ãã°ã確èªããæ¹æ³ã§ããããã°ã§ããŸãããã ããGitãªããžããªã®ç®¡çè
æš©éãå¿
èŠã«ãªãå Žåãããã誰ã§ãç°¡åã«è©Šããããã§ã¯ãããŸããããŸããèšå®å€æŽããã«ãªã¯ãšã¹ãåããæ¿èªåŸã«ã¡ã€ã³ãã©ã³ããžåæ ããŠããå®è¡çµæã確èªããæ¹æ³ã§ã¯ããã£ãŒãããã¯ã«ãŒããé·ããªããŸãã ãã®ãããçŸåšã¯Renovateèšå®ããããŒã«ã«ã§æ€èšŒããŠãããããã€ãã察象ããšããŠæ±ãããã«ããŸããã 2. AIãšãŒãžã§ã³ãçšã¹ãã«ãšããŠæºåãã Renovateã®debug logã¯éåžžã«å€§ããããªããžããªã倧ãããªããšdry runã«ãæéãããããŸããäººãæ¯ååãåæã確èªããªãããã°ãèªãã®ã§ã¯ãªããAIãšãŒãžã§ã³ããäžå®ã®æé ã§èšå®ã調æ»ã»ä¿®æ£ã§ãããããæ€èšŒæé ãã¹ãã«ãšããŠæŽåããŸããã æ§æã¯ä»¥äžã§ãã plugins/renovate-config-tester/ âââ assets â âââ sample.gha.yml # ãµã³ãã«ã®GitHub Actions â âââ sample.renovaterc.json5 # Renovateã®ãµã³ãã«åæèšå® âââ scripts â âââ renovate-local.sh # 1ç« ã§èª¬æããRenovateå®è¡ã¹ã¯ãªãã âââ SKILL.md SKILL.md ã«ã¯ãäž»ã«æ¬¡ã®åæãšèª¿æ»æé ãèšèŒããŠããŸãã Renovateã®ããŒãžã§ã³ Renovateã¯å¿
ãåºå®ããŒãžã§ã³ã§å®è¡ããŸããRenovateã®ç®¡ç察象ãšãªããã¡ã€ã«ïŒäŸïŒGitHub Actionsã®workflowïŒã«ããŒãžã§ã³ãèšè¿°ãããã®æŽæ°ã¯Renovateèªèº«ã«ä»»ããŸããã¹ãã«ã¯ããã®ãã¡ã€ã«ããå®è¡ããããŒãžã§ã³ãååŸããŸãã # CIçšGitHub Actions jobs : validate-and-dry-run : runs-on : ubuntu-latest env : # renovate: datasource=npm depName=renovate RENOVATE_VERSION : 44.29.4 # Renovateèªèº«ãæŽæ°ããã¹ãã«ã¯ããããååŸãã steps : - name : Install Renovate run : npm install -g "renovate@${RENOVATE_VERSION}" - name : Renovate dry run run : renovate --platform=local --dry-run=lookup Node.jsã®å¿
èŠããŒãžã§ã³ Renovateã¯Node.js applicationã§ãããããRenovateã®ããŒãžã§ã³ã«å¯Ÿå¿ããNode.jsãå¿
èŠã§ãã æ¬èšäºã§æ±ã£ãŠããRenovate 44ç³»ã¯ãNode.js 24ã®ç°å¢ã§æ€èšŒããŸãããæå
ã§ã¯Node.js 26ç°å¢ã§æ£åžžã«å®è¡ã§ããªãã£ããããã¹ãã«ã«ã¯Renovateã察å¿ããNode.jsããŒãžã§ã³ã確èªããæ€èšŒæžã¿ã®runtimeã䜿ãããæèšããŠããŸãã gcloudãghã®èªèšŒæ
å ±ãå¿
èŠã§ããããš Private Artifact RegistryãGitHubã«ã¢ã¯ã»ã¹ããå¿
èŠãããå Žåã¯ãäºåã« gcloud ã gh ã®èªèšŒãæžãŸããå¿
èŠããããŸãã ãã ããAIãšãŒãžã§ã³ãã«tokenãçŽæ¥æ±ãããã®ã§ã¯ãªããã¹ã¯ãªãã( renovate-local.sh )ã®äžã§ RENOVATE_HOST_RULES ãGitHub tokenãçµã¿ç«ãŠãããã«ããŠããŸããAIãšãŒãžã§ã³ãã¯ã¹ã¯ãªãããå®è¡ããã ãã§ãcredentialã®çµã¿ç«ãŠæ¹ãæ¯å倿ããå¿
èŠã¯ãããŸããã å¿
èŠãªããŒã«é¡ Renovateã®ãã°ã¯JSONL圢åŒã§åºåãããããã調æ»ã«ã¯ jq ãªã©ãå¿
èŠã§ãããªããžããªãèšå®ã«ãã£ãŠã¯ yq ãå©çšããŸããããããåšèŸºããŒã«ãã¹ãã«ã®åæã«å«ããããšã§ãAIãšãŒãžã§ã³ãã調æ»ã§ããªãçç±ãåãåããããããŠããŸãã å®è¡ãã°ã¯äžæŠãã¡ã€ã«ã«èšé²ããŠããèªã 察象ãªããžããªã倧ããå Žåãdebug logããã®ãŸãŸèªã¿èŸŒãŸãããšLLMã®tokenãå€ãæ¶è²»ããŸããããã§ããã°ãäžæŠãã¡ã€ã«ãžä¿åãã察象ããã±ãŒãžãbranchã«é¢ä¿ããè¡ã ãã grep / jq ã§æœåºããŸãã "${CLAUDE_SKILL_DIR}/scripts/renovate-local.sh" > /tmp/renovate.log 2>&1 ã¹ãã«ãšããŠç¬ç«ãããããšã§ãèªåãæ
åœãããªããžããªä»¥å€ã«ãåãèª¿æ»æé ãå±éã§ããŸããRenovateèšå®ã®èª¿æ»æ¹æ³ã人ãžèª¬æãã代ããã«ãAIãšãŒãžã§ã³ããåãåæãšæé ã§èª¿æ»ã§ããç¶æ
ã«ããããšããä»åã®å€§ããªå€æŽã§ãã 3. devcontainer.jsonãCloud Runã®ãããã§ã¹ããã¢ããªã±ãŒã·ã§ã³åäœã®ãã«ãªã¯ãšã¹ãã«ãŸãšãã Renovateã¯åœåãPythonã®äŸåããã±ãŒãžæŽæ°ãäžå¿ã«å°å
¥ããŸããããã®åŸã devcontainer.json ãCloud Runã®ãããã§ã¹ãããåç
§ããã³ã³ããã€ã¡ãŒãžãªã©ã«ã察象ãåºããŠããŸãã åæãšããã¢ãã¬ãã§åçŽã«å¯Ÿè±¡managerãå¢ãããšãåãã¢ããªã±ãŒã·ã§ã³ã«é¢ããæŽæ°ã§ãã£ãŠãããã¡ã€ã«ã®å Žæã«ãã£ãŠå¥ã®ãã«ãªã¯ãšã¹ãã«ãªããŸãã pyproject.toml / uv.lock ã®æŽæ° .devcontainer/devcontainer.json ã®image/featureæŽæ° kustomizeé
äžã«ããCloud Runãããã§ã¹ãã®ã³ã³ããã€ã¡ãŒãžæŽæ° ãããã¯ãåãã¢ããªã±ãŒã·ã§ã³ã®å®è¡ã»éçºç°å¢ã«é¢ããæŽæ°ããšããŠãŸãšããŠç¢ºèªãããã±ãŒã¹ãå€ãããã«ãªã¯ãšã¹ãã现ããåããããšã¬ãã¥ãŒã®è² æ
ãå¢ããŸããããã§ãmanagerããšã«ç°ãªã packageFileDir ãæ£èŠåããã¢ããªã±ãŒã·ã§ã³åäœã®branch prefixãžæããŸãã A. ã¢ãã¬ãå
ã®devcontainer.jsonãæ€åºãã åã¢ããªã±ãŒã·ã§ã³é
äžã® .devcontainer/devcontainer.json ãRenovateã®å¯Ÿè±¡ãžå«ãããããdevcontainer managerã® managerFilePatterns ã远å ããŸãã devcontainer : { managerFilePatterns : [ "/(?:^|/)\\.devcontainer/(?:[^/]+/)?devcontainer\\.json$/" , "/(?:^|/)\\.devcontainer\\.json$/" , ] , } , B. packageFileDirãã¢ããªã±ãŒã·ã§ã³ã®ãã£ã¬ã¯ããªãžæ£èŠåãã ååã®èšäºã§ç޹ä»ããå
±éã«ãŒã«ã§ã¯ã packageFileDir ã additionalBranchPrefix ã«äœ¿ã£ãŠããŸããããããdevcontainer managerã® packageFileDir ã«ã¯ /.devcontainer ãå«ãŸããããããã®ãŸãŸã§ã¯ pyproject.toml / uv.lock ã®æŽæ°ãšå¥ã®branchã«ãªããŸãã ããã§ãdevcontainer managerããæœåºããdependencyã«ã€ããŠã¯ã packageFileDir æ«å°Ÿã® /.devcontainer ãåãé€ããŸãã // ååã®èšäºã§ç޹ä»ããå
±éã«ãŒã« { matchFileNames : [ "group-x/**" ] , groupName : "group-x" , groupSlug : "group-x" , additionalBranchPrefix : "{{packageFileDir}}/" , } , // devcontainer.jsonçšã®ã«ãŒã« { matchFileNames : [ "**/devcontainer.json" ] , additionalBranchPrefix : "{{replace \"/\\.devcontainer\" \"\" packageFileDir}}/" , } , ããã«ãããããšãã° group-x/project-a/.devcontainer 㯠group-x/project-a ãžæ£èŠåãããŸãã Cloud Runã®ãããã§ã¹ãã kustomize/base ã kustomize/overlays/env ã«åãããŠããå Žåããåãèãæ¹ã§ /kustomize/** ãåãé€ããŸãã { matchFileNames : [ "**/kustomize/**" ] , additionalBranchPrefix : "{{{replace '/kustomize/.*' '' packageFileDir}}}/" , } , ãã®èšå®ã«ãããPython dependencyãdevcontainerã®image/featureãCloud Runãããã§ã¹ãã®ã³ã³ããã€ã¡ãŒãžããã¢ããªã±ãŒã·ã§ã³åäœã®ãã«ãªã¯ãšã¹ããšããŠç¢ºèªãããããªããŸãã ãã ããè€æ°ã¢ããªã±ãŒã·ã§ã³ã®æŽæ°ãããã«1ã€ã®ãã«ãªã¯ãšã¹ããžãŸãšãããšãå¥ã®åé¡ãçºçããŸããã bumpVersions.filePatterns ã§ {{packageFileDir}} ã ããåç
§ãããšãã°ã«ãŒãå
ã«è€æ°ãã pyproject.toml ãš uv.lock ã®ããŒãžã§ã³ãæŽæ°ããããšããŠããäžéšããæŽæ°ãããªãã£ãã®ã§ãã4ç« ã§ã¯ãã®å¯ŸåŠæ¹æ³ã玹ä»ããŸãã 4. ã°ã«ãŒãã³ã°æã®bumpVersionsã§å¯Ÿè±¡ãã¡ã€ã«ãæŒããªãæŽæ°ãã ååèšäºã¯ bumpVersions ãtop-levelã«çœ®ãäŸã玹ä»ããŸãããçŸåšã¯pathãæ¡ä»¶ã«å ããŠããããã packageRules ã®äžã«ç§»ããŠããŸãã éåžžã§ããã°ã filePatterns 㯠{{packageFileDir}}/pyproject.toml ã®ããã«æžããããªããŸããããããè€æ°ã®ã¢ããªã±ãŒã·ã§ã³ã1ã€ã®ãã«ãªã¯ãšã¹ããžã°ã«ãŒãã³ã°ããå Žåããã®èšè¿°ã§ã¯ãããã1ã€ã® pyproject.toml / uv.lock ããæŽæ°ãããŸããã§ããã Renovateã®Discussion #35770ãåèã«ãã°ã«ãŒãå
ã® upgrades ãããã¹ãŠã® packageFileDir ãåãåºããæ£èŠè¡šçŸã®éžæè¢ãšããŠçµã¿ç«ãŠããã倿ŽããŸããã { matchUpdateTypes : [ "major" , "minor" , "patch" ] , bumpVersions : [ { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/pyproject\\.toml$/" , ] , matchStrings : [ "version\\s*=\\s*\"(?<version>[^\"]+)\"" ] , } , { bumpType : "patch" , filePatterns : [ "/^({{#each (distinct (lookupArray upgrades \"packageFileDir\"))}}{{{.}}}{{#unless @last}}|{{/unless}}{{/each}})\\/uv\\.lock$/" , ] , matchStrings : [ "name = \"[^\"]+\"\\nversion = \"(?<version>[^\"]+)\"\\nsource = \\{ (?:editable|virtual) = \"\\.\" \\}" , ] , } , ] , } , lookupArray ã§åupgradeã® packageFileDir ãåãåºãã distinct ã§éè€ãé€ããæ£èŠè¡šçŸã®éžæè¢ãšããŠé£çµããŠããã®ããã€ã³ãã§ãã èšå® ã°ã«ãŒãå
ã®å¯Ÿè±¡ æŽæ°çµæ {{packageFileDir}} ã®ã¿ãåç
§ è€æ°ãã£ã¬ã¯ã㪠ãããã1çµã ãæŽæ° upgrades ãããã£ã¬ã¯ããªãåæ è€æ°ãã£ã¬ã¯ã㪠察象ãšãªãå
šçµãæŽæ° ãã®èšè¿°ã¯Renovateã®template contextã«å«ãŸãã upgrades ãå©çšããŸããå°å
¥æã«ã¯ã䜿çšããRenovateããŒãžã§ã³ã§config validationãšdry runãå®è¡ããæ³å®ãããã¹ãŠã®ãã¡ã€ã«ãæŽæ°å¯Ÿè±¡ã«ãªã£ãŠããããšã確èªããŠãã ããã ãŸãšã ä»åãRenovateã®èšå®ãMend.ioãžåæ ããåã«æ€èšŒã§ããããã«ãããã®æé ãAIãšãŒãžã§ã³ãçšã¹ãã«ãšããŠæŽåããŸãããconfigã ãã§ãªããå®è¡ç°å¢ãcredentialã®æž¡ãæ¹ããã°ã®èª¿ã¹æ¹ãŸã§å«ããŠç®¡çããããšã§ãåãåæãšæé ã§ç¶ç¶çã«èšå®ãæ¹åã§ããŸãã ãŸãã packageFileDir ã®æ£èŠåã«ãã£ãп޿°ãã¢ããªã±ãŒã·ã§ã³åäœã«ãŸãšããè€æ°ã¢ããªã±ãŒã·ã§ã³ãã°ã«ãŒãã³ã°ããå Žåã¯ã bumpVersions ã®å¯Ÿè±¡ãã°ã«ãŒãå
šäœãžåºããŸãããã¢ãã¬ãã®Renovateèšå®ã¯ãæ€èšŒãšéçšãç¹°ãè¿ããªããè²ãŠãããšãéèŠã ãšæããŠããŸãã ãã®ãããªMLOpsåºç€ãéçºç°å¢ã®æ¹åã«ãäžç·ã«åãçµãã¡ã³ããŒãåéããŠããŸãã Weâre Hiring! çŸåšãã¿ã€ããŒã§ã¯ãããŒã¿ãµã€ãšã³ã¹ããšã³ãžãã¢ãªã³ã°ã®åéã§ãå
±ã«æé·ãã驿°ãæšãé²ããŠãããæ°ããªããŒã ã¡ã³ããŒãç©æ¥µçã«æ¢ããŠããŸãïŒ çŸåšåéäžã®ããžã·ã§ã³ã¯ ãã¡ã ã§ãã ãŸããæ°è»œãªé°å²æ°ã§ã® ã«ãžã¥ã¢ã«é¢è« ãéæè¡ã£ãŠããŸããã話ãèãããããšæãããæ¹ã¯ããã²ãæ°è»œã«ãšã³ããªãŒããŠãã ããã References ååèšäº: Renovateの設定 - Timee Product Team Blog Renovate configuration options Renovate package rules Feedback thread: bumpVersions · renovatebot/renovate · Discussion #35770