| """Downloads all MRs from GitLab matching specified criteria.""" |
| |
| import argparse |
| import datetime |
| import json |
| import os |
| import requests |
| import sys |
| |
| EIGEN_PROJECT_ID = 15462818 # Taken from the gitlab project page. |
| |
| |
| def date(date_string: str): |
| """Convert a date YY-MM-DD string to a datetime object.""" |
| try: |
| return datetime.strptime(date_string, "%Y-%m-%d") |
| except ValueError: |
| msg = f"Not a valid date: '{date_string}'. Expected format is YYYY-MM-DD." |
| raise argparse.ArgumentTypeError(msg) |
| |
| |
| def _get_api_query( |
| gitlab_private_token: str, url: str, params: dict[str, str] | None = None |
| ): |
| next_page = "1" |
| if not params: |
| params = dict() |
| params["per_page"] = "100" |
| headers = {"PRIVATE-TOKEN": gitlab_private_token} |
| out = [] |
| while next_page: |
| params["page"] = next_page |
| try: |
| resp = requests.head(url=url, params=params, headers=headers) |
| if resp.status_code != 200: |
| print("Request failed: ", resp, file=sys.stderr) |
| break |
| |
| next_next_page = resp.headers["x-next-page"] |
| |
| resp = requests.get(url=url, params=params, headers=headers) |
| if resp.status_code != 200: |
| # Try again. |
| continue |
| |
| out.extend(resp.json()) |
| # Advance at the end, in case an exception occurs above so we can retry |
| next_page = next_next_page |
| except: |
| # Keep same next_page |
| continue |
| return out |
| |
| |
| def get_merge_requests( |
| gitlab_private_token: str, |
| author_username: str | None = None, |
| state: str | None = None, |
| created_before: datetime.datetime | None = None, |
| created_after: datetime.datetime | None = None, |
| updated_after: datetime.datetime | None = None, |
| updated_before: datetime.datetime | None = None, |
| related_issues: bool = False, |
| closes_issues: bool = False, |
| ): |
| """Return list of merge requests. |
| |
| Args: |
| gitlab_token: GitLab API token. |
| author_username: MR author username. |
| state: MR state (merged, opened, closed, locked). |
| created_after: datetime start of period. |
| created_before: datetime end of period. |
| updated_after: datetime start of period. |
| updated_before: datetime end of period. |
| |
| Returns: |
| List of merge requests. |
| """ |
| url = ( |
| "https://gitlab.com/api/v4/projects/" |
| + str(EIGEN_PROJECT_ID) |
| + "/merge_requests" |
| ) |
| params = dict() |
| if author_username: |
| params["author_username"] = author_username |
| |
| if state: |
| params["state"] = state |
| |
| if created_before: |
| params["created_before"] = created_before.isoformat() |
| |
| if created_after: |
| params["created_after"] = created_after.isoformat() |
| |
| if updated_before: |
| params["updated_before"] = updated_before.isoformat() |
| |
| if updated_after: |
| params["updated_after"] = updated_after.isoformat() |
| |
| params["order_by"] = "created_at" |
| params["sort"] = "asc" |
| |
| next_page = "1" |
| params["per_page"] = "100" |
| headers = {"PRIVATE-TOKEN": gitlab_private_token} |
| |
| mrs = _get_api_query(gitlab_private_token, url, params) |
| |
| if related_issues: |
| for mr in mrs: |
| mr["related_issues"] = _get_api_query( |
| gitlab_private_token, f"{url}/{mr['iid']}/related_issues" |
| ) |
| |
| if closes_issues: |
| for mr in mrs: |
| mr["closes_issues"] = _get_api_query( |
| gitlab_private_token, f"{url}/{mr['iid']}/closes_issues" |
| ) |
| |
| return mrs |
| |
| |
| def main(_): |
| parser = argparse.ArgumentParser( |
| description="A script to download all MRs from GitLab matching specified criteria." |
| ) |
| parser.add_argument( |
| "--gitlab_private_token", |
| type=str, |
| help="GitLab private API token. Defaults to the GITLAB_PRIVATE_TOKEN environment variable if set.", |
| ) |
| parser.add_argument("--author", type=str, help="The name of the author.") |
| parser.add_argument( |
| "--state", |
| type=str, |
| choices=["merged", "opened", "closed", "locked"], |
| help="The state of the MR.", |
| ) |
| parser.add_argument( |
| "--created_before", |
| type=date, |
| help="The created-before date in YYYY-MM-DD format.", |
| ) |
| parser.add_argument( |
| "--created_after", |
| type=date, |
| help="The created-after date in YYYY-MM-DD format.", |
| ) |
| parser.add_argument( |
| "--updated_before", |
| type=date, |
| help="The updated-before date in YYYY-MM-DD format.", |
| ) |
| parser.add_argument( |
| "--updated_after", |
| type=date, |
| help="The updated-after date in YYYY-MM-DD format.", |
| ) |
| parser.add_argument( |
| "--related_issues", action="store_true", help="Query for related issues." |
| ) |
| parser.add_argument( |
| "--closes_issues", |
| action="store_true", |
| help="Query for issues closed by the MR.", |
| ) |
| |
| args = parser.parse_args() |
| |
| if not args.gitlab_private_token: |
| args.gitlab_private_token = os.getenv("GITLAB_PRIVATE_TOKEN") |
| if not args.gitlab_private_token: |
| print("Could not determine GITLAB_PRIVATE_TOKEN.", file=sys.stderr) |
| parser.print_usage() |
| sys.exit(1) |
| |
| # Parse the arguments from the command line |
| mrs = get_merge_requests( |
| gitlab_private_token=args.gitlab_private_token, |
| author_username=args.author, |
| state=args.state, |
| created_before=args.created_before, |
| created_after=args.created_after, |
| updated_before=args.updated_before, |
| updated_after=args.updated_after, |
| related_issues=args.related_issues, |
| closes_issues=args.closes_issues, |
| ) |
| |
| mr_str = json.dumps(mrs, indent=2) |
| print(mr_str) |
| |
| |
| if __name__ == "__main__": |
| main(sys.argv) |