|
| 1 | +--- |
| 2 | +# SPDX-License-Identifier: Apache-2.0 |
| 3 | +# SPDX-FileCopyrightText: 2025 The Linux Foundation |
| 4 | + |
| 5 | +name: github2gerrit-reusable-workflow-v2 |
| 6 | + |
| 7 | + |
| 8 | +on: |
| 9 | + workflow_call: |
| 10 | + inputs: |
| 11 | + SUBMIT_SINGLE_COMMITS: |
| 12 | + description: "Submit one commit at a time to the Gerrit repository" |
| 13 | + required: false |
| 14 | + default: false |
| 15 | + type: boolean |
| 16 | + USE_PR_AS_COMMIT: |
| 17 | + description: "Use PR body and title as commit message" |
| 18 | + required: false |
| 19 | + default: false |
| 20 | + type: boolean |
| 21 | + FETCH_DEPTH: |
| 22 | + description: "fetch-depth for the clone. (Default: 10)" |
| 23 | + required: false |
| 24 | + default: "10" |
| 25 | + type: string |
| 26 | + GERRIT_KNOWN_HOSTS: |
| 27 | + description: "known hosts" |
| 28 | + required: true |
| 29 | + type: string |
| 30 | + GERRIT_SERVER: |
| 31 | + description: "Gerrit hostname ex: git.opendaylight.org" |
| 32 | + required: false |
| 33 | + default: "" |
| 34 | + type: string |
| 35 | + GERRIT_SERVER_PORT: |
| 36 | + description: "Gerrit port. (Default: 29418)" |
| 37 | + required: false |
| 38 | + default: "29418" |
| 39 | + type: string |
| 40 | + GERRIT_PROJECT: |
| 41 | + description: "Gerrit project name. ex: releng/builder" |
| 42 | + required: false |
| 43 | + default: "" |
| 44 | + type: string |
| 45 | + GERRIT_SSH_USER_G2G: |
| 46 | + description: "Gerrit user-id for SSH" |
| 47 | + required: true |
| 48 | + type: string |
| 49 | + GERRIT_SSH_USER_G2G_EMAIL: |
| 50 | + description: "Email of the SSH user" |
| 51 | + required: true |
| 52 | + type: string |
| 53 | + ORGANIZATION: |
| 54 | + description: "Organization name, e.g. opendaylight" |
| 55 | + required: false |
| 56 | + type: string |
| 57 | + default: ${{ github.repository_owner }} |
| 58 | + REVIEWERS_EMAIL: |
| 59 | + description: "Committers email list (comma separated) to notify on code-reviews" |
| 60 | + required: false |
| 61 | + default: "" |
| 62 | + type: string |
| 63 | + secrets: |
| 64 | + GERRIT_SSH_PRIVKEY_G2G: |
| 65 | + description: "SSH Private key" |
| 66 | + required: true |
| 67 | + outputs: |
| 68 | + gerrit_change_url: |
| 69 | + description: "URL of the Gerrit change request" |
| 70 | + value: ${{ jobs.github2gerrit.outputs.change_url }} |
| 71 | + gerrit_change_number: |
| 72 | + description: "Gerrit change request number" |
| 73 | + value: ${{ jobs.github2gerrit.outputs.change_number }} |
| 74 | + |
| 75 | +concurrency: |
| 76 | + group: g2g-v2-${{ github.workflow }}-${{ github.run_id }} |
| 77 | + cancel-in-progress: true |
| 78 | + |
| 79 | +jobs: |
| 80 | + github2gerrit: |
| 81 | + name: "Submit PR to Gerrit" |
| 82 | + runs-on: ubuntu-latest |
| 83 | + timeout-minutes: 15 |
| 84 | + permissions: |
| 85 | + contents: read |
| 86 | + pull-requests: write |
| 87 | + |
| 88 | + outputs: |
| 89 | + change_url: ${{ env.GERRIT_CHANGE_REQUEST_URL }} |
| 90 | + change_number: ${{ env.GERRIT_CHANGE_REQUEST_NUM }} |
| 91 | + |
| 92 | + steps: |
| 93 | + - name: "Validate workflow inputs" |
| 94 | + if: ${{ inputs.USE_PR_AS_COMMIT && inputs.SUBMIT_SINGLE_COMMITS }} |
| 95 | + run: | |
| 96 | + echo "::error::USE_PR_AS_COMMIT and SUBMIT_SINGLE_COMMITS cannot be enabled simultaneously" |
| 97 | + exit 1 |
| 98 | +
|
| 99 | + - name: "Setup Python environment" |
| 100 | + uses: actions/setup-python@v5 |
| 101 | + with: |
| 102 | + python-version: "3.11" |
| 103 | + |
| 104 | + - name: "Install dependencies" |
| 105 | + run: | |
| 106 | + python -m pip install --upgrade pip |
| 107 | + pip install "git-review==2.3.1" jq |
| 108 | + echo "::notice::Installed git-review $(git review --version)" |
| 109 | + echo "::notice::Installed jq $(jq --version)" |
| 110 | +
|
| 111 | + - name: "Checkout repository" |
| 112 | + uses: actions/checkout@v4 |
| 113 | + with: |
| 114 | + fetch-depth: ${{ inputs.FETCH_DEPTH }} |
| 115 | + ref: ${{ github.event.pull_request.head.sha }} |
| 116 | + token: ${{ github.token }} |
| 117 | + |
| 118 | + - name: "Setup SSH key" |
| 119 | + uses: shimataro/ssh-key-action@d4fffb50872869abe2d9a9098a6d9c5aa7d16be4 |
| 120 | + with: |
| 121 | + key: ${{ secrets.GERRIT_SSH_PRIVKEY_G2G }} |
| 122 | + name: "id_rsa" |
| 123 | + known_hosts: ${{ inputs.GERRIT_KNOWN_HOSTS }} |
| 124 | + config: | |
| 125 | + Host ${{ env.GERRIT_SERVER }} |
| 126 | + User ${{ inputs.GERRIT_SSH_USER_G2G }} |
| 127 | + Port ${{ env.GERRIT_SERVER_PORT || '29418' }} |
| 128 | + PubkeyAcceptedKeyTypes +ssh-rsa |
| 129 | + IdentityFile ~/.ssh/id_rsa |
| 130 | +
|
| 131 | + - name: "Setup environment variables" |
| 132 | + run: ./scripts/setup-environment.sh |
| 133 | + env: |
| 134 | + GITHUB_EVENT_PULL_REQUEST_NUMBER: ${{ github.event.pull_request.number }} |
| 135 | + GITHUB_EVENT_ISSUE_NUMBER: ${{ github.event.issue.number }} |
| 136 | + GITHUB_BASE_REF: ${{ github.base_ref }} |
| 137 | + |
| 138 | + - name: "Parse git review configuration" |
| 139 | + run: ./scripts/parse-gitreview.sh |
| 140 | + env: |
| 141 | + GERRIT_PROJECT_INPUT: ${{ inputs.GERRIT_PROJECT }} |
| 142 | + GERRIT_SERVER_INPUT: ${{ inputs.GERRIT_SERVER }} |
| 143 | + GERRIT_SERVER_PORT_INPUT: ${{ inputs.GERRIT_SERVER_PORT }} |
| 144 | + GITHUB_REPOSITORY: ${{ github.repository }} |
| 145 | + |
| 146 | + - name: "Setup Issue ID lookup (if enabled)" |
| 147 | + if: vars.ISSUEID == 'true' |
| 148 | + run: | |
| 149 | + # Set key to use for JSON lookup |
| 150 | + ACTOR="${{ github.actor }}" |
| 151 | + ACTOR_ID="${{ github.actor_id }}" |
| 152 | + echo "::notice::Using GitHub actor as lookup key: $ACTOR [$ACTOR_ID]" |
| 153 | + echo "key=$ACTOR" >> "$GITHUB_ENV" |
| 154 | +
|
| 155 | + - name: "Get ticket from JSON lookup table" |
| 156 | + if: vars.ISSUEID == 'true' |
| 157 | + uses: lfit/releng-reusable-workflows/.github/actions/json-key-value-lookup-action@main |
| 158 | + with: |
| 159 | + json: ${{ vars.ISSUE_ID_LOOKUP_JSON }} |
| 160 | + key: ${{ env.key }} |
| 161 | + |
| 162 | + - name: "Set Issue ID in environment" |
| 163 | + if: vars.ISSUEID == 'true' |
| 164 | + run: | |
| 165 | + if [[ -n "${{ env.value }}" ]]; then |
| 166 | + echo "SET_ISSUE_ID=${{ env.value }}" >> "$GITHUB_ENV" |
| 167 | + echo "::notice::Issue ID set: ${{ env.value }}" |
| 168 | + fi |
| 169 | +
|
| 170 | + - name: "Configure git for Gerrit" |
| 171 | + run: ./scripts/setup-git.sh |
| 172 | + env: |
| 173 | + GERRIT_SSH_USER_G2G: ${{ inputs.GERRIT_SSH_USER_G2G }} |
| 174 | + GERRIT_SSH_USER_G2G_EMAIL: ${{ inputs.GERRIT_SSH_USER_G2G_EMAIL }} |
| 175 | + |
| 176 | + - name: "Handle PR updates and Change-ID reuse" |
| 177 | + if: >- |
| 178 | + github.event_name == 'pull_request_target' && |
| 179 | + (github.event.action == 'reopened' || github.event.action == 'synchronize') |
| 180 | + run: ./scripts/handle-pr-updates.sh |
| 181 | + env: |
| 182 | + ORGANIZATION: ${{ inputs.ORGANIZATION }} |
| 183 | + GITHUB_EVENT_ACTION: ${{ github.event.action }} |
| 184 | + GH_TOKEN: ${{ github.token }} |
| 185 | + |
| 186 | + - name: "Process PR commits" |
| 187 | + run: ./scripts/process-commits.sh |
| 188 | + env: |
| 189 | + SUBMIT_SINGLE_COMMITS: ${{ inputs.SUBMIT_SINGLE_COMMITS }} |
| 190 | + USE_PR_AS_COMMIT: ${{ inputs.USE_PR_AS_COMMIT }} |
| 191 | + GITHUB_EVENT_PULL_REQUEST_BASE_SHA: ${{ github.event.pull_request.base.sha }} |
| 192 | + GITHUB_SHA: ${{ github.sha }} |
| 193 | + GITHUB_EVENT_ACTION: ${{ github.event.action }} |
| 194 | + SET_ISSUE_ID: ${{ env.SET_ISSUE_ID }} |
| 195 | + ISSUEID_ENABLED: ${{ vars.ISSUEID }} |
| 196 | + GH_TOKEN: ${{ github.token }} |
| 197 | + |
| 198 | + - name: "Handle PR title and body as commit message" |
| 199 | + if: ${{ inputs.USE_PR_AS_COMMIT && github.event_name == 'pull_request_target' }} |
| 200 | + run: | |
| 201 | + echo "::notice::Using PR title and body as commit message" |
| 202 | +
|
| 203 | + # Get PR title and body |
| 204 | + gh pr view "$PR_NUMBER" --json title,body > pr_data.json |
| 205 | +
|
| 206 | + # Extract title and body |
| 207 | + jq -r '.title // ""' pr_data.json > pr_title.txt |
| 208 | + echo "" >> pr_title.txt # Blank line between title and body |
| 209 | + jq -r '.body // ""' pr_data.json > pr_body.txt |
| 210 | +
|
| 211 | + # Combine title and body |
| 212 | + cat pr_title.txt pr_body.txt > pr_commit.txt |
| 213 | +
|
| 214 | + # Get author info and signed-off-by lines |
| 215 | + if [[ -s author-info.txt && -s signed-off-by-final.txt ]]; then |
| 216 | + author=$(cat author-info.txt) |
| 217 | + echo "" >> pr_commit.txt # Blank line before trailers |
| 218 | + cat signed-off-by-final.txt >> pr_commit.txt |
| 219 | +
|
| 220 | + # Amend commit with PR content |
| 221 | + git commit --amend --author "$author" -F pr_commit.txt |
| 222 | + echo "::notice::Updated commit with PR title and body" |
| 223 | + fi |
| 224 | + env: |
| 225 | + GH_TOKEN: ${{ github.token }} |
| 226 | + |
| 227 | + - name: "Submit to Gerrit" |
| 228 | + run: ./scripts/submit-to-gerrit.sh |
| 229 | + env: |
| 230 | + REVIEWERS_EMAIL: ${{ inputs.REVIEWERS_EMAIL }} |
| 231 | + GERRIT_SSH_USER_G2G: ${{ inputs.GERRIT_SSH_USER_G2G }} |
| 232 | + GERRIT_SERVER: ${{ env.GERRIT_SERVER }} |
| 233 | + GITHUB_SERVER_URL: ${{ github.server_url }} |
| 234 | + GITHUB_REPOSITORY: ${{ github.repository }} |
| 235 | + GITHUB_RUN_ID: ${{ github.run_id }} |
| 236 | + |
| 237 | + - name: "Add GitHub reference to PR" |
| 238 | + if: env.GERRIT_CR_URL_CID != '' |
| 239 | + # yamllint disable rule:line-length |
| 240 | + uses: actions/github-script@v7 |
| 241 | + with: |
| 242 | + # yamllint disable rule:line-length |
| 243 | + script: | |
| 244 | + const changeUrls = `${{ env.GERRIT_CR_URL_CID }}`; |
| 245 | + const prNumber = `${{ env.PR_NUMBER }}`; |
| 246 | + const organization = `${{ inputs.ORGANIZATION }}`; |
| 247 | + const gerritServer = `${{ env.GERRIT_SERVER }}`; |
| 248 | +
|
| 249 | + const message = `The pull-request PR-${prNumber} has been submitted to Gerrit [${organization}](https://${gerritServer})! |
| 250 | +
|
| 251 | + **Gerrit Change(s):** |
| 252 | + ${changeUrls} |
| 253 | +
|
| 254 | + **Important:** This PR will be closed automatically. Re-opening will create a new change - use the Gerrit link above for updates.`; |
| 255 | +
|
| 256 | + await github.rest.issues.createComment({ |
| 257 | + issue_number: context.issue.number, |
| 258 | + owner: context.repo.owner, |
| 259 | + repo: context.repo.repo, |
| 260 | + body: message |
| 261 | + }); |
| 262 | + # yamllint enable rule:line-length |
| 263 | + |
| 264 | + - name: "Close pull request" |
| 265 | + if: github.event_name == 'pull_request_target' |
| 266 | + run: | |
| 267 | + echo "::notice::Closing PR #$PR_NUMBER after successful Gerrit submission" |
| 268 | +
|
| 269 | + if [[ -n "${SET_ISSUE_ID:-}" ]]; then |
| 270 | + # Keep branch for issue tracking workflows |
| 271 | + gh pr close --comment "Auto-closed: Submitted to Gerrit" "$PR_NUMBER" |
| 272 | + else |
| 273 | + # Delete branch for cleaner repo |
| 274 | + gh pr close --comment "Auto-closed: Submitted to Gerrit" --delete-branch "$PR_NUMBER" |
| 275 | + fi |
| 276 | + env: |
| 277 | + GH_TOKEN: ${{ github.token }} |
| 278 | + |
| 279 | + - name: "Workflow summary" |
| 280 | + if: always() |
| 281 | + run: | |
| 282 | + { |
| 283 | + echo "# GitHub2Gerrit Workflow Summary" |
| 284 | + echo "" |
| 285 | + echo "**PR Number:** $PR_NUMBER" |
| 286 | + echo "**Repository:** ${{ github.repository }}" |
| 287 | + echo "**Gerrit Server:** ${GERRIT_SERVER:-'Not set'}" |
| 288 | + submission_mode="${{ inputs.SUBMIT_SINGLE_COMMITS == true && 'Individual commits' || 'Squashed commit' }}" |
| 289 | + echo "**Submission Mode:** $submission_mode" |
| 290 | +
|
| 291 | + if [[ -n "${GERRIT_CHANGE_REQUEST_URL:-}" ]]; then |
| 292 | + echo "" |
| 293 | + echo "**✅ Success:** Changes submitted to Gerrit" |
| 294 | + echo "**Gerrit URL(s):** $GERRIT_CHANGE_REQUEST_URL" |
| 295 | + else |
| 296 | + echo "" |
| 297 | + echo "**❌ Status:** Workflow completed but no Gerrit URL available" |
| 298 | + fi |
| 299 | +
|
| 300 | + echo "" |
| 301 | + echo "**Timestamp:** $(date -u '+%Y-%m-%d %H:%M:%S UTC')" |
| 302 | + } >> "$GITHUB_STEP_SUMMARY" |
| 303 | +
|
| 304 | + - name: "Set job outputs" |
| 305 | + if: env.GERRIT_CHANGE_REQUEST_URL != '' |
| 306 | + run: | |
| 307 | + echo "change_url=${GERRIT_CHANGE_REQUEST_URL}" >> "$GITHUB_OUTPUT" |
| 308 | + echo "change_number=${GERRIT_CHANGE_REQUEST_NUM}" >> "$GITHUB_OUTPUT" |
0 commit comments