Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 1 addition & 15 deletions .goreleaser.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,20 +21,6 @@ builds:
- -X main.commit={{.Commit}}
- -X main.date={{.Date}}

archives:
- id: default
format: tar.gz
files:
- locales/**
name_template: >-
{{ .ProjectName }}_{{ .Version }}_
{{- title .Os }}_
{{- if eq .Arch "amd64" }}x86_64
{{- else }}{{ .Arch }}{{ end }}
format_overrides:
- goos: windows
format: zip

checksum:
name_template: 'checksums.txt'

Expand All @@ -47,4 +33,4 @@ changelog:
- Merge pull request
- Merge branch

version: 2
version: 2
2 changes: 1 addition & 1 deletion cmd/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@

ticketService := jira.NewJiraService(cfgApp, &http.Client{})

commitService := services.NewCommitService(gitService, aiProvider, ticketService, cfgApp)
commitService := services.NewCommitService(gitService, aiProvider, ticketService, cfgApp, translations)

Check warning on line 69 in cmd/main.go

View check run for this annotation

Codecov / codecov/patch

cmd/main.go#L69

Added line #L69 was not covered by tests

commitHandler := handler.NewSuggestionHandler(gitService, translations)

Expand Down
19 changes: 18 additions & 1 deletion internal/i18n/locales/active.en.toml
Original file line number Diff line number Diff line change
Expand Up @@ -167,6 +167,9 @@ missing_criteria_none = "✅ Missing Criteria: None"
pr_title_section = "PR Title"
pr_labels_section = "Suggested Tags"
pr_changes_section = "Key Changes"
error_gemini_client = "Error creating Gemini client: {{.Error}}"
response_empty = "Empty AI response"
title_not_found = "The title was not found in the answer"

[suggestion_header]
other = "=========[ Suggestion {{.Number}} ]========="
Expand Down Expand Up @@ -230,4 +233,18 @@ config_set_vcs_repo_usage = "Name of the repository"
config_vcs_updated = "VCS provider '{{.Provider}}' configuration updated successfully"
config_set_active_vcs_usage = "Set the active VCS provider"
config_set_active_vcs_provider_usage = "Name of the VCS provider to set as active"
config_active_vcs_updated = "Active VCS provider set to '{{.Provider}}'"
config_active_vcs_updated = "Active VCS provider set to '{{.Provider}}'"

[commit_service]
undetected_changes = "No changes detected"
error_getting_diff = "Error getting changes: {{.Error}}"
no_differences_detected = "No differences were detected in the files"
error_get_id_ticket = "Error getting ticket ID: {{.Error}}"
error_get_ticket_info = "Error obtaining ticket information: {{.Error}}"
ticket_id_not_found_branch = "No ticket ID found in the branch name"
error_get_name_from_branch = "Error getting branch name: {{.Error}}"

[pr_service]
error_get_pr = "Error getting the PR: {{.Error}}"
error_create_summary_pr = "Error creating PR summary: {{.Error}}"
error_update_pr = "Error updating PR: {{.Error}}"
19 changes: 18 additions & 1 deletion internal/i18n/locales/active.es.toml
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ technical_analysis_section = "💭 Analisis Tecnico:"
pr_title_section = "Titulo del PR"
pr_labels_section = "Etiquetas sugeridas"
pr_changes_section = "Cambios Clave"
error_gemini_client = "Error al crear el cliente de Gemini: {{.Error}}"
response_empty = "Respuesta de la IA vacia"
title_not_found = "No se encontro el titulo en la respuesta"

[suggestion_header]
other = "=========[ Sugerencias {{.Number}} ]========="
Expand Down Expand Up @@ -237,4 +240,18 @@ config_set_vcs_repo_usage = "Nombre del repositorio"
config_vcs_updated = "Configuración del proveedor VCS '{{.Provider}}' actualizada correctamente"
config_set_active_vcs_usage = "Establecer el proveedor VCS activo"
config_set_active_vcs_provider_usage = "Nombre del proveedor VCS a establecer como activo"
config_active_vcs_updated = "Proveedor VCS activo establecido a '{{.Provider}}'"
config_active_vcs_updated = "Proveedor VCS activo establecido a '{{.Provider}}'"

[commit_service]
undetected_changes = "No hay cambios detectados"
error_getting_diff = "Error al obtener los cambios: {{.Error}}"
no_differences_detected = "No se detectaron diferencias en los archivos"
error_get_id_ticket = "Error al obtener el ID del ticket: {{.Error}}"
error_get_ticket_info = "Error al obtener informacion del ticket: {{.Error}}"
ticket_id_not_found_branch = "No se encontro un ID de ticket en el nombre de la branch"
error_get_name_from_branch = "Error al obtener el nombre de la branch: {{.Error}}"

[pr_service]
error_get_pr = "Error al obtener el PR: {{.Error}}"
error_create_summary_pr = "Error al crear el resumen del PR: {{.Error}}"
error_update_pr = "Error al actualizar el PR: {{.Error}}"
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,10 @@

client, err := genai.NewClient(ctx, option.WithAPIKey(cfg.GeminiAPIKey))
if err != nil {
return nil, fmt.Errorf("failed to create Gemini client: %w", err)
msg := trans.GetMessage("error_gemini_client", 0, map[string]interface{}{
"Error": err,
})
return nil, fmt.Errorf("%s", msg)

Check warning on line 34 in internal/infrastructure/ai/gemini/gemini_pr_summarizer_service.go

View check run for this annotation

Codecov / codecov/patch

internal/infrastructure/ai/gemini/gemini_pr_summarizer_service.go#L31-L34

Added lines #L31 - L34 were not covered by tests
}

modelName := string(cfg.AIConfig.Models[config.AIGemini])
Expand Down Expand Up @@ -57,7 +60,8 @@

rawSummary := formatResponse(resp)
if rawSummary == "" {
return models.PRSummary{}, fmt.Errorf("respuesta vacía de la IA")
msg := gps.trans.GetMessage("gemini_service.response_empty", 0, nil)
return models.PRSummary{}, fmt.Errorf("%s", msg)

Check warning on line 64 in internal/infrastructure/ai/gemini/gemini_pr_summarizer_service.go

View check run for this annotation

Codecov / codecov/patch

internal/infrastructure/ai/gemini/gemini_pr_summarizer_service.go#L63-L64

Added lines #L63 - L64 were not covered by tests
}

return gps.parseSummary(rawSummary)
Expand Down Expand Up @@ -113,7 +117,8 @@
summary.Body = strings.Join(bodyParts, "\n\n")

if summary.Title == "" {
return summary, fmt.Errorf("no se encontró el título en la respuesta")
msg := gps.trans.GetMessage("title_not_found", 0, nil)
return summary, fmt.Errorf("%s", msg)

Check warning on line 121 in internal/infrastructure/ai/gemini/gemini_pr_summarizer_service.go

View check run for this annotation

Codecov / codecov/patch

internal/infrastructure/ai/gemini/gemini_pr_summarizer_service.go#L120-L121

Added lines #L120 - L121 were not covered by tests
}

if utf8.RuneCountInString(summary.Title) > 80 {
Expand Down
30 changes: 10 additions & 20 deletions internal/infrastructure/ai/gemini/gemini_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,10 @@
}
client, err := genai.NewClient(ctx, option.WithAPIKey(cfg.GeminiAPIKey))
if err != nil {
return nil, fmt.Errorf("failed to create Gemini client: %w", err)
msg := trans.GetMessage("error_gemini_client", 0, map[string]interface{}{
"Error": err,
})
return nil, fmt.Errorf("%s", msg)

Check warning on line 33 in internal/infrastructure/ai/gemini/gemini_service.go

View check run for this annotation

Codecov / codecov/patch

internal/infrastructure/ai/gemini/gemini_service.go#L30-L33

Added lines #L30 - L33 were not covered by tests
}

modelName := string(cfg.AIConfig.Models[config.AIGemini])
Expand Down Expand Up @@ -73,7 +76,6 @@
func (s *GeminiService) generatePrompt(locale string, info models.CommitInfo, count int) string {
promptTemplate := ai.GetCommitPromptTemplate(locale, info.TicketInfo != nil && info.TicketInfo.TicketTitle != "")

// Preparar la información del ticket si existe
ticketInfo := ""
if info.TicketInfo != nil && info.TicketInfo.TicketTitle != "" {
ticketInfo = fmt.Sprintf("\nTicket Title: %s\nTicket Description: %s\nAcceptance Criteria: %s",
Expand All @@ -82,13 +84,12 @@
strings.Join(info.TicketInfo.Criteria, ", "))
}

// El orden de los argumentos debe coincidir con los placeholders en el template
return fmt.Sprintf(promptTemplate,
count, // Primer %d
count, // Segundo %d
formatChanges(info.Files), // %s para archivos modificados
info.Diff, // %s para el diff
ticketInfo, // %s para la información del ticket
count,
count,
formatChanges(info.Files),
info.Diff,
ticketInfo,
)
}

Expand Down Expand Up @@ -139,7 +140,7 @@

delimiter := s.getSuggestionDelimiter()
re := regexp.MustCompile(delimiter)
parts := re.Split(responseText, -1) // Dividir usando regex
parts := re.Split(responseText, -1)
suggestions := make([]models.CommitSuggestion, 0)

for _, part := range parts {
Expand Down Expand Up @@ -168,15 +169,13 @@
RequirementsAnalysis: models.RequirementsAnalysis{},
}

// Extraer el título del commit
for _, line := range lines {
if strings.HasPrefix(line, "Commit:") {
suggestion.CommitTitle = strings.TrimSpace(strings.TrimPrefix(line, "Commit:"))
break
}
}

// Extraer los archivos modificados
var collectingFiles bool
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)
Expand All @@ -196,7 +195,6 @@
}
}

// Extraer la explicación
var explanation strings.Builder
for _, line := range lines {
if strings.HasPrefix(line, s.trans.GetMessage("gemini_service.explanation_prefix", 0, nil)) {
Expand All @@ -206,7 +204,6 @@
}
suggestion.Explanation = strings.TrimSpace(explanation.String())

// Extraer el análisis de código
for i, line := range lines {
if strings.HasPrefix(line, s.trans.GetMessage("gemini_service.code_analysis_prefix", 0, nil)) {
if i+1 < len(lines) && strings.HasPrefix(lines[i+1], s.trans.GetMessage("gemini_service.changes_overview_prefix", 0, nil)) {
Expand All @@ -222,7 +219,6 @@
}
}

// Extraer el análisis de requisitos
var (
collectingMissingCriteria bool
collectingImprovements bool
Expand All @@ -231,7 +227,6 @@
for _, line := range lines {
trimmedLine := strings.TrimSpace(line)

// Procesar estado de criterios
if strings.HasPrefix(trimmedLine, "⚠️") {
switch {
case strings.Contains(trimmedLine, s.trans.GetMessage("gemini_service.criteria_fully_met_prefix", 0, nil)):
Expand All @@ -244,21 +239,18 @@
continue
}

// Procesar criterios faltantes
if strings.HasPrefix(trimmedLine, "❌") {
collectingMissingCriteria = true
collectingImprovements = false
continue
}

// Procesar sugerencias de mejora
if strings.HasPrefix(trimmedLine, "💡") {
collectingMissingCriteria = false
collectingImprovements = true
continue
}

// Recolectar criterios faltantes
if collectingMissingCriteria && strings.HasPrefix(trimmedLine, "-") {
criteria := strings.TrimSpace(strings.TrimPrefix(trimmedLine, "-"))
suggestion.RequirementsAnalysis.MissingCriteria = append(
Expand All @@ -267,7 +259,6 @@
)
}

// Recolectar sugerencias de mejora
if collectingImprovements && strings.HasPrefix(trimmedLine, "-") {
improvement := strings.TrimSpace(strings.TrimPrefix(trimmedLine, "-"))
suggestion.RequirementsAnalysis.ImprovementSuggestions = append(
Expand All @@ -276,7 +267,6 @@
)
}

// Detener la recolección si encontramos una línea vacía o un nuevo encabezado
if trimmedLine == "" || strings.HasPrefix(trimmedLine, "📊") ||
strings.HasPrefix(trimmedLine, "📝") {
collectingMissingCriteria = false
Expand Down
2 changes: 1 addition & 1 deletion internal/infrastructure/factory/pr_service_factory.go
Original file line number Diff line number Diff line change
Expand Up @@ -43,5 +43,5 @@
return nil, fmt.Errorf("proveedor de VCS no compatible: %s", vcsConfig.Provider)
}

return services.NewPRService(vcsClient, f.aiService), nil
return services.NewPRService(vcsClient, f.aiService, f.trans), nil

Check warning on line 46 in internal/infrastructure/factory/pr_service_factory.go

View check run for this annotation

Codecov / codecov/patch

internal/infrastructure/factory/pr_service_factory.go#L46

Added line #L46 was not covered by tests
}
55 changes: 35 additions & 20 deletions internal/services/commit_service.go
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,56 @@

import (
"context"
"errors"
"fmt"
"github.com/Tomas-vilte/MateCommit/internal/config"
"github.com/Tomas-vilte/MateCommit/internal/domain/models"
"github.com/Tomas-vilte/MateCommit/internal/domain/ports"
"github.com/Tomas-vilte/MateCommit/internal/i18n"
"regexp"
)

type CommitService struct {
git ports.GitService
ai ports.AIProvider
jiraService ports.TickerManager
config *config.Config
git ports.GitService
ai ports.AIProvider
ticketManager ports.TickerManager
config *config.Config
trans *i18n.Translations
}

func NewCommitService(git ports.GitService, ai ports.AIProvider, jiraService ports.TickerManager, cfg *config.Config) *CommitService {
func NewCommitService(git ports.GitService, ai ports.AIProvider, ticketManager ports.TickerManager, cfg *config.Config, trans *i18n.Translations) *CommitService {
return &CommitService{
git: git,
ai: ai,
jiraService: jiraService,
config: cfg,
git: git,
ai: ai,
ticketManager: ticketManager,
config: cfg,
trans: trans,
}
}

func (s *CommitService) GenerateSuggestions(ctx context.Context, count int) ([]models.CommitSuggestion, error) {
var commitInfo models.CommitInfo

// Obtener los cambios en el código
changes, err := s.git.GetChangedFiles()
if err != nil {
return nil, err
}

if len(changes) == 0 {
return nil, fmt.Errorf("no hay cambios detectados")
msg := s.trans.GetMessage("commit_service.undetected_changes", 0, nil)
return nil, fmt.Errorf("%s", msg)
}

diff, err := s.git.GetDiff()
if err != nil {
return nil, fmt.Errorf("error al obtener el diff: %v", err)
msg := s.trans.GetMessage("commit_service.error_getting_diff", 0, map[string]interface{}{
"Error": err,
})
return nil, fmt.Errorf("%s", msg)
}

if diff == "" {
return nil, errors.New("no se detectaron diferencias en los archivos")
msg := s.trans.GetMessage("commit_service.no_differences_detected", 0, nil)
return nil, fmt.Errorf("%s", msg)

Check warning on line 54 in internal/services/commit_service.go

View check run for this annotation

Codecov / codecov/patch

internal/services/commit_service.go#L53-L54

Added lines #L53 - L54 were not covered by tests
}

files := make([]string, 0)
Expand All @@ -61,31 +67,40 @@
if s.config.UseTicket {
ticketID, err := s.getTicketIDFromBranch()
if err != nil {
return nil, fmt.Errorf("error al obtener el ID del ticket: %v", err)
msg := s.trans.GetMessage("commit_service.error_get_id_ticket", 0, map[string]interface{}{
"Error": err,
})
return nil, fmt.Errorf("%s", msg)
}

ticketInfo, err := s.jiraService.GetTicketInfo(ticketID)
ticketInfo, err := s.ticketManager.GetTicketInfo(ticketID)
if err != nil {
return nil, fmt.Errorf("error al obtener la información del ticket: %v", err)
msg := s.trans.GetMessage("commit_service.error_get_ticket_info", 0, map[string]interface{}{
"Error": err,
})
return nil, fmt.Errorf("%s", msg)

Check warning on line 81 in internal/services/commit_service.go

View check run for this annotation

Codecov / codecov/patch

internal/services/commit_service.go#L78-L81

Added lines #L78 - L81 were not covered by tests
}

commitInfo.TicketInfo = ticketInfo
}

// Generar sugerencias de commit usando la IA
return s.ai.GenerateSuggestions(ctx, commitInfo, count)
}

func (s *CommitService) getTicketIDFromBranch() (string, error) {
branchName, err := s.git.GetCurrentBranch()
if err != nil {
return "", fmt.Errorf("error al obtener el nombre de la branch: %v", err)
msg := s.trans.GetMessage("commit_service.error_get_name_from_branch", 0, map[string]interface{}{
"Error": err,
})
return "", fmt.Errorf("%s", msg)

Check warning on line 96 in internal/services/commit_service.go

View check run for this annotation

Codecov / codecov/patch

internal/services/commit_service.go#L93-L96

Added lines #L93 - L96 were not covered by tests
}

re := regexp.MustCompile(`([A-Za-z]+-\d+)`)
match := re.FindString(branchName)
if match == "" {
return "", fmt.Errorf("no se encontró un ID de ticket en el nombre de la branch")
msg := s.trans.GetMessage("commit_service.ticket_id_not_found_branch", 0, nil)
return "", fmt.Errorf("%s", msg)
}

return match, nil
Expand Down
Loading
Loading