diff --git a/pyproject.toml b/pyproject.toml index 915a415..08ddb8b 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -8,7 +8,7 @@ description = "Git workflow automation to follow best practices when working wit dynamic = ["version"] requires-python = ">= 3.14" dependencies = [ - "requests", "typer", "rich", "questionary" + "requests", "typer", "rich", "questionary", "giturlparse" ] authors = [ {name = "Alan Facundo Biglieri", email = "abiglieri@renatre.org.ar"}, diff --git a/src/git_flow/__init__.py b/src/git_flow/__init__.py index 46e9993..9c5d36d 100644 --- a/src/git_flow/__init__.py +++ b/src/git_flow/__init__.py @@ -43,9 +43,10 @@ COMMIT_TYPE_INCREMENT = { "test": SEMVER_SKIP, } -SUPPORTED_REMOTE_APIS = [ - "bitbucket.org", - "github.com", +SUPPORTED_REMOTE_URL_SCHEMAS = [ + "ssh", + "http", + "https" ] REPOSITORY_TOKEN_FILENAME = ".repository-token" diff --git a/src/git_flow/remote/base.py b/src/git_flow/remote/base.py index ef42b82..4f8cecb 100644 --- a/src/git_flow/remote/base.py +++ b/src/git_flow/remote/base.py @@ -1,6 +1,8 @@ from abc import ABC, abstractmethod -from git_flow import SUPPORTED_REMOTE_APIS, GitFlowError +import giturlparse + +from git_flow import SUPPORTED_REMOTE_URL_SCHEMAS, GitFlowError from git_flow.git import Git @@ -11,25 +13,35 @@ class RemoteAPI(ABC): @staticmethod def parse(remote: str): + """Soportamos 3 tipos de URL: http, https y ssh. Si no se especifica el esquema, asumimos ssh: + Ejemplos: + - http://gitea/user/repo + - https://gitea.com/user/repo.git + - git@bitbucket.org:user/repo + - git.mygiteainstance.com:user/repo.git + - ssh://git@git.mygiteainstance.com:1234/user/repo.git + """ url = Git("remote", "get-url", remote).firstline() - if url.startswith("git@"): - at_pos = url.index("@") - colon_pos = url.index(":") - schema = "ssh://" - host = url[at_pos+1:colon_pos] - repository = url[colon_pos+1:] - elif url.startswith("http://") or url.startswith("https://"): - start_pos = url.index("://") + 3 - uri_start = url.index("/", start_pos) - schema = url[:start_pos] - host = url[start_pos:uri_start] - repository = url[uri_start+1:] - else: + parsed_url = giturlparse.parse(url) # Validamos que la URL sea válida + + if not parsed_url.valid: raise GitFlowError(f"La URL del remoto {remote} es inválida: {url}") - repository = repository.removesuffix(".git") - return [schema, host, repository] + if not parsed_url.protocol in SUPPORTED_REMOTE_URL_SCHEMAS: + raise GitFlowError( + f"La URL del remoto {remote} no tiene un esquema soportado: {url}" + ) + + # Cuando el host es seteado por pipeline puede ser "http://gitea/OWNER/REPO", en ese caso + # la libreria parsea OWNER="" y REPO="OWNER/REPO" + repository = ( + "/".join(filter(None, [parsed_url.owner, parsed_url.repo])) + .removeprefix("/") + .removesuffix(".git") + ) + + return [parsed_url.protocol + "://", parsed_url.host, repository] @abstractmethod def create_pull_request( @@ -38,7 +50,7 @@ class RemoteAPI(ABC): destination: str, title: str | None = None, description: str | None = None, - close_source_branch: bool = True + close_source_branch: bool = True, ) -> str: pass diff --git a/uv.lock b/uv.lock index 2335e7c..2128d82 100644 --- a/uv.lock +++ b/uv.lock @@ -70,6 +70,7 @@ wheels = [ name = "git-flow-envs" source = { editable = "." } dependencies = [ + { name = "giturlparse" }, { name = "questionary" }, { name = "requests" }, { name = "rich" }, @@ -78,12 +79,22 @@ dependencies = [ [package.metadata] requires-dist = [ + { name = "giturlparse" }, { name = "questionary" }, { name = "requests" }, { name = "rich" }, { name = "typer" }, ] +[[package]] +name = "giturlparse" +version = "0.15.0" +source = { registry = "https://pypi.org/simple" } +sdist = { url = "https://files.pythonhosted.org/packages/8c/8a/b70b84cc78f9059d627f09560c81a11e8f046570d220a24e169489cad6c9/giturlparse-0.15.0.tar.gz", hash = "sha256:9af3f1fd5c4a0cac94ddb283593635005646393ee0debbe330d1bdff8866bf2c", size = 16138, upload-time = "2026-06-16T07:28:56.021Z" } +wheels = [ + { url = "https://files.pythonhosted.org/packages/c7/96/147a2771ab655b9353781fb2f95c94eaf1d8576dccc991c1a61d0d355067/giturlparse-0.15.0-py2.py3-none-any.whl", hash = "sha256:76d2e6983b037356ab99b30683e533ac3db96409b68e2163a20fc3aff6446f10", size = 16683, upload-time = "2026-06-16T07:28:55.184Z" }, +] + [[package]] name = "idna" version = "3.11"