Skip to content

Commit 4cdbd32

Browse files
Add defaults and CLI args for GitLab CI environment variables
- Add default for CI_API_V4_URL (constructed from server host) - Add default for CI_SERVER_HOST (gitlab.com) - Add default for CI_PROJECT_PATH (swiss-armed-forces/cyber-command/cea/loom) - Add --project-id CLI argument for CI_PROJECT_ID override - Fix wait_for_mr_commit_sync() to properly access head commit SHA via diffs - Add proper type hints for Project class and current_sha variable Makes script more flexible for local development and testing while fixing the AttributeError with mr.sha access. 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
1 parent 2cc50bb commit 4cdbd32

File tree

1 file changed

+54
-17
lines changed

1 file changed

+54
-17
lines changed

cicd/test_git_file_changed.py

Lines changed: 54 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111

1212
import git
1313
import gitlab
14-
from gitlab.v4.objects import ProjectMergeRequest
14+
from gitlab.v4.objects import ProjectMergeRequest, Project
1515

1616

1717
# ----------------------------
@@ -86,6 +86,12 @@ def parse_args(argv: list[str] | None = None) -> argparse.Namespace:
8686
default=None,
8787
help="Project access token for creating MR pipelines (default: PROJECT_ACCESS_TOKEN env var).",
8888
)
89+
parser.add_argument(
90+
"--project-id",
91+
type=int,
92+
default=None,
93+
help="GitLab project ID (default: CI_PROJECT_ID env var).",
94+
)
8995
parser.add_argument(
9096
"--skip-ci",
9197
dest="add_skip_ci",
@@ -145,11 +151,14 @@ def build_context(ns: argparse.Namespace) -> CiContext:
145151

146152
changed_out_dir = Path(ns.changed_out_dir).resolve()
147153

154+
server_host = os.getenv("CI_SERVER_HOST", "gitlab.com")
155+
api_url = os.getenv("CI_API_V4_URL") or f"https://{server_host}/api/v4"
156+
148157
return CiContext(
149-
api_url=_req_env("CI_API_V4_URL"),
150-
server_host=_req_env("CI_SERVER_HOST"),
151-
project_id=int(_req_env("CI_PROJECT_ID")),
152-
project_path=_req_env("CI_PROJECT_PATH"),
158+
api_url=api_url,
159+
server_host=server_host,
160+
project_id=ns.project_id or int(_req_env("CI_PROJECT_ID")),
161+
project_path=os.getenv("CI_PROJECT_PATH", "swiss-armed-forces/cyber-command/cea/loom"),
153162
job_token=os.getenv("CI_JOB_TOKEN"),
154163
pipeline_id=os.getenv("CI_PIPELINE_ID"),
155164
pipeline_source=os.getenv("CI_PIPELINE_SOURCE"),
@@ -349,12 +358,19 @@ def count_consecutive_ci_commits(
349358
# ----------------------------
350359

351360

352-
def wait_for_mr_commit_sync(mr: ProjectMergeRequest, expected_commit_sha: str, max_retries: int = 5, base_delay: float = 2.0) -> bool:
361+
def wait_for_mr_commit_sync(
362+
project: Project,
363+
mr_iid: str,
364+
expected_commit_sha: str,
365+
max_retries: int = 5,
366+
base_delay: float = 2.0,
367+
) -> bool:
353368
"""
354369
Wait for GitLab MR to sync with the latest commit using exponential backoff.
355370
356371
Args:
357-
mr: GitLab merge request instance
372+
project: GitLab project instance
373+
mr_iid: Merge request IID
358374
expected_commit_sha: The commit SHA we expect the MR to have
359375
max_retries: Maximum number of retry attempts (default: 5)
360376
base_delay: Base delay in seconds for exponential backoff (default: 2.0)
@@ -363,21 +379,36 @@ def wait_for_mr_commit_sync(mr: ProjectMergeRequest, expected_commit_sha: str, m
363379
True if MR synced successfully, False if timed out
364380
"""
365381
for attempt in range(max_retries + 1):
366-
# Get current MR commit SHA
367-
current_sha = mr.sha # Head commit SHA of the MR
382+
# Always fetch fresh MR data from API
383+
mr = project.mergerequests.get(mr_iid, lazy=True)
384+
diffs = mr.diffs.list()
385+
current_sha = None
386+
if diffs:
387+
# Get the latest diff (most recent)
388+
latest_diff = diffs[0]
389+
current_sha: str | None = latest_diff.head_commit_sha
368390

369391
if current_sha == expected_commit_sha:
370392
if attempt > 0:
371393
logging.info("MR commit sync successful after %d attempt(s)", attempt)
372394
return True
373395

374396
if attempt < max_retries:
375-
delay = base_delay * (2 ** attempt) # Exponential backoff
376-
logging.info("MR has commit %s, waiting for %s. Retrying in %.1fs (attempt %d/%d)",
377-
current_sha[:8], expected_commit_sha[:8], delay, attempt + 1, max_retries)
397+
delay = base_delay * (2**attempt) # Exponential backoff
398+
logging.info(
399+
"MR has commit %s, waiting for %s. Retrying in %.1fs (attempt %d/%d)",
400+
current_sha[:8] if current_sha else "unknown",
401+
expected_commit_sha[:8],
402+
delay,
403+
attempt + 1,
404+
max_retries,
405+
)
378406
time.sleep(delay)
379407

380-
logging.error("MR commit sync timeout after %d attempts. MR may have stale commit.", max_retries)
408+
logging.error(
409+
"MR commit sync timeout after %d attempts. MR may have stale commit.",
410+
max_retries,
411+
)
381412
return False
382413

383414

@@ -396,7 +427,9 @@ def trigger_pipeline(ctx: CiContext) -> None:
396427
return
397428

398429
if not ctx.mr_iid:
399-
logging.warning("CI_MERGE_REQUEST_IID not available; skipping pipeline trigger.")
430+
logging.warning(
431+
"CI_MERGE_REQUEST_IID not available; skipping pipeline trigger."
432+
)
400433
return
401434

402435
if ctx.dry_run:
@@ -416,8 +449,10 @@ def trigger_pipeline(ctx: CiContext) -> None:
416449

417450
# Wait for GitLab MR to sync with the latest commit
418451
logging.info("Waiting for MR !%s to sync with latest commit...", ctx.mr_iid)
419-
if not wait_for_mr_commit_sync(mr, latest_commit_sha):
420-
logging.error("Failed to sync MR with latest commit. Not triggering pipeline to avoid running on stale commit.")
452+
if not wait_for_mr_commit_sync(project, ctx.mr_iid, latest_commit_sha):
453+
logging.error(
454+
"Failed to sync MR with latest commit. Not triggering pipeline to avoid running on stale commit."
455+
)
421456
return
422457

423458
# Create merge request pipeline using the MR pipeline API
@@ -446,7 +481,9 @@ def commit_and_push(repo: git.Repo, ctx: CiContext) -> None:
446481
if ctx.dry_run:
447482
logging.info("[DRY RUN] Would commit with message:\n%s", msg.strip())
448483
logging.info("[DRY RUN] Would push HEAD:%s", ctx.source_branch)
449-
logging.info("[DRY RUN] Would trigger new MR pipeline if project access token provided")
484+
logging.info(
485+
"[DRY RUN] Would trigger new MR pipeline if project access token provided"
486+
)
450487
return
451488

452489
if not has_staged_changes(repo):

0 commit comments

Comments
 (0)