Compare commits

...
53 Commits
Author SHA1 Message Date
bitbucket-pipelines 2a21922a63 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-14 15:22:19 +00:00
JonathanGitFlowandjt 12b2bc9a39 Merged in bugfix/merge-pr-title-generation (pull request #49)
bugfix: merge pr title generation

Approved-by: Jonathan Teran
2025-12-14 15:21:54 +00:00
Jonathan Teran Carballo b24b22a178 bugfix: generación de PR de ramas release 2025-12-14 12:20:32 -03:00
bitbucket-pipelines c5f1be346c chore: bump version and update CHANGELOG.md [skip ci] 2025-12-14 15:05:04 +00:00
JonathanGitFlowandjt 6c32b3e751 Merged in bugfix/release-not-listing-branches (pull request #48)
bugfix: release not listing branches

Approved-by: Jonathan Teran
2025-12-14 15:04:39 +00:00
Jonathan Teran Carballo 5600e5ec83 bugfix: listado de ramas para elegir grupo release no devolvia resultados 2025-12-14 11:59:42 -03:00
bitbucket-pipelines 57142afa40 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-11 20:08:25 +00:00
JonathanGitFlowandjt 4375af35d9 Merged in bugfix/release-switch-to-group (pull request #47)
bugfix: release switch to group

Approved-by: Jonathan Teran
2025-12-11 20:07:56 +00:00
Jonathan Teran Carballo 44b8efd953 bugfix: al hacer un release, cambio a la rama grupo si esta disponible 2025-12-11 17:07:13 -03:00
bitbucket-pipelines f4b47000a6 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-11 18:18:23 +00:00
JonathanGitFlowandjt b12d2a1930 Merged in bugfix/first-fork-point-getter (pull request #46)
bugfix: first fork point getter

Approved-by: Jonathan Teran
2025-12-11 18:17:53 +00:00
Jonathan Teran Carballo 7b4fb34ef3 bugfix: cambio la forma en la que se obtiene el first fork point entre 2 ramas 2025-12-11 15:15:45 -03:00
bitbucket-pipelines 6a758cdb94 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-11 12:55:31 +00:00
JonathanGitFlowandjt b148a9f71d Merged in feature/show-commit-list-on-release (pull request #45)
feature: show commit list on release

Approved-by: Jonathan Teran
2025-12-11 12:55:01 +00:00
Jonathan Teran 2e65cb7a3e feature: muestro el listado de commits a pasar al siguiente entorno en release 2025-12-11 09:53:41 -03:00
bitbucket-pipelines 9c8df34a09 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-10 18:29:40 +00:00
JonathanGitFlowandjt 89f8bc58ba Merged in bugfix/commit-list-for-merge (pull request #44)
bugfix: commit list for merge

Approved-by: Jonathan Teran
2025-12-10 18:29:07 +00:00
Jonathan Teran 4e089dc4d6 bugfix: cambio la lista de commits a mostrar en el merge 2025-12-10 15:06:21 -03:00
Jonathan Teran c8e1a7cc88 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-04 10:19:54 -03:00
JonathanGitFlowandjt 65be7a01f0 Merged in bugfix/tag-name-scheme (pull request #43)
bugfix: tag name scheme

Approved-by: Jonathan Teran
2025-12-04 13:17:52 +00:00
Jonathan Teran 7ce6addf07 bugfix: revertir guion entre version y entorno, agregar guion a sufijo 2025-12-04 10:17:26 -03:00
bitbucket-pipelines 824b5f06de chore: bump version and update CHANGELOG.md [skip ci] 2025-12-04 13:16:27 +00:00
JonathanGitFlowandjt 9937f4b6bc Merged in bugfix/tag-name-scheme (pull request #42)
bugfix: tag name scheme

Approved-by: Jonathan Teran
2025-12-04 13:15:56 +00:00
Jonathan Teran 053b8b102b bugfix: agrego guion entre version y nombre de entorno 2025-12-04 10:15:32 -03:00
bitbucket-pipelines a2475195b2 chore: bump version and update CHANGELOG.md [skip ci] 2025-12-04 13:13:42 +00:00
JonathanGitFlowandjt 603519bf39 Merged in bugfix/create-changelog-when-missing (pull request #41)
bugfix: create changelog when missing

Approved-by: Jonathan Teran
2025-12-04 13:13:14 +00:00
Jonathan Teran 0c8e7bb37a bugfix: crear changelog cuando no existe 2025-12-04 10:11:10 -03:00
bitbucket-pipelines 6643271747 chore: bump version and update CHANGELOG.md [skip ci] 2025-11-29 16:41:43 +00:00
JonathanGitFlowandjt 68e11c66d1 Merged in bugfix/group-release-confirmation-and-commits-squash-count (pull request #40)
bugfix: group release confirmation and commits squash count

Approved-by: Jonathan Teran
2025-11-29 16:41:19 +00:00
jt f1f6e6724c bugfix: arreglo mensaje de confirmacion de squash, muevo prompt de commit message antes del squash 2025-11-29 13:40:34 -03:00
jt 83df7e9553 bugfix: cambio default en confirmacion para agrupar release 2025-11-29 13:35:25 -03:00
bitbucket-pipelines f5e683a1da chore: bump version and update CHANGELOG.md [skip ci] 2025-11-29 16:22:03 +00:00
JonathanGitFlowandjt fb9b8b57f6 Merged in feature/release-branch-groups (pull request #39)
feature: release branch groups

Approved-by: Jonathan Teran
2025-11-29 16:21:39 +00:00
jt c47afffa14 feature: agrego opcion para agrupar ramas release 2025-11-29 13:20:33 -03:00
bitbucket-pipelines 341e6239b2 chore: bump version and update CHANGELOG.md [skip ci] 2025-11-28 18:48:40 +00:00
JonathanGitFlowandjt 4fb00e786f Merged in bugfix/command-init-merge-errors (pull request #38)
bugfix: command init merge errors

Approved-by: Jonathan Teran
2025-11-28 18:48:15 +00:00
Jonathan Teran Carballo 2ad747111f bugfix: arreglo split de ramas de flow.branches 2025-11-28 15:44:05 -03:00
Jonathan Teran Carballo 5c3a3171fd bugfix: arreglo iteracion de items de flowconfig en set_config 2025-11-28 15:43:46 -03:00
Jonathan Teran Carballo 50979e2c3d docs: agrego venv a lista de carpetas ignoradas 2025-11-28 15:42:54 -03:00
bitbucket-pipelines a670b90899 chore: bump version and update CHANGELOG.md [skip ci] 2025-11-27 02:35:54 +00:00
JonathanGitFlowandjt 532a93e95e Merged in refactor/move-constants (pull request #37)
refactor: move constants

Approved-by: Jonathan Teran
2025-11-27 02:35:28 +00:00
jt 29757ccfb5 refactor: muevo constantes a modulo git_flow para centralizar uso 2025-11-26 23:35:00 -03:00
jt 7f857fdd68 bugfix: ignorar carpetas generadas en build de lsp 2025-11-26 23:34:39 -03:00
bitbucket-pipelines 432885e107 chore: bump version and update CHANGELOG.md [skip ci] 2025-11-27 01:53:24 +00:00
JonathanGitFlowandjt a7ea4a6149 Merged in feature/branch-list-format (pull request #36)
feature: branch list format

Approved-by: Jonathan Teran
2025-11-27 01:53:01 +00:00
jt 717708469a feature: modifico formato de listado de ramas 2025-11-26 22:51:58 -03:00
bitbucket-pipelines 542c629489 chore: bump version and update CHANGELOG.md [skip ci] 2025-11-23 21:51:20 +00:00
JonathanGitFlowandjt 3c271be415 Merged in feature/add-branch-command (pull request #35)
feature: add branch command

Approved-by: Jonathan Teran
2025-11-23 21:50:57 +00:00
jt c74f1a3093 feature: agrego comando branch para listar ramas 2025-11-23 18:50:00 -03:00
jt eafb3f1b31 feature: agrego soporte para parametros repetidos en llamadas a Git 2025-11-23 18:49:42 -03:00
JonathanGitFlowandjt 82b9a70400 Merged in docs/agregar-seccion-instalacion (pull request #34)
docs: agregar seccion instalacion

Approved-by: Jonathan Teran
2025-11-23 07:05:38 +00:00
jt 6f90a37ba2 style: arreglo sublistas en seccion configuracion 2025-11-23 04:04:48 -03:00
jt 5a641938c0 docs: agrego seccion con pasos de instalacion 2025-11-23 04:02:35 -03:00
15 changed files with 352 additions and 86 deletions
+8 -3
View File
@@ -1,6 +1,11 @@
# Bitbucket token
.repository-token .repository-token
**/__pycache__/ # Virtual environment
build/ venv/
**/*.egg-info/
# Build folders and artifacts
dist/ dist/
build/
**/__pycache__/
**/*.egg-info/
+123
View File
@@ -1,3 +1,126 @@
## v1.7.4 - 2025-12-14
- **bugfix**: generación de PR de ramas release [[jteran](mailto:jteran@renatre.org.ar)] (b24b22a)
---
## v1.7.3 - 2025-12-14
- **bugfix**: listado de ramas para elegir grupo release no devolvia resultados [[jteran](mailto:jteran@renatre.org.ar)] (5600e5e)
---
## v1.7.2 - 2025-12-11
- **bugfix**: al hacer un release, cambio a la rama grupo si esta disponible [[jteran](mailto:jteran@renatre.org.ar)] (44b8efd)
---
## v1.7.1 - 2025-12-11
- **bugfix**: cambio la forma en la que se obtiene el first fork point entre 2 ramas [[jteran](mailto:jteran@renatre.org.ar)] (7b4fb34)
---
## v1.7.0 - 2025-12-11
- **feature**: muestro el listado de commits a pasar al siguiente entorno en release [[jteran](mailto:jteran@renatre.org.ar)] (2e65cb7)
---
## v1.6.4 - 2025-12-10
- **bugfix**: cambio la lista de commits a mostrar en el merge [[jteran](mailto:jteran@renatre.org.ar)] (4e089dc)
---
## v1.6.3 - 2025-12-04
- **bugfix**: revertir guion entre version y entorno, agregar guion a sufijo [[jteran](mailto:jteran@renatre.org.ar)] (7ce6add)
- **bugfix**: agrego guion entre version y nombre de entorno [[jteran](mailto:jteran@renatre.org.ar)] (053b8b1)
---
## v1.6.3- - 2025-12-04
- **bugfix**: agrego guion entre version y nombre de entorno [[jteran](mailto:jteran@renatre.org.ar)] (053b8b1)
---
## v1.6.2 - 2025-12-04
- **bugfix**: crear changelog cuando no existe [[jteran](mailto:jteran@renatre.org.ar)] (0c8e7bb)
---
## v1.6.1 - 2025-11-29
### Commits
- **bugfix**: arreglo mensaje de confirmacion de squash, muevo prompt de commit message antes del squash [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (f1f6e67)
- **bugfix**: cambio default en confirmacion para agrupar release [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (83df7e9)
---
## v1.6.0 - 2025-11-29
### Commits
- **feature**: agrego opcion para agrupar ramas release [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (c47afff)
---
## v1.5.2 - 2025-11-28
### Commits
- **bugfix**: arreglo split de ramas de flow.branches [[jteran](mailto:jteran@renatre.org.ar)] (2ad7471)
- **bugfix**: arreglo iteracion de items de flowconfig en set_config [[jteran](mailto:jteran@renatre.org.ar)] (5c3a317)
- **docs**: agrego venv a lista de carpetas ignoradas [[jteran](mailto:jteran@renatre.org.ar)] (50979e2)
---
## v1.5.1 - 2025-11-27
### Commits
- **refactor**: muevo constantes a modulo git_flow para centralizar uso [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (29757cc)
- **bugfix**: ignorar carpetas generadas en build de lsp [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (7f857fd)
---
## v1.5.0 - 2025-11-27
### Commits
- **feature**: modifico formato de listado de ramas [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (7177084)
---
## v1.4.0 - 2025-11-23
### Commits
- **feature**: agrego comando branch para listar ramas [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (c74f1a3)
- **feature**: agrego soporte para parametros repetidos en llamadas a Git [[jonathan.nerat](mailto:jonathan.nerat@gmail.com)] (eafb3f1)
---
## v1.3.0 - 2025-11-23 ## v1.3.0 - 2025-11-23
### Commits ### Commits
+28 -7
View File
@@ -7,6 +7,30 @@ Herramienta para establecer un workflow siguiendo [Conventional Commits
Esta diseñada para facilitar la creación de commits y branches en un formato estandar, que permite Esta diseñada para facilitar la creación de commits y branches en un formato estandar, que permite
derivar versiones automaticamente de los merges sobre ramas principales. derivar versiones automaticamente de los merges sobre ramas principales.
## Instalación
Para instalar Git Flow, se sugiere utilizar [pipx](https://github.com/pypa/pipx) (seguir la
documentación para instalarlo). Una vez instalado `pipx`, simplemente clonamos el repositorio en una
carpeta local, y lo instalamos:
```sh
cd ~/Downloads
git clone git@bitbucket.org:sushi2717/git-flow.git
cd git-flow
pipx install .
```
Luego podemos verificar que se haya instalado correctamente ejecutando: `git-flow -h`
Para actualizarlo, hacemos un pull de los cambios recientes del repositorio, y hacemos el upgrade
mediante `pipx`:
```sh
cd ~/Downloads/git-flow
git pull
pipx upgrade git-flow
```
## Configuración ## Configuración
Para poder usar la herramienta en un repositorio, necesitamos inicializarlo: Para poder usar la herramienta en un repositorio, necesitamos inicializarlo:
@@ -22,18 +46,15 @@ El comando solicitará 2 opciones:
- **Remoto** (opcional): Nombre del remoto que se usará para crear PRs, y taggear merges - **Remoto** (opcional): Nombre del remoto que se usará para crear PRs, y taggear merges
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 - [ ] Github: planeado
La configuración del remoto requiere tener un API Token para poder crear los PRs automáticamente. La configuración del remoto requiere tener un API Token para poder crear los PRs automáticamente.
El mismo se puede crear desde: *"Atlassian Account Settings" > "Security" > "Create and Manage API El mismo se puede crear desde: *"Atlassian Account Settings" > "Security" > "Create and Manage API
Tokens" > "Create API Tokens with scopes"*. El token necesita el scope `write:pullrequest:bitbucket` Tokens" > "Create API Tokens with scopes"*. El token necesita el scope `write:pullrequest:bitbucket`
- `write:repository:bitbucket` Luego se debe guardar el token dentro del repositorio a configurar, en el archivo
`.repository-token`. De no especificarse, los merges y tags se harán de forma local.
Luego se debe guardar el token dentro del repositorio a configurar, en el archivo `.repository-token`.
De no especificarse, los merges y tags se harán de forma local.
## Utilización ## Utilización
+6
View File
@@ -0,0 +1,6 @@
{
"exclude": [
"build",
"**/*.egg-info"
]
}
+13 -1
View File
@@ -18,7 +18,7 @@ COMMIT_TYPES = [
"docs", "docs",
"style", "style",
"test", "test",
"wip" "wip",
] ]
# Commits con estos valores incrementan el componente correspondiente # Commits con estos valores incrementan el componente correspondiente
@@ -46,3 +46,15 @@ SUPPORTED_REMOTE_APIS = [
] ]
REPOSITORY_TOKEN_FILENAME = ".repository-token" REPOSITORY_TOKEN_FILENAME = ".repository-token"
COLOR_BLACK = "\033[30m"
COLOR_RED = "\033[31m"
COLOR_GREEN = "\033[32m"
COLOR_YELLOW = "\033[33m"
COLOR_BLUE = "\033[34m"
COLOR_5 = "\033[35m"
COLOR_6 = "\033[36m"
COLOR_7 = "\033[37m"
COLOR_BLACK_BOLD = "\033[1;30m"
COLOR_WHITE_BOLD = "\033[1;37m"
COLOR_RESET = "\033[0m"
+3 -4
View File
@@ -16,8 +16,6 @@ class Changelog:
return f"""## {title} - {now.strftime("%F")} return f"""## {title} - {now.strftime("%F")}
### Commits
""" """
def generate_content(self, base: str, branch: str): def generate_content(self, base: str, branch: str):
@@ -56,5 +54,6 @@ class Changelog:
if isfile(self.FILENAME): if isfile(self.FILENAME):
with open(self.FILENAME, "r") as f: with open(self.FILENAME, "r") as f:
content += f.read() content += f.read()
with open(self.FILENAME, "w") as f:
f.write(content) with open(self.FILENAME, "w") as f:
f.write(content)
+6 -12
View File
@@ -4,6 +4,11 @@ from os.path import isfile
from git_flow import ( from git_flow import (
BRANCH_TYPES, BRANCH_TYPES,
COLOR_BLUE,
COLOR_GREEN,
COLOR_RED,
COLOR_RESET,
COLOR_YELLOW,
REPOSITORY_TOKEN_FILENAME, REPOSITORY_TOKEN_FILENAME,
GitFlowError, GitFlowError,
) )
@@ -12,17 +17,6 @@ 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
COLOR_BLACK = "\033[30m"
COLOR_RED = "\033[31m"
COLOR_GREEN = "\033[32m"
COLOR_YELLOW = "\033[33m"
COLOR_BLUE = "\033[34m"
COLOR_5 = "\033[35m"
COLOR_6 = "\033[36m"
COLOR_7 = "\033[37m"
COLOR_BLACK_BOLD = "\033[1;30m"
COLOR_RESET = "\033[0m"
TYPE_SUCCESS = 0 TYPE_SUCCESS = 0
TYPE_WARNING = 1 TYPE_WARNING = 1
TYPE_ERROR = 2 TYPE_ERROR = 2
@@ -176,7 +170,7 @@ class Command(ABC):
def get_branch_env_and_type(self, branch: str) -> tuple[str, str]: def get_branch_env_and_type(self, branch: str) -> tuple[str, str]:
components = branch.split("/") components = branch.split("/")
target_branches = self.flowconfig["flow.branches"].split(" ") target_branches = self.flowconfig["flow.branches"].split(",")
if len(components) == 2: if len(components) == 2:
if components[0] not in BRANCH_TYPES: if components[0] not in BRANCH_TYPES:
+89
View File
@@ -0,0 +1,89 @@
from argparse import ArgumentParser, Namespace
from git_flow import COLOR_WHITE_BOLD, COLOR_GREEN, COLOR_RED, COLOR_RESET, GitFlowError
from git_flow.command.base import Command
from git_flow.git import Git
BRANCH_FORMAT = "%(refname:short)"
class BranchCommand(Command):
def name(self) -> str:
return "branch"
def description(self) -> str:
return """Lista ramas del repositorio, agrupandolas por entorno objetivo"""
def setup_parser(self, parser: ArgumentParser) -> ArgumentParser:
parser.add_argument(
"environment",
nargs="?",
help="Entorno de las ramas a listar. Puede ser vacio para listar ramas de todos los entornos, un entorno válido, o 'trash' para listar las ramas eliminadas.",
)
return parser
def run(self, args: Namespace = Namespace()):
self.ensure_initialized()
self.current_branch = Git.get_current_branch()
self.environments = self.flowconfig["flow.branches"].split(",")
if args.environment is None:
self.show_envs()
self.show_all_branches()
elif args.environment in self.environments:
self.show_envs()
self.show_env_branches(args.environment)
elif args.environment == "trash":
self.show_trash_branches()
else:
raise GitFlowError(f"'{args.environment}' no es un entorno válido.")
def show_trash_branches(self):
branches = self.get_trash_branches()
if not branches:
print("No hay ramas a mostrar.")
return
for branch in branches:
prefix = (COLOR_RED + "*") if branch == self.current_branch else " "
print(prefix + " " + branch + COLOR_RESET)
def get_trash_branches(self):
return Git("for-each-ref", "refs/heads/trash/", format=BRANCH_FORMAT).lines()
def show_all_branches(self):
for environment in self.environments:
self.show_env_branches(environment, len(self.environments) > 1)
trash = len(self.get_trash_branches())
if trash:
print()
print(COLOR_WHITE_BOLD + "Papelera: " + COLOR_RESET + str(trash) + " rama" + ("" if trash == 1 else "s"))
def show_envs(self):
print(COLOR_WHITE_BOLD + "Entornos" + COLOR_RESET)
for environment in self.environments:
prefix = (COLOR_GREEN + "*") if environment == self.current_branch else " "
print(prefix + " " + environment + COLOR_RESET)
print()
def show_env_branches(self, environment: str, show_env: bool = False):
is_first_env = environment == self.environments[0]
pattern = "refs/heads/" if is_first_env else "refs/heads/release/" + environment + "/"
pattern += "*/*"
exclude = ["refs/heads/trash/", "refs/heads/release/"] if is_first_env else []
branches = Git("for-each-ref", pattern, exclude=exclude, format=BRANCH_FORMAT).lines()
print(COLOR_WHITE_BOLD + (environment if show_env else "Ramas") + COLOR_RESET)
if not branches:
print("No hay ramas a mostrar.")
return
for branch in branches:
prefix = (COLOR_GREEN + "*") if branch == self.current_branch else " "
print(prefix + " " + branch + COLOR_RESET)
-1
View File
@@ -1,5 +1,4 @@
from argparse import Namespace from argparse import Namespace
import os
from git_flow import GitFlowError from git_flow import GitFlowError
from git_flow.command.base import Command from git_flow.command.base import Command
from git_flow.git import FLOWCONFIG_FILE, Git from git_flow.git import FLOWCONFIG_FILE, Git
+15 -12
View File
@@ -17,16 +17,15 @@ class MergeCommand(Command):
branch = self.ensure_right_branch() branch = self.ensure_right_branch()
(target, _) = self.get_branch_env_and_type(branch) (target, _) = self.get_branch_env_and_type(branch)
base = Git.get_first_fork_point(branch, target)
if "flow.remote" in self.flowconfig: if "flow.remote" in self.flowconfig:
remote = self.flowconfig["flow.remote"] remote = self.flowconfig["flow.remote"]
token = self.ensure_repository_token() token = self.ensure_repository_token()
self.run_remote(remote, token, base, branch, target) self.run_remote(remote, token, branch, target)
else: else:
self.run_local(base, branch, target) self.run_local(branch, target)
def run_remote(self, remote: str, token: str, base: str, branch: str, target: str): def run_remote(self, remote: str, token: str, branch: str, target: str):
self.ensure_clean_worktree(True) self.ensure_clean_worktree(True)
Git("switch", target).exec(print="Cambiando a rama destino") Git("switch", target).exec(print="Cambiando a rama destino")
@@ -34,26 +33,26 @@ class MergeCommand(Command):
Git("switch", "-").exec(print="Volviendo a rama a mergear") Git("switch", "-").exec(print="Volviendo a rama a mergear")
self.check_merge_conflicts(target) self.check_merge_conflicts(target)
self.show_commits_to_merge(base) self.show_commits_to_merge(target)
if self.confirm(f"¿Crear PR de '{branch}' a '{target}'?"): if self.confirm(f"¿Crear PR de '{branch}' a '{target}'?"):
Git("push", remote, branch, set_upstream=True).exec( Git("push", remote, branch, set_upstream=True).exec(
print="Subiendo rama al remoto para crear PR" print="Subiendo rama al remoto para crear PR"
) )
self.create_pull_request(token, base, branch, target) self.create_pull_request(token, branch, target)
def run_local(self, base: str, branch: str, target: str): def run_local(self, branch: str, target: str):
self.ensure_clean_worktree(False) self.ensure_clean_worktree(False)
self.check_merge_conflicts(target) self.check_merge_conflicts(target)
self.show_commits_to_merge(base) self.show_commits_to_merge(target)
if self.confirm(f"¿Mergear rama '{branch}' a '{target}'?"): if self.confirm(f"¿Mergear rama '{branch}' a '{target}'?"):
Git("switch", target).exec(print="Cambiando a rama destino") Git("switch", target).exec(print="Cambiando a rama destino")
Git("merge", branch, ff=False).exec(print="Mergeando") Git("merge", branch, ff=False).exec(print="Mergeando")
def show_commits_to_merge(self, base): def show_commits_to_merge(self, target):
commits = Git("log", base + "..", format="%s").lines() commits = Git("log", target + "..", format="%s").lines()
if not commits: if not commits:
raise GitFlowError("No hay cambios a mergear.") raise GitFlowError("No hay cambios a mergear.")
@@ -79,14 +78,18 @@ class MergeCommand(Command):
else: else:
self.success("No se detectaron merge conflicts.") self.success("No se detectaron merge conflicts.")
def create_pull_request(self, token: str, base: str, branch: str, target: str): def create_pull_request(self, token: str, branch: str, target: str):
changelog = Changelog() changelog = Changelog()
title = branch.replace("/", ": ").replace("-", " ")
if title.startswith("release: "):
title = title[title.index(' ', title.index(' ') + 1) + 1:]
message = self.get_remote_api(token).create_pull_request( message = self.get_remote_api(token).create_pull_request(
branch, branch,
target, target,
branch.replace("/", ": ").replace("-", " "), branch.replace("/", ": ").replace("-", " "),
changelog.generate_content(base, branch), changelog.generate_content(target, branch),
) )
self.success(message) self.success(message)
+43 -16
View File
@@ -1,4 +1,4 @@
from argparse import Namespace from argparse import ArgumentParser, Namespace
from git_flow import COMMIT_TYPES, GitFlowError from git_flow import COMMIT_TYPES, GitFlowError
from git_flow.command.base import Command from git_flow.command.base import Command
from git_flow.git import Git from git_flow.git import Git
@@ -11,6 +11,15 @@ class ReleaseCommand(Command):
def description(self) -> str: def description(self) -> str:
return """Realiza el merge de la rama, o crea el PR para hacerlo si tiene un remoto configurado""" return """Realiza el merge de la rama, o crea el PR para hacerlo si tiene un remoto configurado"""
def setup_parser(self, parser: ArgumentParser) -> ArgumentParser:
parser.add_argument(
"--group",
nargs="?",
help="Agrupar release",
)
return parser
def run(self, args: Namespace = Namespace()): def run(self, args: Namespace = Namespace()):
self.ensure_initialized() self.ensure_initialized()
@@ -34,34 +43,52 @@ class ReleaseCommand(Command):
else f"release/{next_env}/{branch}" else f"release/{next_env}/{branch}"
) )
base = Git.get_first_fork_point(branch, env)
commits = Git("log", base + ".." , format="%s").lines()
if not commits:
raise GitFlowError("No hay cambios a mergear.")
print("Cambios a pasar al proximo entorno:")
for commit in commits:
print("- " + commit)
group = args.group
if not group:
if self.confirm("¿Desea agrupar este release con otra rama?", False):
group = self.choice("Grupo release: ", Git.get_branches("release/" + next_env + "/"))
else:
group = next_env
if has_remote: if has_remote:
Git("pull").exec(print="Sincronizando cambios de la rama") Git("switch", group).exec(print="Cambiando a la rama objetivo")
Git("switch", next_env).exec(print="Cambiando al siguiente entorno") Git("pull").exec(print="Sincronizando cambios la rama objetivo")
Git("pull").exec(print="Sincronizando cambios del entorno")
Git("switch", "-").exec(print="Volviendo a la rama original") Git("switch", "-").exec(print="Volviendo a la rama original")
base = Git.get_first_fork_point(branch, env) if len(commits) > 1 and self.confirm(
commits = int(Git("rev-list", base + "..", count=True).firstline()) f"¿Desea reemplazar los {len(commits)} commits de la rama por uno solo?",
False
if commits > 1 and self.confirm(
"Desea reemplazar los {commits} commits de la rama por uno solo?"
): ):
Git("switch", next_branch, base, create=True).exec( self.info("Debe ingresar el mensaje del commit a crear.")
print="Creando rama release en base"
)
Git("merge", branch, squash=True).exec(
print="Squasheando commits en uno solo"
)
commit_type = self.choice("Tipo de commit", COMMIT_TYPES[:-1]) commit_type = self.choice("Tipo de commit", COMMIT_TYPES[:-1])
commit_message = self.prompt( commit_message = self.prompt(
"Mensaje (máximo recomendado: 100 caracteres)", persistent=True "Mensaje (máximo recomendado: 100 caracteres)", persistent=True
) )
message = commit_type + ": " + commit_message message = commit_type + ": " + commit_message
Git("switch", next_branch, base, create=True).exec(
print="Creando rama release en base"
)
Git("merge", branch, squash=True).exec(
print="Squasheando commits en uno solo"
)
Git("commit", m=message).exec(print="Creando commit único") Git("commit", m=message).exec(print="Creando commit único")
else: else:
Git("switch", next_branch, create=True).exec(print="Creando rama release") Git("switch", next_branch, create=True).exec(print="Creando rama release")
Git("rebase", base, next_branch, onto=next_env).exec( Git("rebase", base, next_branch, onto=group).exec(
print="Moviendo cambios hacia el siguiente ambiente" print="Moviendo cambios hacia el siguiente ambiente"
) )
+2 -2
View File
@@ -56,7 +56,7 @@ class TagCommand(Command):
def get_token_and_ci(self, args: Namespace): def get_token_and_ci(self, args: Namespace):
try: try:
return (self.ensure_repository_token(), False) return (self.ensure_repository_token(), False)
except GitFlowError as e: except GitFlowError:
return (args.token, True) return (args.token, True)
def setup_parser(self, parser: ArgumentParser) -> ArgumentParser: def setup_parser(self, parser: ArgumentParser) -> ArgumentParser:
@@ -91,7 +91,7 @@ class TagCommand(Command):
tag = Git.get_current_tag() or f"v0.0.0-{branch}" tag = Git.get_current_tag() or f"v0.0.0-{branch}"
until_dash = tag.index("-") if "-" in tag else None until_dash = tag.index("-") if "-" in tag else None
suffix = tag[until_dash + 1 :] if until_dash is not None else "" suffix = tag[until_dash:] if until_dash is not None else ""
[major, minor, patch] = map(int, tag[1:until_dash].split(".")) [major, minor, patch] = map(int, tag[1:until_dash].split("."))
if increment == SEMVER_MAJOR: if increment == SEMVER_MAJOR:
+13 -19
View File
@@ -38,7 +38,7 @@ class Git:
@staticmethod @staticmethod
def set_config(config: dict[str, str], file: str | None = None): def set_config(config: dict[str, str], file: str | None = None):
for key, value in config: for key, value in config.items():
Git("config", key, value, file=file).exec() Git("config", key, value, file=file).exec()
@staticmethod @staticmethod
@@ -50,8 +50,8 @@ class Git:
return Git("rev-parse", "HEAD", abbrev_ref=True).firstline() return Git("rev-parse", "HEAD", abbrev_ref=True).firstline()
@staticmethod @staticmethod
def get_branches(): def get_branches(prefix: str = ""):
return Git._get_references("heads") return Git._get_references("heads/" + prefix)
@staticmethod @staticmethod
def status(): def status():
@@ -63,33 +63,24 @@ class Git:
@staticmethod @staticmethod
def get_first_fork_point(branch: str, env: str): def get_first_fork_point(branch: str, env: str):
boundary_commits = list( # Lista de commits alcanzables por el entorno (por ejemplo, dev),
filter( env_commits = Git("rev-list", env + "..." + branch, first_parent=True).lines()
lambda c: c.startswith("-"), # boundary commits
Git(
"rev-list", env + "..." + branch, topo_order=True, boundary=True
).lines(),
)
)
if not boundary_commits: if not env_commits:
raise RuntimeError("No se encontro un commit base para realizar el rebase.") raise RuntimeError("No se encontro un commit base para realizar el rebase.")
return boundary_commits[-1].removeprefix("-") return Git("show", env_commits[-1] + "^", patch=False, format="%h").firstline()
@staticmethod @staticmethod
def _get_references(kind: str): def _get_references(kind: str):
prefix = "refs/" + kind + "/" prefix = "refs/" + kind
return list(map( return Git("for-each-ref", prefix + "**", format="%(refname:short)").lines()
lambda r: r.removeprefix(prefix),
Git("for-each-ref", prefix + "*", format="%(refname)").lines(),
))
@staticmethod @staticmethod
def is_repository() -> bool: def is_repository() -> bool:
return Git("rev-parse", is_inside_work_tree=True).code() == 0 return Git("rev-parse", is_inside_work_tree=True).code() == 0
def __init__(self, subcommand: str, *args: str, **kwargs: str | int | bool | None) -> None: def __init__(self, subcommand: str, *args: str, **kwargs: str | int | bool | list[str] | None) -> None:
self.command = ["git", subcommand] self.command = ["git", subcommand]
for option, value in kwargs.items(): for option, value in kwargs.items():
@@ -103,6 +94,9 @@ class Git:
self.command.append(prefix + option) self.command.append(prefix + option)
elif value is None: elif value is None:
continue continue
elif isinstance(value, list):
for v in value:
self.command.append("--" + option + "=" + v)
else: else:
value = value if isinstance(value, str) else str(value) value = value if isinstance(value, str) else str(value)
+1 -9
View File
@@ -1,14 +1,6 @@
import sys import sys
from git_flow import COLOR_BLACK_BOLD, COLOR_BLUE, COLOR_GREEN, COLOR_RED, COLOR_RESET, COLOR_YELLOW
COLOR_BLACK = '\033[30m'
COLOR_RED = '\033[31m'
COLOR_GREEN = '\033[32m'
COLOR_YELLOW = '\033[33m'
COLOR_BLUE = '\033[34m'
COLOR_BLACK_BOLD = '\033[1;30m'
COLOR_RESET = '\033[0m'
def confirm(prompt, default: bool = True): def confirm(prompt, default: bool = True):
user_input = input(prompt + (" [Y/n]: " if default else " [y/N]: ")) user_input = input(prompt + (" [Y/n]: " if default else " [y/N]: "))
+2
View File
@@ -5,6 +5,7 @@ from argparse import Namespace, ArgumentParser
from git_flow import GitFlowError from git_flow import GitFlowError
from git_flow.command.base import Command from git_flow.command.base import Command
from git_flow.command.branch import BranchCommand
from git_flow.command.commit import CommitCommand from git_flow.command.commit import CommitCommand
from git_flow.command.init import InitCommand from git_flow.command.init import InitCommand
from git_flow.command.merge import MergeCommand from git_flow.command.merge import MergeCommand
@@ -70,6 +71,7 @@ def main():
MergeCommand(), MergeCommand(),
TagCommand(), TagCommand(),
ReleaseCommand(), ReleaseCommand(),
BranchCommand()
) )
try: try:
command.run() command.run()