Skip to content

Commit 9ea91ad

Browse files
committed
feat: Refactor github2gerrit add tests framework
- Create modular V2 workflow with 6 dedicated shell scripts for better maintainability - Add comprehensive integration testing suite with mock Gerrit simulation - Implement regression testing framework comparing V1 vs V2 workflows - Add error scenario testing and performance benchmarking - Create local testing support with act runner integration - Maintain 100% backward compatibility with original workflow - Include security validation and input sanitization tests - Add workflow outputs for change URLs and numbers Scripts created: - setup-environment.sh: Environment variable configuration - parse-gitreview.sh: .gitreview parsing with fallbacks - setup-git.sh: Git configuration for Gerrit - process-commits.sh: Commit processing (squash/individual) - submit-to-gerrit.sh: Gerrit submission and tracking - handle-pr-updates.sh: Change-ID reuse for PR updates Testing framework includes: - Mock Gerrit testing with matrix scenarios - V1 vs V2 regression validation - Error handling verification - Performance comparison metrics - Local testing with run-tests.sh script Signed-off-by: Anil Belur <abelur@linuxfoundation.org>
1 parent 3df3528 commit 9ea91ad

15 files changed

+1731
-90
lines changed
Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,70 @@
1+
---
2+
# SPDX-License-Identifier: Apache-2.0
3+
# SPDX-FileCopyrightText: 2024 The Linux Foundation
4+
5+
# Example usage of the new modular github2gerrit-v2 workflow
6+
name: Example GitHub2Gerrit V2 Usage
7+
8+
# yamllint disable-line rule:truthy
9+
on:
10+
pull_request_target:
11+
types: [opened, reopened, synchronize]
12+
branches: [main, master]
13+
workflow_dispatch:
14+
inputs:
15+
pr_number:
16+
description: "PR number to process"
17+
required: true
18+
type: number
19+
20+
concurrency:
21+
group: example-g2g-v2-${{ github.ref }}
22+
cancel-in-progress: true
23+
24+
jobs:
25+
submit-to-gerrit:
26+
name: "Submit PR to Gerrit (V2)"
27+
if: false # Disabled by default - remove this line to enable
28+
permissions:
29+
contents: read
30+
pull-requests: write
31+
uses: ./.github/workflows/github2gerrit-v2.yaml
32+
with:
33+
# Submission options
34+
SUBMIT_SINGLE_COMMITS: false # Default: squash commits
35+
USE_PR_AS_COMMIT: false # Default: use original commit messages
36+
FETCH_DEPTH: "0" # Fetch full history
37+
38+
# Gerrit configuration (required)
39+
GERRIT_KNOWN_HOSTS: ${{ vars.GERRIT_KNOWN_HOSTS }}
40+
GERRIT_SSH_USER_G2G: ${{ vars.GERRIT_SSH_USER_G2G }}
41+
GERRIT_SSH_USER_G2G_EMAIL: ${{ vars.GERRIT_SSH_USER_G2G_EMAIL }}
42+
43+
# Optional Gerrit overrides (use when .gitreview is missing)
44+
# GERRIT_SERVER: "review.opendev.org"
45+
# GERRIT_SERVER_PORT: "29418"
46+
# GERRIT_PROJECT: "openstack/nova"
47+
48+
# GitHub organization
49+
ORGANIZATION: ${{ vars.ORGANIZATION }}
50+
51+
# Optional: Reviewers to notify
52+
REVIEWERS_EMAIL: ${{ vars.REVIEWERS_EMAIL }}
53+
secrets:
54+
GERRIT_SSH_PRIVKEY_G2G: ${{ secrets.GERRIT_SSH_PRIVKEY_G2G }}
55+
56+
# Example: Use the outputs from the workflow
57+
follow-up-actions:
58+
name: "Follow-up Actions"
59+
needs: submit-to-gerrit
60+
if: needs.submit-to-gerrit.outputs.gerrit_change_url != ''
61+
runs-on: ubuntu-latest
62+
steps:
63+
- name: "Process Gerrit submission results"
64+
run: |
65+
echo "::notice::Gerrit change created successfully!"
66+
echo "::notice::Change URL: ${{ needs.submit-to-gerrit.outputs.gerrit_change_url }}"
67+
echo "::notice::Change Number: ${{ needs.submit-to-gerrit.outputs.gerrit_change_number }}"
68+
69+
# Example: Send notification, update issue, etc.
70+
# slack-notify, email, database update, etc.
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
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"

.github/workflows/github2gerrit.yaml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
name: github2gerrit-reusable-workflow
66

7+
# yamllint disable-line rule:truthy
78
on:
89
workflow_call:
910
inputs:

0 commit comments

Comments
 (0)