diff --git a/.gitignore b/.gitignore index 8f9b5db..6b45421 100644 --- a/.gitignore +++ b/.gitignore @@ -1,6 +1,39 @@ -*.pyc -.*.sw* -*.token +# pyenv +.python-version -# local virtual environment +# dotenv +.env + +# virtualenv .venv +venv/ +ENV/ +# IDE settings +.vscode/ + +# Byte-compiled / optimized / DLL files +__pycache__/ +*.py[cod] +*$py.class + +# Distribution / packaging +.Python +env/ +build/ +develop-eggs/ +dist/ +downloads/ +eggs/ +.eggs/ +lib/ +lib64/ +parts/ +sdist/ +var/ +wheels/ +*.egg-info/ +.installed.cfg +*.egg + +.*.sw* +*.token \ No newline at end of file diff --git a/gitea/apiobject.py b/gitea/apiobject.py index 9fd5174..77526ba 100644 --- a/gitea/apiobject.py +++ b/gitea/apiobject.py @@ -48,6 +48,45 @@ class Organization(ApiObject): ) self.dirty_fields = {} + def create_repo( + self, + repoName: str, + description: str = "", + private: bool = False, + autoInit=True, + gitignores: str = None, + license: str = None, + readme: str = "Default", + issue_labels: str = None, + default_branch="master", + ): + """Create an organization Repository + + Throws: + AlreadyExistsException: If the Repository exists already. + Exception: If something else went wrong. + """ + result = self.gitea.requests_post( + f"/orgs/{self.name}/repos", + data={ + "name": repoName, + "description": description, + "private": private, + "auto_init": autoInit, + "gitignores": gitignores, + "license": license, + "issue_labels": issue_labels, + "readme": readme, + "default_branch": default_branch, + }, + ) + if "id" in result: + self.gitea.logger.info("Successfully created Repository %s " % result["name"]) + else: + self.gitea.logger.error(result["message"]) + raise Exception("Repository not created... (gitea: %s)" % result["message"]) + return Repository.parse_response(self, result) + def get_repositories(self) -> List["Repository"]: results = self.gitea.requests_get_paginated( Organization.ORG_REPOS_REQUEST % self.username @@ -175,6 +214,45 @@ class User(ApiObject): self.gitea.requests_patch(User.ADMIN_EDIT_USER.format(**args), data=values) self.dirty_fields = {} + def create_repo( + self, + repoName: str, + description: str = "", + private: bool = False, + autoInit=True, + gitignores: str = None, + license: str = None, + readme: str = "Default", + issue_labels: str = None, + default_branch="master", + ): + """Create a user Repository + + Throws: + AlreadyExistsException: If the Repository exists already. + Exception: If something else went wrong. + """ + result = self.gitea.requests_post( + "/user/repos", + data={ + "name": repoName, + "description": description, + "private": private, + "auto_init": autoInit, + "gitignores": gitignores, + "license": license, + "issue_labels": issue_labels, + "readme": readme, + "default_branch": default_branch, + }, + ) + if "id" in result: + self.gitea.logger.info("Successfully created Repository %s " % result["name"]) + else: + self.gitea.logger.error(result["message"]) + raise Exception("Repository not created... (gitea: %s)" % result["message"]) + return Repository.parse_response(self, result) + def get_repositories(self) -> List["Repository"]: """ Get all Repositories owned by this User.""" url = f"/users/{self.username}/repos" diff --git a/gitea/gitea.py b/gitea/gitea.py index 80af2e6..5358a13 100644 --- a/gitea/gitea.py +++ b/gitea/gitea.py @@ -1,9 +1,10 @@ -import json import logging +import json from typing import List, Dict, Union -import requests from frozendict import frozendict +import requests +import urllib3 from .apiobject import User, Organization, Repository, Team from .exceptions import NotFoundException, ConflictException, AlreadyExistsException @@ -19,17 +20,47 @@ class Gitea: CREATE_ORG = """/admin/users/%s/orgs""" # CREATE_TEAM = """/orgs/%s/teams""" # - def __init__(self, gitea_url: str, token_text: str, log_level="INFO"): - """ Initializing Gitea-instance.""" + def __init__( + self, + gitea_url: str, + token_text=None, + auth=None, + verify=True, + log_level="INFO" + ): + """ Initializing Gitea-instance + + Args: + gitea_url (str): The Gitea instance URL. + token_text (str, None): The access token, by default None. + auth (tuple, None): The user credentials + `(username, password)`, by default None. + verify (bool): If True, allow insecure server connections + when using SSL. + log_level (str): The log level, by default `INFO`. + """ self.logger = logging.getLogger(__name__) self.logger.setLevel(log_level) self.headers = { - "Authorization": "token " + token_text, "Content-type": "application/json", } self.url = gitea_url self.requests = requests.Session() + # Manage authentification + if not token_text and not auth: + raise ValueError("Please provide auth or token_text, but not both") + if token_text: + self.headers["Authorization"] = "token " + token_text + if auth: + self.requests.auth = auth + + # Manage SSL certification verification + self.requests.verify = verify + if not verify: + urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) + + def __get_url(self, endpoint): url = self.url + "/api/v1" + endpoint self.logger.debug("Url: %s" % url) @@ -148,6 +179,7 @@ class Gitea: user_name: str, email: str, password: str, + full_name: str = None, login_name: str = None, change_pw=True, send_notify=True, @@ -160,9 +192,12 @@ class Gitea: """ if not login_name: login_name = user_name + if not full_name: + full_name = user_name request_data = { "source_id": source_id, "login_name": login_name, + "full_name": full_name, "username": user_name, "email": email, "password": password, @@ -197,12 +232,17 @@ class Gitea: license: str = None, readme: str = "Default", issue_labels: str = None, + default_branch="master", ): - """ Create a Repository. - Throws: - AlreadyExistsException, if Repository exists already. - Exception, if something else went wrong. + """ Create a Repository as the administrator + Throws: + AlreadyExistsException: If the Repository exists already. + Exception: If something else went wrong. + + Note: + Non-admin users can not use this method. Please use instead + `gitea.User.create_repo` or `gitea.Organization.create_repo`. """ # although this only says user in the api, this also works for # organizations @@ -218,6 +258,7 @@ class Gitea: "license": license, "issue_labels": issue_labels, "readme": readme, + "default_branch": default_branch, }, ) if "id" in result: @@ -267,6 +308,8 @@ class Gitea: name: str, description: str = "", permission: str = "read", + can_create_org_repo: bool = False, + includes_all_repositories: bool = False, units=( "repo.code", "repo.issues", @@ -291,6 +334,8 @@ class Gitea: "name": name, "description": description, "permission": permission, + "can_create_org_repo": can_create_org_repo, + "includes_all_repositories": includes_all_repositories, "units": units, }, ) diff --git a/tests/conftest.py b/tests/conftest.py new file mode 100644 index 0000000..607c173 --- /dev/null +++ b/tests/conftest.py @@ -0,0 +1,36 @@ +#!/usr/bin/env python +"""Fixtures for testing py-gitea + +Instructions +------------ +put a ".token" file into your directory containg only the token for gitea + +""" + +import os + +import pytest + +from gitea import Gitea + +@pytest.fixture +def instance(scope="module"): + try: + url = os.getenv("PY_GITEA_URL") + token = os.getenv("PY_GITEA_TOKEN") + auth = os.getenv("PY_GITEA_AUTH") + if not url: + raise ValueError("No Gitea URL was provided") + if token and auth: + raise ValueError("Please provide auth or token_text, but not both") + g = Gitea(url, token_text=token, auth=auth, verify=False) + print("Gitea Version: " + g.get_version()) + print("API-Token belongs to user: " + g.get_user().username) + return g + except: + assert ( + False + ), "Gitea could not load. \ + - Instance running at http://localhost:3000 \ + - Token at .token \ + ?"