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
This commit is contained in:
182
internal/validation/validation.go
Normal file
182
internal/validation/validation.go
Normal file
@@ -0,0 +1,182 @@
|
||||
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
|
||||
}
|
Reference in New Issue
Block a user