Compare commits

...
12 Commits
9 changed files with 154 additions and 36 deletions
+2
View File
@@ -2,3 +2,5 @@
remote = origin remote = origin
branches = main branches = main
initialized = true initialized = true
version = 2
remote-type = gitea
+35
View File
@@ -0,0 +1,35 @@
name: Publish to PyPI
on:
push:
branches:
- main
jobs:
Publish-to-PyPI:
runs-on: ubuntu-latest
steps:
- name: Check out repository code
uses: actions/checkout@v4
with:
fetch-depth: 10
- name: Show git log
run: |
git log --oneline --graph -10
- name: "Set up Python"
uses: actions/setup-python@v5
with:
python-version-file: ".python-version"
- name: Install uv
uses: astral-sh/setup-uv@v8.3.2
- name: Bump version
run: |
uv run git-flow tag --token="${{ secrets.GITEA_TOKEN}}"
- name: Build and publish
run: |
uv build
uv publish --token="${{ secrets.PYPI_TOKEN }}"
+2 -1
View File
@@ -47,7 +47,8 @@ El comando solicitará 2 opciones:
automáticamente. Por el momento, se soportan los siguientes remotos: automáticamente. Por el momento, se soportan los siguientes remotos:
- [x] Bitbucket - [x] Bitbucket
- [ ] Github: planeado - [x] Github
- [x] Gitea
La configuración del remoto requiere tener un Access Token para poder crear los PRs automáticamente. La configuración del remoto requiere tener un Access Token para poder crear los PRs automáticamente.
El mismo se puede crear desde el repositorio al que se quiere dar acceso: *"Repository settings" > El mismo se puede crear desde el repositorio al que se quiere dar acceso: *"Repository settings" >
+2
View File
@@ -51,3 +51,5 @@ SUPPORTED_REMOTE_APIS = [
REPOSITORY_TOKEN_FILENAME = ".repository-token" REPOSITORY_TOKEN_FILENAME = ".repository-token"
FLOWCONFIG_FILENAME = ".flowconfig" FLOWCONFIG_FILENAME = ".flowconfig"
FLOWCONFIG_VERSION = 2
+17 -3
View File
@@ -2,12 +2,14 @@ from git_flow import (
BRANCH_TYPES, BRANCH_TYPES,
REPOSITORY_TOKEN_FILENAME, REPOSITORY_TOKEN_FILENAME,
FLOWCONFIG_FILENAME, FLOWCONFIG_FILENAME,
FLOWCONFIG_VERSION,
GitFlowError, GitFlowError,
) )
from git_flow.git import Git from git_flow.git import Git
from git_flow.remote.base import RemoteAPI from git_flow.remote.base import RemoteAPI
from git_flow.remote.bitbucket import BitbucketRemoteAPI from git_flow.remote.bitbucket import BitbucketRemoteAPI
from git_flow.remote.github import GithubRemoteAPI from git_flow.remote.github import GithubRemoteAPI
from git_flow.remote.gitea import GiteaRemoteAPI
from git_flow.io import * from git_flow.io import *
from os.path import isfile from os.path import isfile
@@ -32,6 +34,14 @@ def ensure_initialized():
f"El valor de `flow.initialized` ({initialized}) es inválido." f"El valor de `flow.initialized` ({initialized}) es inválido."
) )
version = int(flowconfig.get("flow.version", "1"))
if version != FLOWCONFIG_VERSION:
raise GitFlowError(
f"La versión del flowconfig ({version}) no es compatible con esta versión de git-flow ({FLOWCONFIG_VERSION}). "
f"Debe reinicializar el repositorio."
)
def ensure_right_branch(): def ensure_right_branch():
branch = Git.get_current_branch() branch = Git.get_current_branch()
@@ -104,14 +114,18 @@ def get_remote_api(token: str) -> RemoteAPI:
if "flow.remote" not in flowconfig: if "flow.remote" not in flowconfig:
raise GitFlowError("El repositorio no tiene configurado un remoto.") raise GitFlowError("El repositorio no tiene configurado un remoto.")
remote_type = flowconfig.get("flow.remote-type", "").lower()
[host, repository] = RemoteAPI.parse(flowconfig["flow.remote"]) [host, repository] = RemoteAPI.parse(flowconfig["flow.remote"])
if host == "bitbucket.org": if remote_type == "gitea":
return GiteaRemoteAPI("https://" + host, repository, token)
elif remote_type == "bitbucket":
return BitbucketRemoteAPI(repository, token) return BitbucketRemoteAPI(repository, token)
elif host == "github.com": elif remote_type == "github":
return GithubRemoteAPI(repository, token) return GithubRemoteAPI(repository, token)
else: else:
raise GitFlowError("El host del repositorio remoto es inválido") raise GitFlowError("El tipo del remoto es inválido")
def is_valid_ticket(ticket: str) -> bool: def is_valid_ticket(ticket: str) -> bool:
"""Check if the specified string is a valid ticket number (ABC-123)""" """Check if the specified string is a valid ticket number (ABC-123)"""
+9 -4
View File
@@ -1,6 +1,6 @@
import typer import typer
from git_flow import FLOWCONFIG_FILENAME, GitFlowError from git_flow import FLOWCONFIG_FILENAME, FLOWCONFIG_VERSION, GitFlowError
from git_flow.git import Git from git_flow.git import Git
from git_flow.command.base import * from git_flow.command.base import *
@@ -15,15 +15,17 @@ def init():
_ensure_not_already_initialized() _ensure_not_already_initialized()
branches = _setup_flow_branches() branches = _setup_flow_branches()
remote = _setup_flow_remote() (remote, remote_type) = _setup_flow_remote()
flowconfig = { flowconfig = {
"flow.version": str(FLOWCONFIG_VERSION),
"flow.initialized": "true", "flow.initialized": "true",
"flow.branches": ",".join(branches), "flow.branches": ",".join(branches),
} }
if remote: if remote and remote_type:
flowconfig["flow.remote"] = remote flowconfig["flow.remote"] = remote
flowconfig["flow.remote-type"] = remote_type
Git.set_config(flowconfig, FLOWCONFIG_FILENAME) Git.set_config(flowconfig, FLOWCONFIG_FILENAME)
Git("add", FLOWCONFIG_FILENAME).exec() Git("add", FLOWCONFIG_FILENAME).exec()
@@ -74,6 +76,7 @@ def _setup_flow_remote():
) )
remote = None remote = None
remote_type = None
if confirm("¿Configurar repositorio remoto?"): if confirm("¿Configurar repositorio remoto?"):
remotes = Git("remote").lines(print="Listando remotos disponibles") remotes = Git("remote").lines(print="Listando remotos disponibles")
@@ -93,7 +96,9 @@ def _setup_flow_remote():
info("Tiene más de un remoto, seleccione el que va a utilizar.") info("Tiene más de un remoto, seleccione el que va a utilizar.")
remote = choice("Remoto", remotes) remote = choice("Remoto", remotes)
return remote remote_type = choice("Tipo de remoto:", ["bitbucket", "github", "gitea"])
return (remote, remote_type)
def _ensure_all_flow_branches_exist(branches: list[str], remote: str | None): def _ensure_all_flow_branches_exist(branches: list[str], remote: str | None):
+4 -24
View File
@@ -12,43 +12,23 @@ class RemoteAPI(ABC):
@staticmethod @staticmethod
def parse(remote: str): def parse(remote: str):
url = Git("remote", "get-url", remote).firstline() url = Git("remote", "get-url", remote).firstline()
invalid_url_error = GitFlowError(f"La URL del remoto {remote} es inválida: {url}")
url_type = None
if url.startswith("git@"): if url.startswith("git@"):
url_type = "ssh"
elif url.startswith("http://") or url.startswith("https://"):
url_type = "https"
else:
raise invalid_url_error
if url_type == "ssh":
# git@host:user/repo[.git]
at_pos = url.index("@") at_pos = url.index("@")
colon_pos = url.index(":") colon_pos = url.index(":")
host = url[at_pos+1:colon_pos] host = url[at_pos+1:colon_pos]
repository = url[colon_pos+1:] repository = url[colon_pos+1:]
else: elif url.startswith("http://") or url.startswith("https://"):
# https://host/user/repo[.git]
host_start = url.index("://") + 3 host_start = url.index("://") + 3
uri_start = url.index("/", host_start) uri_start = url.index("/", host_start)
host = url[host_start:uri_start] host = url[host_start:uri_start]
repository = url[uri_start+1:] repository = url[uri_start+1:]
else:
raise GitFlowError(f"La URL del remoto {remote} es inválida: {url}")
# Esto permite configurar hosts "imaginarios" para permitir el uso de multiples claves ssh repository = repository.removesuffix(".git")
if host.endswith(".bitbucket.org"):
host = "bitbucket.org"
elif host.endswith(".github.com"):
host = "github.com"
if host not in SUPPORTED_REMOTE_APIS:
raise GitFlowError("El host del repositorio remoto es inválido")
repository.removesuffix(".git")
return [host, repository] return [host, repository]
@abstractmethod @abstractmethod
def create_pull_request( def create_pull_request(
self, self,
+55
View File
@@ -0,0 +1,55 @@
import requests
from git_flow import GitFlowError
from git_flow.remote.base import RemoteAPI
class GiteaRemoteAPI(RemoteAPI):
def __init__(self, base_url: str, repository: str, token: str) -> None:
super().__init__(repository, token)
self.base_url = base_url
def create_pull_request(
self,
source: str,
destination: str,
title: str | None = None,
description: str | None = None,
close_source_branch: bool = True
) -> str:
response = requests.post(
self.get_endpoint("/pulls"),
headers=self.get_headers(),
json={
"title": title,
"body": description,
"head": source,
"base": destination,
},
)
if response.ok:
return "PR creado exitosamente: " + response.json()["html_url"]
else:
raise GitFlowError("Ocurrió un error al crear el PR: " + response.text)
def create_tag(self, tag: str, commit: str) -> str:
response = requests.post(
self.get_endpoint("/tags"),
headers=self.get_headers(),
json={"tag_name": tag, "target": commit},
)
if response.ok:
return "Tag creado exitosamente: " + tag
else:
raise GitFlowError("Ocurrió un error al crear el tag: " + response.text)
def get_endpoint(self, resource: str) -> str:
return f"{self.base_url}/api/v1/repos/{self.repository}{resource}"
def get_headers(self):
return {
"Accept": "application/json",
"Content-Type": "application/json",
"Authorization": "token " + self.token,
}
+28 -4
View File
@@ -1,9 +1,10 @@
import requests
from git_flow import GitFlowError from git_flow import GitFlowError
from git_flow.remote.base import RemoteAPI from git_flow.remote.base import RemoteAPI
class GithubRemoteAPI(RemoteAPI): class GithubRemoteAPI(RemoteAPI):
API_ENDPOINT = "https://github.com" API_ENDPOINT = "https://api.github.com/repos"
def create_pull_request( def create_pull_request(
self, self,
@@ -13,10 +14,33 @@ class GithubRemoteAPI(RemoteAPI):
description: str | None = None, description: str | None = None,
close_source_branch: bool = True close_source_branch: bool = True
) -> str: ) -> str:
raise GitFlowError("not implemented yet!") response = requests.post(
self.get_endpoint("/pulls"),
headers=self.get_headers(),
json={
"title": title,
"body": description,
"head": source,
"base": destination,
},
)
if response.ok:
return "PR creado exitosamente: " + response.json()["html_url"]
else:
raise GitFlowError("Ocurrió un error al crear el PR: " + response.text)
def create_tag(self, tag: str, commit: str) -> str: def create_tag(self, tag: str, commit: str) -> str:
raise GitFlowError("not implemented yet!") response = requests.post(
self.get_endpoint("/git/refs"),
headers=self.get_headers(),
json={"ref": f"refs/tags/{tag}", "sha": commit},
)
if response.ok:
return "Tag creado exitosamente: " + tag
else:
raise GitFlowError("Ocurrió un error al crear el tag: " + response.text)
def get_endpoint(self, resource: str) -> str: def get_endpoint(self, resource: str) -> str:
return GithubRemoteAPI.API_ENDPOINT return f"{GithubRemoteAPI.API_ENDPOINT}/{self.repository}{resource}"