Files
Dev 15bbfdcda2 feat: initial commit of Git Automation CLI
- Add comprehensive Git workflow automation tools
- Include branch management utilities
- Add commit helpers with conventional commit support
- Implement GitHub integration for PR management
- Add configuration management system
- Include comprehensive test coverage
- Add professional documentation and examples
2025-09-11 17:02:12 +03:00

182 lines
5.0 KiB
Go

package validation
import (
"context"
"fmt"
"regexp"
"strings"
"github.com/iwasforcedtobehere/git-automation-cli/internal/git"
)
// ValidationResult represents the result of a validation
type ValidationResult struct {
IsValid bool
Errors []string
}
// NewValidationResult creates a new validation result
func NewValidationResult() *ValidationResult {
return &ValidationResult{
IsValid: true,
Errors: make([]string, 0),
}
}
// AddError adds an error to the validation result
func (vr *ValidationResult) AddError(format string, args ...interface{}) {
vr.IsValid = false
vr.Errors = append(vr.Errors, fmt.Sprintf(format, args...))
}
// GetErrors returns all validation errors as a single string
func (vr *ValidationResult) GetErrors() string {
return strings.Join(vr.Errors, "\n")
}
// ValidateGitRepository checks if the current directory is a Git repository
func ValidateGitRepository(ctx context.Context) *ValidationResult {
result := NewValidationResult()
if _, err := git.CurrentBranch(ctx); err != nil {
result.AddError("not a git repository or no branch found")
}
return result
}
// ValidateBranchName checks if a branch name is valid
func ValidateBranchName(name string) *ValidationResult {
result := NewValidationResult()
// Check if branch name is empty
if name == "" {
result.AddError("branch name cannot be empty")
return result
}
// Check for invalid characters
if strings.Contains(name, "..") || strings.Contains(name, " ") ||
strings.Contains(name, ":") || strings.Contains(name, "?") ||
strings.Contains(name, "*") || strings.Contains(name, "[") ||
strings.Contains(name, "@") || strings.Contains(name, "\\") {
result.AddError("branch name contains invalid characters")
}
// Check if branch name starts with a dot
if strings.HasPrefix(name, ".") {
result.AddError("branch name cannot start with a dot")
}
// Check if branch name ends with a slash
if strings.HasSuffix(name, "/") {
result.AddError("branch name cannot end with a slash")
}
// Check for consecutive slashes
if strings.Contains(name, "//") {
result.AddError("branch name cannot contain consecutive slashes")
}
// Check if branch name is a valid refname
// Git refname rules: must contain at least one /
if !strings.Contains(name, "/") && name != "HEAD" &&
name != "main" && name != "master" {
// This is not necessarily an error, but a warning
// We'll allow it but log a warning
}
return result
}
// ValidateCommitMessage checks if a commit message is valid
func ValidateCommitMessage(message string) *ValidationResult {
result := NewValidationResult()
// Check if commit message is empty
if strings.TrimSpace(message) == "" {
result.AddError("commit message cannot be empty")
return result
}
// Check if commit message is too long
if len(message) > 72 {
result.AddError("commit message should be 72 characters or less")
}
// Check if commit message starts with a whitespace
if strings.HasPrefix(message, " ") {
result.AddError("commit message should not start with whitespace")
}
return result
}
// ValidateConventionalCommit checks if a commit message follows the conventional commit format
func ValidateConventionalCommit(message string) *ValidationResult {
result := NewValidationResult()
// Conventional commit format: type(scope): description
// Example: feat(auth): add login functionality
// Check if commit message is empty
if strings.TrimSpace(message) == "" {
result.AddError("commit message cannot be empty")
return result
}
// Regex pattern for conventional commit
pattern := `^(\w+)(\([^)]+\))?(!)?:\s.+`
matched, err := regexp.MatchString(pattern, message)
if err != nil {
result.AddError("error validating conventional commit format: %v", err)
return result
}
if !matched {
result.AddError("commit message does not follow conventional commit format")
result.AddError("expected format: type(scope): description")
result.AddError("example: feat(auth): add login functionality")
}
return result
}
// ValidateGitHubToken checks if a GitHub token is valid
func ValidateGitHubToken(token string) *ValidationResult {
result := NewValidationResult()
// Check if token is empty
if token == "" {
result.AddError("GitHub token cannot be empty")
return result
}
// Check if token starts with "ghp_" (personal access token) or "gho_" (OAuth token)
if !strings.HasPrefix(token, "ghp_") && !strings.HasPrefix(token, "gho_") {
result.AddError("GitHub token should start with 'ghp_' or 'gho_'")
}
// Check token length (GitHub tokens are typically 40 characters long)
if len(token) != 40 {
result.AddError("GitHub token should be 40 characters long")
}
return result
}
// ValidateWorkingDirectory checks if the working directory is clean
func ValidateWorkingDirectory(ctx context.Context) *ValidationResult {
result := NewValidationResult()
clean, err := git.IsCleanWorkingDir(ctx)
if err != nil {
result.AddError("failed to check working directory: %v", err)
return result
}
if clean {
result.AddError("no changes to commit")
}
return result
}