Compare commits

..

5 commits

Author SHA1 Message Date
57dd12de07 🚧 inital export with issues and wiki pages 2023-10-24 22:02:49 +11:00
7c4e1c6d5f 🚧 remove commented code 2023-10-24 13:13:28 +11:00
02f4919da2 🚧 change to use click instead of argparse 2023-10-24 13:10:59 +11:00
024f01c9d1 🚧 reorganise code 2023-10-24 11:25:26 +11:00
6343ee6db3 🚧 WIP: Begin exploring the GitLab API
- add PrettyTable for display

This commit is very much WIP. Just some inital playing
with the API.

Eventually this should lead to a tool that will export at a
minimum the wiki and issues for a project.

First though, I need to get a feel for how the API works.
2023-10-23 20:36:23 +11:00
6 changed files with 304 additions and 15 deletions

View file

@ -17,7 +17,7 @@ repos:
rev: 5.12.0 rev: 5.12.0
hooks: hooks:
- id: isort - id: isort
args: ["--profile", "black"] args: ["--profile", "black", "-l", "79"]
- repo: https://github.com/PyCQA/flake8 - repo: https://github.com/PyCQA/flake8
rev: 6.1.0 rev: 6.1.0
hooks: hooks:

View file

@ -17,7 +17,9 @@ classifiers = [
"Operating System :: OS Independent", "Operating System :: OS Independent",
] ]
dependencies = [ dependencies = [
"requests" "click",
"prettytable",
"requests",
] ]
[project.urls] [project.urls]
@ -25,7 +27,7 @@ dependencies = [
"Bug Tracker" = "https://git.evatt.ingenious.com.au/neillc/gitea-gitlab-exporter" "Bug Tracker" = "https://git.evatt.ingenious.com.au/neillc/gitea-gitlab-exporter"
[project.scripts] [project.scripts]
gitlab2gitea = "gitea_gitlab_exporter:exporter" gl2gt = "gitea_gitlab_exporter.cli:cli"
[project.optional-dependencies] [project.optional-dependencies]
dev = [ dev = [

View file

@ -1,18 +1,7 @@
""" """
A skeleton for a python project. A tool to copy a project from gitlab to gitea
Copyright 2023 Neill Cox Copyright 2023 Neill Cox
""" """
import logging
def hello_world():
logging.debug("hello_world called")
print("Hello World!")
if __name__ == "__main__":
hello_world()

View file

@ -0,0 +1,67 @@
import json
import logging
from .utils import get, gitlab_url
def get_user(args):
logging.debug("get_user called")
url = gitlab_url("/user")
result = get(url, args)
return result
def list_projects(args):
logging.debug("list_projects called")
user_id = get_user(args)["id"]
url = gitlab_url(
f"/users/{user_id}/projects?pagination=offset&per_page=500&"
"order_by=name&sort=asc"
)
result = get(url, args)
return result
def get_project_details(args):
user_id = get_user(args)["id"]
url = gitlab_url(
f"/users/{user_id}/projects?pagination=offset&per_page=500&"
"order_by=name&sort=asc"
)
response = get(url, args)
project = [
project
for project in response
if (project["name"] == args.project or project["id"] == args.project)
][0]
if not project:
raise KeyError(f"Project {args.project} not found")
print(json.dumps(project))
def get_issues(args):
project_id = args.project_id
url = gitlab_url(
f"/projects/{project_id}/issues?pagination=offset&per_page=500&"
)
response = get(url, args)
return response
def get_wiki(args):
url = gitlab_url(f"/projects/{args.project_id}/wikis?with_content=1")
response = get(url, args)
return response

View file

@ -0,0 +1,209 @@
import argparse
import json
import logging
import os
import sys
import click
from prettytable import PrettyTable
from requests.exceptions import HTTPError
from .api import (
get_issues,
get_project_details,
get_user,
get_wiki,
list_projects,
)
def parse_args():
"""Parse the command line arguments"""
parser = argparse.ArgumentParser()
parser.add_argument(
"-t",
"--gitlab-token",
help=(
"A private access token to access GitLab with. If not specified "
"will use $GL_TOKEN. Required."
),
)
parser.add_argument("--debug", action="store_true")
parser.add_argument("--log-file", default="gitlab2gitea.log")
parser.add_argument("-f", "--format", default="json")
subparsers = parser.add_subparsers()
lp_sp = subparsers.add_parser("list-projects")
lp_sp.set_defaults(func=list_projects)
lp_sp = subparsers.add_parser("get-user")
lp_sp.set_defaults(func=cli_get_user)
lp_sp = subparsers.add_parser("get-project-details")
lp_sp.add_argument("--project", required=True)
lp_sp.set_defaults(func=get_project_details)
args = parser.parse_args()
log_level = logging.INFO
if args.debug:
log_level = logging.DEBUG
logging.basicConfig(filename=args.log_file, level=log_level)
if args.gitlab_token is None:
args.gitlab_token = os.environ.get("GL_TOKEN")
if args.gitlab_token is None:
err_str = "/".join(
[ac for ac in parser._actions if ac.dest == "gitlab_token"][
0
].option_strings
)
parser.print_usage()
print(
f"{parser.prog}: error: the following arguments are "
f"required: {err_str}"
)
sys.exit(2)
if "func" in args:
args.func(args)
return args
def cli_get_user(args):
logging.debug("cli_get_user called")
user = get_user(args)
print(json.dumps(user))
class Context:
pass
@click.group()
@click.option("--format", default="json")
@click.option("--log-file", default="json")
@click.option(
"-t",
"--gitlab-token",
help=(
"A private access token to access GitLab with. If not specified "
"will use $GL_TOKEN. Required."
),
envvar="GL2GT_GL_TOKEN",
required=True,
)
@click.option("--debug", is_flag=True)
@click.option("--log-file", default="gitlab2gitea.log")
@click.pass_context
def cli(ctx, format, gitlab_token, debug, log_file):
ctx.ensure_object(Context)
ctx.obj.format = format
ctx.obj.gitlab_token = gitlab_token
ctx.obj.debug = debug
ctx.obj.log_file = log_file
log_level = logging.INFO
if debug:
log_level = logging.DEBUG
logging.basicConfig(filename=log_file, level=log_level)
@click.command()
@click.pass_context
def list_projects_click(ctx):
format = ctx.obj.format
try:
projects = list_projects(ctx.obj)
except HTTPError as err:
if err.response.status_code == 401:
print("Invalid gitlab credentials")
sys.exit(2)
else:
raise
if format == "table":
tbl = PrettyTable()
tbl.align = "l"
tbl.field_names = [
"ID",
"Name",
# "Description"
]
for row in projects:
tbl.add_row(
[
row["id"],
row["name"],
# row["description"][:50] if row["description"] else ""
]
)
print(tbl)
elif format == "json":
print(json.dumps(projects))
else:
print(projects)
cli.add_command(list_projects_click)
@click.command()
@click.pass_context
def click_get_user(ctx):
logging.debug("cli_get_user called")
user = get_user(ctx.obj)
print(json.dumps(user))
cli.add_command(click_get_user)
@click.command()
@click.option("--project", help="Project name or ID", required=True)
@click.pass_context
def export_project(ctx, project):
project_list = [
prj
for prj in list_projects(ctx.obj)
if (prj["id"] == project or prj["name"] == project)
]
if len(project_list) > 1:
print("Multiple projects found.")
sys.exit(2)
if len(project_list) < 1:
print("No matching projects")
sys.exit(2)
project_details = project_list[0]
ctx.obj.project_id = project_details["id"]
issues = get_issues(ctx.obj)
project_details["issues"] = issues
wiki_pages = get_wiki(ctx.obj)
project_details["wiki_pages"] = wiki_pages
print(json.dumps(project_details))
cli.add_command(export_project)

View file

@ -0,0 +1,22 @@
import logging
import requests
def gitlab_url(path):
return "https://gitlab.com/api/v4" + path
def get(url, args):
logging.debug("get called")
logging.debug("url: %s", url)
response = requests.get(url, headers={"PRIVATE-TOKEN": args.gitlab_token})
# if not response.ok:
response.raise_for_status()
logging.debug("response.status: %s", response.status_code)
logging.debug("body: %s", response.text)
return response.json()