|
| 1 | +# -------------------------------------------------------------------------------------- |
| 2 | +# Renovate Bot Job Template |
| 3 | +# -------------------------------------------------------------------------------------- |
| 4 | +# This Azure DevOps pipeline job template runs Renovate (https://docs.renovatebot.com/) |
| 5 | +# to automatically update dependencies in a GitHub repository. |
| 6 | +# |
| 7 | +# Renovate scans the repository for dependency files and creates pull requests to update |
| 8 | +# outdated dependencies based on the configuration specified in the renovateConfigPath |
| 9 | +# parameter. |
| 10 | +# |
| 11 | +# Usage: |
| 12 | +# For each product repo wanting to make use of Renovate, this template is called from |
| 13 | +# an internal Azure DevOps pipeline, typically with a schedule trigger, to check for |
| 14 | +# and propose dependency updates. |
| 15 | +# |
| 16 | +# For more info, see https://github.com/dotnet/arcade/blob/main/Documentation/Renovate.md |
| 17 | +# -------------------------------------------------------------------------------------- |
| 18 | + |
| 19 | +parameters: |
| 20 | + |
| 21 | +# Path to the Renovate configuration file within the repository. |
| 22 | +- name: renovateConfigPath |
| 23 | + type: string |
| 24 | + default: 'eng/renovate.json' |
| 25 | + |
| 26 | +# GitHub repository to run Renovate against, in the format 'owner/repo'. |
| 27 | +# This could technically be any repo but convention is to target the same |
| 28 | +# repo that contains the calling pipeline. The Renovate config file would |
| 29 | +# be co-located with the pipeline's repo and, in most cases, the config |
| 30 | +# file is specific to the repo being targeted. |
| 31 | +- name: gitHubRepo |
| 32 | + type: string |
| 33 | + |
| 34 | +# List of base branches to target for Renovate PRs. |
| 35 | +# NOTE: The Renovate configuration file is always read from the branch where the |
| 36 | +# pipeline is run, NOT from the target branches specified here. If you need different |
| 37 | +# configurations for different branches, run the pipeline from each branch separately. |
| 38 | +- name: baseBranches |
| 39 | + type: object |
| 40 | + default: |
| 41 | + - main |
| 42 | + |
| 43 | +# When true, Renovate will run in dry run mode, which previews changes without creating PRs. |
| 44 | +# See the 'Run Renovate' step log output for details of what would have been changed. |
| 45 | +- name: dryRun |
| 46 | + type: boolean |
| 47 | + default: false |
| 48 | + |
| 49 | +# By default, Renovate will not recreate a PR for a given dependency/version pair that was |
| 50 | +# previously closed. This allows opting in to always recreating PRs even if they were |
| 51 | +# previously closed. |
| 52 | +- name: forceRecreatePR |
| 53 | + type: boolean |
| 54 | + default: false |
| 55 | + |
| 56 | +# Name of the arcade repository resource in the pipeline. |
| 57 | +# This allows repos which haven't been onboarded to Arcade to still use this |
| 58 | +# template by checking out the repo as a resource with a custom name and pointing |
| 59 | +# this parameter to it. |
| 60 | +- name: arcadeRepoResource |
| 61 | + type: string |
| 62 | + default: self |
| 63 | + |
| 64 | +# Directory name for the self repo under $(Build.SourcesDirectory) in multi-checkout. |
| 65 | +# In multi-checkout (when arcadeRepoResource != 'self'), Azure DevOps checks out the |
| 66 | +# self repo to $(Build.SourcesDirectory)/<repoName>. Set this to match the auto-generated |
| 67 | +# directory name. Using the auto-generated name is necessary rather than explicitly |
| 68 | +# defining a checkout path because container jobs expect repos to live under the agent's |
| 69 | +# workspace ($(Pipeline.Workspace)). On some self-hosted setups the host path |
| 70 | +# (e.g., /mnt/vss/_work) differs from the container path (e.g., /__w), and a custom checkout |
| 71 | +# path can fail validation. Using the default checkout location keeps the paths consistent |
| 72 | +# and avoids this issue. |
| 73 | +- name: selfRepoName |
| 74 | + type: string |
| 75 | + default: '' |
| 76 | +- name: arcadeRepoName |
| 77 | + type: string |
| 78 | + default: '' |
| 79 | + |
| 80 | +# Pool configuration for the job. |
| 81 | +- name: pool |
| 82 | + type: object |
| 83 | + default: |
| 84 | + name: NetCore1ESPool-Internal |
| 85 | + image: build.azurelinux.3.amd64 |
| 86 | + os: linux |
| 87 | + |
| 88 | +jobs: |
| 89 | +- job: Renovate |
| 90 | + displayName: Run Renovate |
| 91 | + container: RenovateContainer |
| 92 | + variables: |
| 93 | + - group: dotnet-renovate-bot |
| 94 | + # The Renovate version is automatically updated by https://github.com/dotnet/arcade/blob/main/azure-pipelines-renovate.yml. |
| 95 | + # Changing the variable name here would require updating the name in https://github.com/dotnet/arcade/blob/main/eng/renovate.json as well. |
| 96 | + - name: renovateVersion |
| 97 | + value: '42' |
| 98 | + readonly: true |
| 99 | + - name: renovateLogFilePath |
| 100 | + value: '$(Build.ArtifactStagingDirectory)/renovate.json' |
| 101 | + readonly: true |
| 102 | + - name: dryRunArg |
| 103 | + readonly: true |
| 104 | + ${{ if eq(parameters.dryRun, true) }}: |
| 105 | + value: 'full' |
| 106 | + ${{ else }}: |
| 107 | + value: '' |
| 108 | + - name: recreateWhenArg |
| 109 | + readonly: true |
| 110 | + ${{ if eq(parameters.forceRecreatePR, true) }}: |
| 111 | + value: 'always' |
| 112 | + ${{ else }}: |
| 113 | + value: '' |
| 114 | + # In multi-checkout (without custom paths), Azure DevOps places each repo under |
| 115 | + # $(Build.SourcesDirectory)/<repoName>. selfRepoName must be provided in that case. |
| 116 | + - name: selfRepoPath |
| 117 | + readonly: true |
| 118 | + ${{ if eq(parameters.arcadeRepoResource, 'self') }}: |
| 119 | + value: '$(Build.SourcesDirectory)' |
| 120 | + ${{ else }}: |
| 121 | + value: '$(Build.SourcesDirectory)/${{ parameters.selfRepoName }}' |
| 122 | + - name: arcadeRepoPath |
| 123 | + readonly: true |
| 124 | + ${{ if eq(parameters.arcadeRepoResource, 'self') }}: |
| 125 | + value: '$(Build.SourcesDirectory)' |
| 126 | + ${{ else }}: |
| 127 | + value: '$(Build.SourcesDirectory)/${{ parameters.arcadeRepoName }}' |
| 128 | + pool: ${{ parameters.pool }} |
| 129 | + |
| 130 | + templateContext: |
| 131 | + outputParentDirectory: $(Build.ArtifactStagingDirectory) |
| 132 | + outputs: |
| 133 | + - output: pipelineArtifact |
| 134 | + displayName: Publish Renovate Log |
| 135 | + condition: succeededOrFailed() |
| 136 | + targetPath: $(Build.ArtifactStagingDirectory) |
| 137 | + artifactName: $(Agent.JobName)_Logs_Attempt$(System.JobAttempt) |
| 138 | + sbomEnabled: false |
| 139 | + |
| 140 | + steps: |
| 141 | + - checkout: self |
| 142 | + fetchDepth: 1 |
| 143 | + |
| 144 | + - ${{ if ne(parameters.arcadeRepoResource, 'self') }}: |
| 145 | + - checkout: ${{ parameters.arcadeRepoResource }} |
| 146 | + fetchDepth: 1 |
| 147 | + |
| 148 | + - script: | |
| 149 | + renovate-config-validator $(selfRepoPath)/${{parameters.renovateConfigPath}} 2>&1 | tee /tmp/renovate-config-validator.out |
| 150 | + validatorExit=${PIPESTATUS[0]} |
| 151 | + if grep -q '^ WARN:' /tmp/renovate-config-validator.out; then |
| 152 | + echo "##vso[task.logissue type=warning]Renovate config validator produced warnings." |
| 153 | + echo "##vso[task.complete result=SucceededWithIssues]" |
| 154 | + fi |
| 155 | + exit $validatorExit |
| 156 | + displayName: Validate Renovate config |
| 157 | + env: |
| 158 | + LOG_LEVEL: info |
| 159 | + LOG_FILE_LEVEL: debug |
| 160 | + LOG_FILE: $(Build.ArtifactStagingDirectory)/renovate-config-validator.json |
| 161 | + |
| 162 | + - script: | |
| 163 | + . $(arcadeRepoPath)/eng/common/renovate.env |
| 164 | + renovate 2>&1 | tee /tmp/renovate.out |
| 165 | + renovateExit=${PIPESTATUS[0]} |
| 166 | + if grep -q '^ WARN:' /tmp/renovate.out; then |
| 167 | + echo "##vso[task.logissue type=warning]Renovate produced warnings." |
| 168 | + echo "##vso[task.complete result=SucceededWithIssues]" |
| 169 | + fi |
| 170 | + exit $renovateExit |
| 171 | + displayName: Run Renovate |
| 172 | + env: |
| 173 | + RENOVATE_FORK_TOKEN: $(BotAccount-dotnet-renovate-bot-PAT) |
| 174 | + RENOVATE_TOKEN: $(BotAccount-dotnet-renovate-bot-PAT) |
| 175 | + RENOVATE_REPOSITORIES: ${{parameters.gitHubRepo}} |
| 176 | + RENOVATE_BASE_BRANCHES: ${{ convertToJson(parameters.baseBranches) }} |
| 177 | + RENOVATE_DRY_RUN: $(dryRunArg) |
| 178 | + RENOVATE_RECREATE_WHEN: $(recreateWhenArg) |
| 179 | + LOG_LEVEL: info |
| 180 | + LOG_FILE_LEVEL: debug |
| 181 | + LOG_FILE: $(renovateLogFilePath) |
| 182 | + RENOVATE_CONFIG_FILE: $(selfRepoPath)/${{parameters.renovateConfigPath}} |
| 183 | + |
| 184 | + - script: | |
| 185 | + echo "PRs created by Renovate:" |
| 186 | + if [ -s "$(renovateLogFilePath)" ]; then |
| 187 | + if ! jq -r 'select(.msg == "PR created" and .pr != null) | "https://github.com/\(.repository)/pull/\(.pr)"' "$(renovateLogFilePath)" | sort -u; then |
| 188 | + echo "##vso[task.logissue type=warning]Failed to parse Renovate log file with jq." |
| 189 | + echo "##vso[task.complete result=SucceededWithIssues]" |
| 190 | + fi |
| 191 | + else |
| 192 | + echo "##vso[task.logissue type=warning]No Renovate log file found or file is empty." |
| 193 | + echo "##vso[task.complete result=SucceededWithIssues]" |
| 194 | + fi |
| 195 | + displayName: List created PRs |
| 196 | + condition: and(succeededOrFailed(), eq('${{ parameters.dryRun }}', false)) |
0 commit comments