Commit 898feabc authored by Miguel Angel Reina Ortega's avatar Miguel Angel Reina Ortega
Browse files

Get raw diff in private/internal repos

parent f50d7d11
Loading
Loading
Loading
Loading
Loading
+78 −4
Original line number Diff line number Diff line
@@ -6,8 +6,10 @@
#	(c) 2023 by Andreas Kraft, Miguel Angel Reina Ortega
#	License: BSD 3-Clause License. See the LICENSE file for further details.
#
from typing import Tuple
from typing import Tuple, Optional
import argparse, os, re, sys
import subprocess, tempfile, shutil
from urllib.parse import urlparse, urlunparse, quote as urlquote
from rich import print
from rich.progress import Progress, TextColumn, TimeElapsedColumn
import logging
@@ -36,6 +38,19 @@ def fetch_json(url : str, expected_content_type : str = None, headers = None) ->
    r = fetch(url, expected_content_type, headers)
    return r.json()

def add_basic_auth_to_url(url: str, username: str, password: Optional[str]) -> str:
    if not password:
        return url
    parsed = urlparse(url)
    if '@' in parsed.netloc:
        return url  # already has creds
    user_enc = urlquote(username, safe='')
    pass_enc = urlquote(password, safe='')
    netloc = f"{user_enc}:{pass_enc}@{parsed.netloc}"
    parts = list(parsed)
    parts[1] = netloc
    return urlunparse(parts)

def readMDFile(progress:Progress, document:str) -> list[str]:
    """	Read the markdown file and return a list of lines.
    """
@@ -144,10 +159,69 @@ class MR:
        self.source_branch = self.raw_mr_details['source_branch']
        self.title = self.raw_mr_details['title']
        self.description = self.raw_mr_details['description']
        self.raw_diff = fetch_text(f'{self.web_url}/-/merge_requests/{self.mr_id}.diff', expected_content_type='text/plain', headers = headers)
        self.patch_set = PatchSet.from_string(self.raw_diff)
        self.headers = headers
        
        # Minimal fetch of just the two branch tips, then compute diff using explicit refs
        # Avoid triple-dot syntax to better handle branches with special characters
        # Fall back to web .diff on failure
        try:
            # Build optional authentication headers for Git over HTTPS
            extra:list[str] = []
            if isinstance(self.headers, dict):
                if 'PRIVATE-TOKEN' in self.headers and self.headers['PRIVATE-TOKEN']:
                    extra += ['-c', f"http.extraheader=PRIVATE-TOKEN: {self.headers['PRIVATE-TOKEN']}"]
                if 'Authorization' in self.headers and self.headers['Authorization']:
                    # e.g., 'Bearer <token>'
                    extra += ['-c', f"http.extraheader={self.headers['Authorization']}"]
            
            set_url_cmd = [
                'git', 'remote', 'set-url', 'origin',add_basic_auth_to_url(self.web_url, 'gitlab-ci-token', self.headers['PRIVATE-TOKEN'])
            ]
            logging.debug(f"Running: {' '.join(str(x) for x in set_url_cmd)}")
            set_url_proc = subprocess.run(set_url_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False, text=True)
            logging.debug(f"set_url returncode={set_url_proc.returncode}")
            if set_url_proc.stderr:
                logging.debug(f"set_url stderr: {set_url_proc.stderr.strip()}")

            fetch_cmd = [
                'git', '-c', 'http.sslVerify=false', *extra, 'fetch', '--no-tags', '--depth', '100', 'origin',
                f'+refs/heads/{self.target_branch}:refs/remotes/origin/{self.target_branch}',
                f'+refs/heads/{self.source_branch}:refs/remotes/origin/{self.source_branch}'
            ]
            logging.debug(f"Running: {' '.join(str(x) for x in fetch_cmd)}")
            fetch_proc = subprocess.run(fetch_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=False, text=True)
            logging.debug(f"fetch returncode={fetch_proc.returncode}")
            if fetch_proc.stderr:
                logging.debug(f"fetch stderr: {fetch_proc.stderr.strip()}")

            # Diff from target branch to source branch to mimic MR diff semantics
            diff_cmd = [
                'git', 'diff', '--patch', '--no-color',
                f'refs/remotes/origin/{self.target_branch}...refs/remotes/origin/{self.source_branch}'
            ]
            logging.debug(f"Running: {' '.join(str(x) for x in diff_cmd)}")
            diff_proc = subprocess.run(diff_cmd, stdout=subprocess.PIPE, stderr=subprocess.PIPE, check=True, text=True)
            if diff_proc.stderr:
                logging.debug(f"diff stderr: {diff_proc.stderr.strip()}")
            self.raw_diff = diff_proc.stdout
            if not self.raw_diff:
                raise RuntimeError('Empty diff produced by git')
        except Exception as e:
            logging.warning(f"git minimal fetch+diff failed ({e}); falling back to web .diff fetch")
            try:
                self.raw_diff = fetch_text(
                    f'{self.web_url}/-/merge_requests/{self.mr_id}.diff',
                    expected_content_type='text/plain', headers = headers
                )
                if not self.raw_diff or not self.raw_diff.strip():
                    raise ValueError('Downloaded web .diff is empty')
            except Exception as web_err:
                raise ValueError(
                    f"Unable to obtain diff: git failed with '{e}', and web .diff fetch failed with '{web_err}'. "
                    f"Check network access, token permissions, and MR visibility."
                )
        self.patch_set = PatchSet.from_string(self.raw_diff)
        
    def api_url(self, route : str = "") -> str:
        return f"{self.root}/api/v4/projects/{self.project_id}/{route}"
    def retrieve_text(self, branch: str, filename: str) -> str:
+1 −1
Original line number Diff line number Diff line
@@ -3,7 +3,7 @@ FROM python:3.9-slim-bullseye
ADD /generateCR/ /generateCR/
RUN chmod +x generateCR/checking_conflicts.sh
RUN apt-get update -y && \
#    apt-get install -y libcairo2 && \
    apt-get install -y git && \
    rm -rf /var/lib/apt/lists/* &&\
    pip install -e generateCR/ &&\
	pip install -r generateCR/requirements.txt