Skip to content

Commit 37195c4

Browse files
owtaylorclaude
andauthored
ci: rewrite GitHub PR mirror script in Python (#398)
The GitLab CI runner doesn't have permissions to install packages at runtime, so the microdnf-based approach in the ubi10-minimal image fails. Switch to a Python base image that ships git, and rewrite the bash/curl/jq script as a Python stdlib script to eliminate all runtime package installs. Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent ef50b0b commit 37195c4

File tree

3 files changed

+132
-91
lines changed

3 files changed

+132
-91
lines changed

.gitlab-ci.yml

Lines changed: 2 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,12 +22,10 @@ stages:
2222

2323
mirror-github-pr:
2424
stage: mirror
25-
image: registry.access.redhat.com/ubi10-minimal
25+
image: images.paas.redhat.com/repository/it-cloud-ocp-proxy-lib/python:3.14
2626
tags:
2727
- rhel-lightspeed
28-
before_script:
29-
- microdnf install -y jq git-core
3028
script:
31-
- bash scripts/mirror_github_pr.sh
29+
- python3 scripts/mirror_github_pr.py
3230
rules:
3331
- if: $GITHUB_PR_NUMBER && $GITHUB_COMMIT_SHA

scripts/mirror_github_pr.py

Lines changed: 130 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,130 @@
1+
#!/usr/bin/env python3
2+
"""
3+
Mirror a GitHub PR to GitLab as a merge request.
4+
5+
This script is designed to run in GitLab CI to create MRs from GitHub PRs
6+
for internal testing. It fetches the PR metadata from GitHub, pushes the
7+
commit to a branch in GitLab, and creates a merge request using push options.
8+
9+
Required environment variables:
10+
GITHUB_PR_NUMBER - The GitHub PR number to mirror
11+
GITHUB_COMMIT_SHA - The commit SHA to fetch
12+
GITLAB_TOKEN - GitLab access token with 'write_repository' scope
13+
14+
Optional environment variables:
15+
GITHUB_REPO - GitHub repository (default: rhel-lightspeed/linux-mcp-server)
16+
GITLAB_PROJECT - GitLab project path (default: rhel-lightspeed/mcp/linux-mcp-server)
17+
GITLAB_HOST - GitLab hostname (default: gitlab.cee.redhat.com)
18+
"""
19+
20+
import json
21+
import os
22+
import subprocess
23+
import sys
24+
import urllib.error
25+
import urllib.request
26+
27+
28+
def run_git(*args, cwd=None):
29+
"""Run a git command, raising on failure."""
30+
subprocess.run(["git", *args], check=True, cwd=cwd)
31+
32+
33+
def branch_exists(branch_name, cwd):
34+
"""Check if a branch exists on the GitLab remote."""
35+
result = subprocess.run(
36+
["git", "ls-remote", "--exit-code", "origin", f"refs/heads/{branch_name}"],
37+
capture_output=True,
38+
cwd=cwd,
39+
)
40+
return result.returncode == 0
41+
42+
43+
def fetch_pr_info(github_repo, pr_number):
44+
"""Fetch PR metadata from the GitHub API."""
45+
url = f"https://api.github.com/repos/{github_repo}/pulls/{pr_number}"
46+
try:
47+
with urllib.request.urlopen(url) as response:
48+
return json.loads(response.read().decode())
49+
except urllib.error.HTTPError as e:
50+
sys.exit(f"ERROR: Could not fetch PR #{pr_number} from GitHub (HTTP {e.code})")
51+
except urllib.error.URLError as e:
52+
sys.exit(f"ERROR: Could not connect to GitHub API: {e.reason}")
53+
54+
55+
def main():
56+
github_repo = os.environ.get("GITHUB_REPO", "rhel-lightspeed/linux-mcp-server")
57+
gitlab_project = os.environ.get("GITLAB_PROJECT", "rhel-lightspeed/mcp/linux-mcp-server")
58+
gitlab_host = os.environ.get("GITLAB_HOST", "gitlab.cee.redhat.com")
59+
pr_number = os.environ.get("GITHUB_PR_NUMBER", "")
60+
commit_sha = os.environ.get("GITHUB_COMMIT_SHA", "")
61+
gitlab_token = os.environ.get("GITLAB_TOKEN", "")
62+
63+
if not pr_number or not commit_sha:
64+
sys.exit("ERROR: GITHUB_PR_NUMBER and GITHUB_COMMIT_SHA are required")
65+
66+
if not gitlab_token:
67+
sys.exit("ERROR: GITLAB_TOKEN is required")
68+
69+
branch_name = f"github-pr-{pr_number}"
70+
gitlab_remote = f"https://oauth2:{gitlab_token}@{gitlab_host}/{gitlab_project}.git"
71+
72+
# Fetch PR information from GitHub
73+
print(f"Fetching PR #{pr_number} from GitHub...")
74+
pr_info = fetch_pr_info(github_repo, pr_number)
75+
76+
pr_title = pr_info.get("title")
77+
pr_url = pr_info.get("html_url")
78+
79+
if not pr_title:
80+
sys.exit(f"ERROR: PR #{pr_number} not found or has no title")
81+
82+
print(f"PR Title: {pr_title}")
83+
print(f"PR URL: {pr_url}")
84+
85+
# Clone from GitLab (has shared history), then fetch PR commit from GitHub
86+
print("Cloning from GitLab...")
87+
run_git("clone", gitlab_remote, "repo")
88+
89+
print(f"Fetching commit {commit_sha[:7]} from GitHub...")
90+
run_git("remote", "add", "github", f"https://github.com/{github_repo}.git", cwd="repo")
91+
run_git("fetch", "github", commit_sha, cwd="repo")
92+
run_git("checkout", "-b", branch_name, commit_sha, cwd="repo")
93+
94+
# Check if branch already exists in GitLab
95+
print(f"Checking if branch {branch_name} exists in GitLab...")
96+
if branch_exists(branch_name, cwd="repo"):
97+
# Branch exists - just push to update the MR
98+
print("Branch exists, updating...")
99+
run_git("push", "-f", "origin", branch_name, cwd="repo")
100+
print(f"Updated branch {branch_name}")
101+
else:
102+
# Branch doesn't exist - create MR with push options
103+
print("Creating new branch and MR...")
104+
mr_description = (
105+
f"This MR mirrors GitHub PR #{pr_number} for internal testing."
106+
f"\\n\\n- GitHub PR: {pr_url}"
107+
f"\\n- Commit: {commit_sha}"
108+
)
109+
110+
run_git(
111+
"push",
112+
"origin",
113+
branch_name,
114+
"-o",
115+
"merge_request.create",
116+
"-o",
117+
"merge_request.target=main",
118+
"-o",
119+
f"merge_request.title={pr_title}",
120+
"-o",
121+
f"merge_request.description={mr_description}",
122+
"-o",
123+
"merge_request.remove_source_branch",
124+
cwd="repo",
125+
)
126+
print(f"Created MR for branch {branch_name}")
127+
128+
129+
if __name__ == "__main__":
130+
main()

scripts/mirror_github_pr.sh

Lines changed: 0 additions & 87 deletions
This file was deleted.

0 commit comments

Comments
 (0)