Merge pull request 'feature: update I/O methods to use questionary where applicable' (#2) from feature/refactor-use-questionary-package into main
Publish to PyPI / Publish-to-PyPI (push) Failing after 43s

Reviewed-on: #2
This commit was merged in pull request #2.
This commit is contained in:
jt
2026-07-18 01:12:22 +00:00
8 changed files with 202 additions and 120 deletions
+65 -16
View File
@@ -3,6 +3,7 @@ from git_flow import (
REPOSITORY_TOKEN_FILENAME, REPOSITORY_TOKEN_FILENAME,
FLOWCONFIG_FILENAME, FLOWCONFIG_FILENAME,
FLOWCONFIG_VERSION, FLOWCONFIG_VERSION,
COMMIT_TYPES,
GitFlowError, GitFlowError,
) )
from git_flow.git import Git from git_flow.git import Git
@@ -18,9 +19,11 @@ import rich
import rich.panel import rich.panel
import questionary import questionary
flowconfig = Git.get_config(FLOWCONFIG_FILENAME) if isfile(FLOWCONFIG_FILENAME) else {} flowconfig = Git.get_config(FLOWCONFIG_FILENAME) if isfile(FLOWCONFIG_FILENAME) else {}
environments = flowconfig["flow.branches"].split(",") if "flow.branches" in flowconfig else [] environments = (
flowconfig["flow.branches"].split(",") if "flow.branches" in flowconfig else []
)
def ensure_initialized(): def ensure_initialized():
initialized = flowconfig["flow.initialized"] if flowconfig else None initialized = flowconfig["flow.initialized"] if flowconfig else None
@@ -48,7 +51,7 @@ def ensure_right_branch():
if branch == "HEAD": if branch == "HEAD":
raise GitFlowError("No se encuentra parado sobre una rama.") raise GitFlowError("No se encuentra parado sobre una rama.")
elif io_confirm(f"Se encuentra sobre la rama '{branch}'. ¿Es correcto?"): elif io_confirm(f"Rama actual: {branch}. ¿Es correcto?"):
return branch return branch
else: else:
raise GitFlowError("Ejecución cancelada.") raise GitFlowError("Ejecución cancelada.")
@@ -103,11 +106,11 @@ def ensure_clean_worktree(has_remote: bool):
if not_empty: if not_empty:
if not has_remote: if not has_remote:
io_warning("Existen cambios en tu entorno de trabajo sin commitear.") io_warning("Existen cambios en tu entorno de trabajo sin commitear.")
else:
raise GitFlowError("No se puede continuar con cambios pendientes.")
if not io_confirm("¿Desea continuar?", False): if not io_confirm("¿Desea continuar?", False):
raise GitFlowError("Ejecución abortada") raise GitFlowError("Ejecución abortada")
else:
raise GitFlowError("No se puede continuar con cambios pendientes.")
def get_remote_api(token: str) -> RemoteAPI: def get_remote_api(token: str) -> RemoteAPI:
@@ -118,7 +121,8 @@ def get_remote_api(token: str) -> RemoteAPI:
[schema, host, repository] = RemoteAPI.parse(flowconfig["flow.remote"]) [schema, host, repository] = RemoteAPI.parse(flowconfig["flow.remote"])
if remote_type == "gitea": if remote_type == "gitea":
return GiteaRemoteAPI(("https://" if schema == "ssh://" else schema) + host, repository, token) remote_schema = "https://" if schema == "ssh://" else schema
return GiteaRemoteAPI(remote_schema + host, repository, token)
elif remote_type == "bitbucket": elif remote_type == "bitbucket":
return BitbucketRemoteAPI(repository, token) return BitbucketRemoteAPI(repository, token)
elif remote_type == "github": elif remote_type == "github":
@@ -137,36 +141,81 @@ def is_valid_ticket(ticket: str) -> bool:
ticket_project = components[0] ticket_project = components[0]
ticket_number = components[1] ticket_number = components[1]
return ticket_project.isalpha() and ticket_project.isupper() and ticket_number.isnumeric() return (
ticket_project.isalpha()
and ticket_project.isupper()
and ticket_number.isnumeric()
)
def get_commit_message(commit_types: list[str] = COMMIT_TYPES) -> str:
commit_type = io_choice("Tipo de commit", commit_types)
commit_message = io_prompt(
"Mensaje de commit",
validator=lambda s: 0 < len(s) and len(s) < 100,
instruction="100 caracteres máximo",
)
return commit_type + ": " + commit_message
def io_error(message: str, title: Optional[str] = None): def io_error(message: str, title: Optional[str] = None):
_io_status(message, " Error: ", "red", title) _io_status(message, " Error: ", "red", title)
def io_warning(message: str, title: Optional[str] = None): def io_warning(message: str, title: Optional[str] = None):
_io_status(message, " Warning: ", "yellow", title) _io_status(message, "⚠ Warning: ", "yellow", title)
def io_info(message: str, title: Optional[str] = None): def io_info(message: str, title: Optional[str] = None):
_io_status(message, " Info: ", "blue", title) _io_status(message, " Info: ", "blue", title)
def io_success(message: str, title: Optional[str] = None): def io_success(message: str, title: Optional[str] = None):
_io_status(message, " Success: ", "green", title) _io_status(message, " Success: ", "green", title)
def _io_status(message: str, prefix: str, style: str, title: Optional[str] = None): def _io_status(message: str, prefix: str, style: str, title: Optional[str] = None):
if title: if title:
rich.print(rich.panel.Panel(message, title=prefix + title, style=style, expand=False, title_align="left")) rich.print(
rich.panel.Panel(
message,
title=prefix + title,
style=style,
expand=False,
title_align="left",
)
)
else: else:
rich.print(f"[{style}]{prefix}{message}[/{style}]") rich.print(f"[{style}]{prefix}{message}[/{style}]")
def io_confirm(question: str, default: bool = True) -> bool: def io_confirm(question: str, default: bool = True) -> bool:
return questionary.confirm(question, default=default, auto_enter=False).ask() return questionary.confirm(question, default=default, auto_enter=False).unsafe_ask()
def io_choice(prompt: str, options: list[str]) -> str: def io_choice(prompt: str, options: list[str]) -> str:
return questionary.select(prompt, options, instruction="Usar flechas").ask() return questionary.select(
prompt,
options,
use_search_filter=True,
use_jk_keys=False,
instruction="Tipear o usar flechas",
).unsafe_ask()
def io_prompt(message: str, default: str = "", persistent: bool = False, strip: bool = False, validator = None) -> str:
def io_prompt(
message: str,
default: str = "",
persistent: bool = False,
strip: bool = False,
validator=None,
instruction: str | None = None,
) -> str:
if persistent and not validator: if persistent and not validator:
validator = lambda s: len(s.strip()) > 0 validator = lambda s: len(s.strip()) > 0
value = questionary.text(message, default, validate=validator).ask() value = questionary.text(
message, default, validate=validator, instruction=instruction
).unsafe_ask()
return value.strip() if strip else value return value.strip() if strip else value
+20 -14
View File
@@ -1,16 +1,20 @@
from typing import Annotated
import typer
from rich.console import Console
import git_flow.command.base as base
from git_flow import ( from git_flow import (
TRASH_BRANCH_PREFIX, TRASH_BRANCH_PREFIX,
WIP_BRANCH_PREFIX, WIP_BRANCH_PREFIX,
GitFlowError, GitFlowError,
) )
from git_flow.command.base import *
from git_flow.git import Git from git_flow.git import Git
from typing import Annotated
import typer
BRANCH_FORMAT = "%(refname:short)" BRANCH_FORMAT = "%(refname:short)"
app = typer.Typer() app = typer.Typer()
console = Console()
@app.command() @app.command()
@@ -38,28 +42,28 @@ def branch(
] = False, ] = False,
): ):
"""Lista ramas del repositorio, agrupandolas por entorno objetivo""" """Lista ramas del repositorio, agrupandolas por entorno objetivo"""
ensure_initialized() base.ensure_initialized()
current_branch = Git.get_current_branch() current_branch = Git.get_current_branch()
if current_branch in environments: if current_branch in base.environments:
current_environment = current_branch current_environment = current_branch
else: else:
current_environment, _ = get_branch_env_and_type(current_branch) current_environment, _ = base.get_branch_env_and_type(current_branch)
if trash: if trash:
show_branches(TRASH_BRANCH_PREFIX, current_branch) show_branches(TRASH_BRANCH_PREFIX, current_branch)
elif wip: elif wip:
show_branches(WIP_BRANCH_PREFIX, current_branch) show_branches(WIP_BRANCH_PREFIX, current_branch)
elif all: elif all:
show_envs(environments, current_branch) show_envs(base.environments, current_branch)
show_all_branches(environments, current_branch) show_all_branches(base.environments, current_branch)
elif environment is None: elif environment is None:
show_envs(environments, current_branch) show_envs(base.environments, current_branch)
show_env_branches(current_environment, environments, current_branch) show_env_branches(current_environment, base.environments, current_branch)
elif environment in environments: elif environment in base.environments:
show_envs(environments, current_branch) show_envs(base.environments, current_branch)
show_env_branches(environment, environments, current_branch) show_env_branches(environment, base.environments, current_branch)
else: else:
raise GitFlowError(f"'{environment}' no es un entorno válido.") raise GitFlowError(f"'{environment}' no es un entorno válido.")
@@ -78,7 +82,9 @@ def show_branches(branch_prefix: str, current_branch: str):
def get_branches(branch_prefix: str): def get_branches(branch_prefix: str):
return Git("for-each-ref", "refs/heads/" + branch_prefix, format=BRANCH_FORMAT).lines() return Git(
"for-each-ref", "refs/heads/" + branch_prefix, format=BRANCH_FORMAT
).lines()
def show_all_branches(environments: list[str], current_branch: str): def show_all_branches(environments: list[str], current_branch: str):
+7 -8
View File
@@ -1,8 +1,10 @@
import typer
from datetime import datetime from datetime import datetime
from git_flow import COMMIT_TYPES, TRASH_BRANCH_PREFIX, WIP_BRANCH_PREFIX, GitFlowError
from git_flow.git import Git import typer
import git_flow.command.base as base import git_flow.command.base as base
from git_flow import TRASH_BRANCH_PREFIX, WIP_BRANCH_PREFIX, GitFlowError
from git_flow.git import Git
app = typer.Typer() app = typer.Typer()
@@ -20,11 +22,8 @@ def commit():
else: else:
raise GitFlowError("Debe agregar algún cambio al indice para continuar.") raise GitFlowError("Debe agregar algún cambio al indice para continuar.")
commit_type = base.io_choice("Tipo de commit", COMMIT_TYPES) message = base.get_commit_message()
commit_message = base.io_prompt( commit_type = message[: message.index(":")]
"Mensaje (máximo recomendado: 100 caracteres)", persistent=True
)
message = commit_type + ": " + commit_message
if branch.startswith(WIP_BRANCH_PREFIX): if branch.startswith(WIP_BRANCH_PREFIX):
original_branch = branch.removeprefix(WIP_BRANCH_PREFIX) original_branch = branch.removeprefix(WIP_BRANCH_PREFIX)
+27 -21
View File
@@ -1,8 +1,8 @@
import typer import typer
import git_flow.command.base as base
from git_flow import FLOWCONFIG_FILENAME, FLOWCONFIG_VERSION, 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 *
app = typer.Typer() app = typer.Typer()
@@ -15,7 +15,7 @@ def init():
_ensure_not_already_initialized() _ensure_not_already_initialized()
branches = _setup_flow_branches() branches = _setup_flow_branches()
(remote, remote_type) = _setup_flow_remote() remote, remote_type = _setup_flow_remote()
flowconfig = { flowconfig = {
"flow.version": str(FLOWCONFIG_VERSION), "flow.version": str(FLOWCONFIG_VERSION),
@@ -36,32 +36,34 @@ def init():
def _ensure_is_repository(): def _ensure_is_repository():
if not Git.is_repository(): if not Git.is_repository():
warning("El directorio actual no es un repositorio.") base.io_warning("El directorio actual no es un repositorio.")
if not confirm("¿Desea inicializarlo?"): if not base.io_confirm("¿Desea inicializarlo?"):
raise GitFlowError("No se puede continuar sin inicializar el repositorio") raise GitFlowError("No se puede continuar sin inicializar el repositorio")
Git("init").exec() Git("init").exec()
def _ensure_not_already_initialized(): def _ensure_not_already_initialized():
if not flowconfig: if not base.flowconfig:
return return
elif flowconfig.get("flow.initialized") == "true": elif base.flowconfig.get("flow.initialized") == "true":
raise GitFlowError("El repositorio ya fue inicializado para usar git-flow") raise GitFlowError("El repositorio ya fue inicializado para usar git-flow")
else: else:
raise GitFlowError("Valor de 'flow.initialized' es inválido") raise GitFlowError("Valor de 'flow.initialized' es inválido")
def _setup_flow_branches(): def _setup_flow_branches():
panel( base.io_info(
"Configuración de entornos",
"""Ingrese las ramas que representan los entornos de deploy del proyecto """Ingrese las ramas que representan los entornos de deploy del proyecto
en orden creciente de cercanía al entorno productivo, y separados por en orden creciente de cercanía al entorno productivo, y separados por
coma. Por ejemplo: "dev, test, prod".""", coma. Por ejemplo: "dev, test, prod".""",
title="Configuración de entornos",
) )
branches = list(map(lambda b: b.strip(), prompt("Ramas", "main").split(","))) branches = list(
map(lambda b: b.strip(), base.io_prompt("Ramas", "main").split(","))
)
if not all(branches): if not all(branches):
raise GitFlowError("No puede ingresar una rama vacia") raise GitFlowError("No puede ingresar una rama vacia")
@@ -70,21 +72,23 @@ coma. Por ejemplo: "dev, test, prod".""",
def _setup_flow_remote(): def _setup_flow_remote():
panel( base.io_info(
"Configuración de remoto", """Configurar un repositorio remoto le permite generar PRs automáticamente para el
"Puede configurar un repositorio remoto para generar PRs automáticamente", entorno correspondiente. Deberá seleccionar uno de los remotos actuales, o crear
uno nuevo.""",
title="Configuración de remoto",
) )
remote = None remote = None
remote_type = None remote_type = None
if confirm("¿Configurar repositorio remoto?"): if base.io_confirm("¿Configurar repositorio remoto?"):
remotes = Git("remote").lines(print="Listando remotos disponibles") remotes = Git("remote").lines(print="Listando remotos disponibles")
if not remotes: if not remotes:
info("No tiene ningún repositorio remoto, se creará uno.") base.io_info("No tiene ningún repositorio remoto, se creará uno.")
url = prompt( url = base.io_prompt(
"Ingrese la URL del repositorio (e.g. https://github.com/user/repo.git)", "Ingrese la URL del repositorio (e.g. https://github.com/user/repo.git)",
persistent=True, persistent=True,
) )
@@ -93,10 +97,12 @@ def _setup_flow_remote():
elif len(remotes) == 1: elif len(remotes) == 1:
remote = remotes[0] remote = remotes[0]
else: else:
info("Tiene más de un remoto, seleccione el que va a utilizar.") base.io_info("Tiene más de un remoto, seleccione el que va a utilizar.")
remote = choice("Remoto", remotes) remote = base.io_choice("Remoto", remotes)
remote_type = choice("Tipo de remoto:", ["bitbucket", "github", "gitea"]) remote_type = base.io_choice(
"Tipo de remoto:", ["bitbucket", "github", "gitea"]
)
return (remote, remote_type) return (remote, remote_type)
@@ -105,10 +111,10 @@ def _ensure_all_flow_branches_exist(branches: list[str], remote: str | None):
existing_branches = Git.get_branches() existing_branches = Git.get_branches()
if not all(map(lambda b: b in existing_branches, branches)): if not all(map(lambda b: b in existing_branches, branches)):
warning("Algunas de las ramas de entornos expecificadas no existen.") base.io_warning("Algunas de las ramas de entornos expecificadas no existen.")
info("Debe indicar sobre que rama se crearán las ramas de entorno.") base.io_info("Debe indicar sobre que rama se crearán las ramas de entorno.")
target_branch = choice("Rama", existing_branches) target_branch = base.io_choice("Rama", existing_branches)
for branch in branches: for branch in branches:
if branch not in existing_branches: if branch not in existing_branches:
+18 -7
View File
@@ -1,7 +1,8 @@
import typer import typer
import git_flow.command.base as base
from git_flow import GitFlowError from git_flow import GitFlowError
from git_flow.changelog import Changelog from git_flow.changelog import Changelog
import git_flow.command.base as base
from git_flow.git import Git from git_flow.git import Git
app = typer.Typer() app = typer.Typer()
@@ -87,8 +88,12 @@ def check_merge_conflicts(target: str):
Git("merge", abort=True).exec(print="Abortando merge de prueba", check=False) Git("merge", abort=True).exec(print="Abortando merge de prueba", check=False)
if merge_conflicts: if merge_conflicts:
base.io_error(f"La rama actual tiene conflictos con {target}.\n" base.io_error(
f"Se recomienda mergear {target} a la rama actual, resolver los conflictos localmente, y ejecutar nuevamente este comando", "Conflictos de merge") f"La rama actual tiene conflictos con {target}.\n"
f"Se recomienda mergear {target} a la rama actual, resolver los conflictos localmente,\n"
"y ejecutar nuevamente este comando",
title="Conflictos de merge",
)
raise GitFlowError("Ejecución abortada.") raise GitFlowError("Ejecución abortada.")
else: else:
base.io_success("No se detectaron merge conflicts.") base.io_success("No se detectaron merge conflicts.")
@@ -109,7 +114,7 @@ def create_pull_request(token: str, branch: str, target: str):
target, target,
title, title,
changelog.generate_content(target, branch), changelog.generate_content(target, branch),
branch not in base.environments branch not in base.environments,
) )
base.io_success(message, "Rama creada") base.io_success(message, "Rama creada")
@@ -118,6 +123,7 @@ def create_pull_request(token: str, branch: str, target: str):
Git("switch", target).exec(print="Cambiando a rama objetivo") Git("switch", target).exec(print="Cambiando a rama objetivo")
Git("pull").exec(print="Obteniendo cambios") Git("pull").exec(print="Obteniendo cambios")
def _get_pr_title_from_branch(branch: str) -> str: def _get_pr_title_from_branch(branch: str) -> str:
if branch in base.environments: if branch in base.environments:
target = base.environments[base.environments.index(branch) + 1] target = base.environments[base.environments.index(branch) + 1]
@@ -138,9 +144,14 @@ def _get_pr_title_from_branch(branch: str) -> str:
branch_ticket = maybe_ticket branch_ticket = maybe_ticket
branch_desc = branch_desc[second_dash + 1 :] branch_desc = branch_desc[second_dash + 1 :]
return " ".join(filter(None, [ return " ".join(
filter(
None,
[
f"[Pasaje a {components[1]}]" if len(components) == 4 else None, f"[Pasaje a {components[1]}]" if len(components) == 4 else None,
branch_type + ":", branch_type + ":",
branch_ticket, branch_ticket,
branch_desc.replace("-", " ") branch_desc.replace("-", " "),
])) ],
)
)
+22 -10
View File
@@ -1,9 +1,11 @@
import typer
from git_flow import BRANCH_TYPES
import git_flow.command.base as base
from git_flow.git import Git
from typing import Optional from typing import Optional
import typer
import git_flow.command.base as base
from git_flow import BRANCH_TYPES
from git_flow.git import Git
app = typer.Typer() app = typer.Typer()
@@ -17,8 +19,11 @@ def new():
if branch not in base.environments: if branch not in base.environments:
base.io_warning("La rama actual no corresponde a un entorno configurado.") base.io_warning("La rama actual no corresponde a un entorno configurado.")
base.io_info("Se creará una nueva rama de trabajo sobre la rama actual.\n" base.io_info(
"Debe seleccionar el tipo de cambio a realizar.", title="Tipo de cambio") "Se creará una nueva rama de trabajo sobre la rama actual.\n"
"Debe seleccionar el tipo de cambio a realizar.",
title="Tipo de cambio",
)
branch_type = base.io_choice("Tipos de cambio", BRANCH_TYPES) branch_type = base.io_choice("Tipos de cambio", BRANCH_TYPES)
ticket = _get_optional_ticket() ticket = _get_optional_ticket()
@@ -30,15 +35,20 @@ def new():
if base.io_confirm(f"¿Crear rama '{new_branch}' sobre la rama actual?"): if base.io_confirm(f"¿Crear rama '{new_branch}' sobre la rama actual?"):
Git("switch", new_branch, create=True).exec(print="Creando nueva rama") Git("switch", new_branch, create=True).exec(print="Creando nueva rama")
def _get_optional_ticket() -> Optional[str]: def _get_optional_ticket() -> Optional[str]:
return base.io_prompt( return base.io_prompt(
"Ticket que respalda el cambio (en formato ABC-123)", "Ticket que respalda el cambio (en formato ABC-123)",
validator=lambda x: len(x.strip()) == 0 or base.is_valid_ticket(x) validator=lambda x: len(x.strip()) == 0 or base.is_valid_ticket(x),
) )
def _get_unique_branch_name(branch_type: str, ticket: Optional[str]) -> str: def _get_unique_branch_name(branch_type: str, ticket: Optional[str]) -> str:
base.io_info(f"Ingrese las palabras clave que describan el cambio de tipo '{branch_type}'.\n" base.io_info(
"Las mismas se utilizaran para generar el nombre de la rama.", title="Nombre de rama") f"Ingrese las palabras clave que describan el cambio de tipo '{branch_type}'.\n"
"Las mismas se utilizaran para generar el nombre de la rama.",
title="Nombre de rama",
)
branch = None branch = None
branches = Git.get_branches() branches = Git.get_branches()
@@ -46,7 +56,9 @@ def _get_unique_branch_name(branch_type: str, ticket: Optional[str]) -> str:
keywords = base.io_prompt("Palabras clave", persistent=True) keywords = base.io_prompt("Palabras clave", persistent=True)
keywords = filter(lambda k: len(k) > 0, keywords.split(" ")) keywords = filter(lambda k: len(k) > 0, keywords.split(" "))
branch = branch_type + "/" + (ticket + "-" if ticket else "") + "-".join(keywords) branch = (
branch_type + "/" + (ticket + "-" if ticket else "") + "-".join(keywords)
)
if branch in branches: if branch in branches:
base.io_error( base.io_error(
+21 -24
View File
@@ -1,38 +1,40 @@
from git_flow import COMMIT_TYPES, GitFlowError
from git_flow.command.base import *
from git_flow.git import Git
from typing import Optional from typing import Optional
import typer import typer
import git_flow.command.base as base
from git_flow import COMMIT_TYPES, GitFlowError
from git_flow.git import Git
app = typer.Typer() app = typer.Typer()
@app.command() @app.command()
def release(group: Optional[str] = None): def release(group: Optional[str] = None):
"""Realiza el merge de la rama, o crea el PR para hacerlo si tiene un remoto configurado""" """Realiza el merge de la rama, o crea el PR para hacerlo si tiene un remoto configurado"""
ensure_initialized() base.ensure_initialized()
branch = ensure_right_branch() branch = base.ensure_right_branch()
env, _ = get_branch_env_and_type(branch) env, _ = base.get_branch_env_and_type(branch)
if env == environments[-1]: if env == base.environments[-1]:
raise GitFlowError( raise GitFlowError(
"No se puede hacer release de una rama en el ultimo entorno." "No se puede hacer release de una rama en el ultimo entorno."
) )
has_remote = "flow.remote" in flowconfig has_remote = "flow.remote" in base.flowconfig
ensure_clean_worktree(has_remote) base.ensure_clean_worktree(has_remote)
next_env = environments[environments.index(env) + 1] next_env = base.environments[base.environments.index(env) + 1]
next_branch = ( next_branch = (
branch.replace(f"/{env}/", f"/{next_env}/") branch.replace(f"/{env}/", f"/{next_env}/")
if branch.startswith("release/") if branch.startswith("release/")
else f"release/{next_env}/{branch}" else f"release/{next_env}/{branch}"
) )
base = Git.get_first_fork_point(branch, env) fork_point = Git.get_first_fork_point(branch, env)
commits = Git("log", base + "..", format="%s").lines() commits = Git("log", fork_point + "..", format="%s").lines()
if not commits: if not commits:
raise GitFlowError("No hay cambios a mergear.") raise GitFlowError("No hay cambios a mergear.")
@@ -45,9 +47,9 @@ def release(group: Optional[str] = None):
grouping = group is not None grouping = group is not None
if not group: if not group:
if confirm("¿Desea agrupar este release con otra rama?", False): if base.io_confirm("¿Desea agrupar este release con otra rama?", False):
grouping = True grouping = True
group = choice( group = base.io_choice(
"Grupo release: ", Git.get_branches("release/" + next_env + "/") "Grupo release: ", Git.get_branches("release/" + next_env + "/")
) )
else: else:
@@ -58,18 +60,13 @@ def release(group: Optional[str] = None):
Git("pull").exec(print="Sincronizando cambios la rama objetivo") Git("pull").exec(print="Sincronizando cambios la rama objetivo")
Git("switch", "-").exec(print="Volviendo a la rama original") Git("switch", "-").exec(print="Volviendo a la rama original")
if len(commits) > 1 and confirm( if len(commits) > 1 and base.io_confirm(
f"¿Desea reemplazar los {len(commits)} commits de la rama por uno solo?", False f"¿Desea reemplazar los {len(commits)} commits de la rama por uno solo?", False
): ):
info("Debe ingresar el mensaje del commit a crear.") base.io_info("Debe ingresar el mensaje del commit a crear.")
message = base.get_commit_message(COMMIT_TYPES[:-1])
commit_type = choice("Tipo de commit", COMMIT_TYPES[:-1]) Git("switch", next_branch, fork_point, create=True).exec(
commit_message = prompt(
"Mensaje (máximo recomendado: 100 caracteres)", persistent=True
)
message = commit_type + ": " + commit_message
Git("switch", next_branch, base, create=True).exec(
print="Creando rama release en base" print="Creando rama release en base"
) )
Git("merge", branch, squash=True).exec(print="Squasheando commits en uno solo") Git("merge", branch, squash=True).exec(print="Squasheando commits en uno solo")
@@ -77,7 +74,7 @@ def release(group: Optional[str] = None):
else: else:
Git("switch", next_branch, create=True).exec(print="Creando rama release") Git("switch", next_branch, create=True).exec(print="Creando rama release")
status = Git("rebase", base, next_branch, onto=group).code( status = Git("rebase", fork_point, next_branch, onto=group).code(
print="Moviendo cambios hacia el siguiente ambiente" print="Moviendo cambios hacia el siguiente ambiente"
) )
+13 -11
View File
@@ -1,3 +1,8 @@
from typing import Optional
import typer
import git_flow.command.base as base
from git_flow import ( from git_flow import (
COMMIT_TYPE_INCREMENT, COMMIT_TYPE_INCREMENT,
SEMVER_MAJOR, SEMVER_MAJOR,
@@ -7,10 +12,7 @@ from git_flow import (
GitFlowError, GitFlowError,
) )
from git_flow.changelog import Changelog from git_flow.changelog import Changelog
from git_flow.command.base import *
from git_flow.git import Git from git_flow.git import Git
from typing import Optional
import typer
app = typer.Typer() app = typer.Typer()
@@ -18,22 +20,22 @@ app = typer.Typer()
@app.command() @app.command()
def tag(token: Optional[str] = None): def tag(token: Optional[str] = None):
"""Crea un nuevo tag para el último merge.""" """Crea un nuevo tag para el último merge."""
ensure_initialized() base.ensure_initialized()
target = Git.get_current_branch() target = Git.get_current_branch()
if target not in environments: if target not in base.environments:
raise GitFlowError("Solo se pueden taggear commits en ramas principales.") raise GitFlowError("Solo se pueden taggear commits en ramas principales.")
branch = Git( branch = Git(
"show", get_last_merge_commit() + "^2", patch=False, format="%h" "show", get_last_merge_commit() + "^2", patch=False, format="%h"
).firstline() ).firstline()
base = Git.get_first_fork_point(branch, target) fork_point = Git.get_first_fork_point(branch, target)
commits = Git("log", base + ".." + branch, format="%s").lines() commits = Git("log", fork_point + ".." + branch, format="%s").lines()
next_tag = get_next_tag_from_commits(commits, target) next_tag = get_next_tag_from_commits(commits, target)
if not next_tag: if not next_tag:
info( base.io_info(
"Los cambios realizados no implican un salto de versión, se mantiene la anterior." "Los cambios realizados no implican un salto de versión, se mantiene la anterior."
) )
return return
@@ -45,21 +47,21 @@ def tag(token: Optional[str] = None):
changelog = Changelog() changelog = Changelog()
changelog.update(next_tag, base, branch) changelog.update(next_tag, fork_point, branch)
Git("add", Changelog.FILENAME).exec(print="Agregando nuevo CHANGELOG.md") Git("add", Changelog.FILENAME).exec(print="Agregando nuevo CHANGELOG.md")
Git("commit", message=Changelog.COMMIT_MESSAGE).exec(print="Creando commit") Git("commit", message=Changelog.COMMIT_MESSAGE).exec(print="Creando commit")
tag_commit = Git("show", "HEAD", patch=False, format="%h").firstline() tag_commit = Git("show", "HEAD", patch=False, format="%h").firstline()
if ci: if ci:
Git("push").exec(print="Subiendo commit al remoto para taggearlo") Git("push").exec(print="Subiendo commit al remoto para taggearlo")
success(get_remote_api(token).create_tag(next_tag, tag_commit)) base.io_success(base.get_remote_api(token).create_tag(next_tag, tag_commit))
Git("tag", next_tag, tag_commit).exec(print="Creando tag localmente") Git("tag", next_tag, tag_commit).exec(print="Creando tag localmente")
def get_token_and_ci(token: Optional[str]): def get_token_and_ci(token: Optional[str]):
try: try:
return (ensure_repository_token(), False) return (base.ensure_repository_token(), False)
except GitFlowError: except GitFlowError:
return (token, True) return (token, True)