mirror of
https://github.com/Slug-Boi/cocommit.git
synced 2026-05-13 20:55:47 +00:00
refactor: refactor whole project to based on cobra commands
This commit is contained in:
@@ -8,7 +8,10 @@ require (
|
|||||||
github.com/99designs/gqlgen v0.17.31 // indirect
|
github.com/99designs/gqlgen v0.17.31 // indirect
|
||||||
github.com/Khan/genqlient v0.6.0 // indirect
|
github.com/Khan/genqlient v0.6.0 // indirect
|
||||||
github.com/adrg/xdg v0.4.0 // indirect
|
github.com/adrg/xdg v0.4.0 // indirect
|
||||||
|
github.com/inconshreveable/mousetrap v1.1.0 // indirect
|
||||||
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
github.com/mitchellh/go-homedir v1.1.0 // indirect
|
||||||
|
github.com/spf13/cobra v1.8.1 // indirect
|
||||||
|
github.com/spf13/pflag v1.0.5 // indirect
|
||||||
github.com/vektah/gqlparser/v2 v2.5.6 // indirect
|
github.com/vektah/gqlparser/v2 v2.5.6 // indirect
|
||||||
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
golang.org/x/exp v0.0.0-20231110203233-9a3e6036ecaa // indirect
|
||||||
golang.org/x/sync v0.6.0 // indirect
|
golang.org/x/sync v0.6.0 // indirect
|
||||||
|
|||||||
@@ -0,0 +1,64 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"main/src_code/go_src/cmd/utils"
|
||||||
|
"os"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
// rootCmd represents the base command when called without any subcommands
|
||||||
|
var rootCmd = &cobra.Command{
|
||||||
|
Use: `cocommit <commit message> <co-author1> [co-author2] ... ||
|
||||||
|
cocommit <commit message> <co-author1:email> [co-author2:email] ... ||
|
||||||
|
cocommit <commit message> all ||
|
||||||
|
cocommit <commit message> ^<co-author1> ^[co-author2] ... ||
|
||||||
|
cocommit <commit message> <group> ||
|
||||||
|
cocommit users ||`,
|
||||||
|
DisableFlagsInUseLine: true,
|
||||||
|
Short: "A cli tool to help you add co-authors to your git commits",
|
||||||
|
Long: `A cli tool to help you add co-authors to your git commits`,
|
||||||
|
//TODO: add bubble tea interface to this
|
||||||
|
Args: cobra.MinimumNArgs(0),
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
// check if the print flag is set
|
||||||
|
pflag, _ := cmd.Flags().GetBool("print")
|
||||||
|
// run execute commands again as root run will not call this part
|
||||||
|
// redundant check for now but will be useful later when we add tui
|
||||||
|
if len(args) == 1 {
|
||||||
|
utils.GitWrapper(args[0])
|
||||||
|
if pflag {
|
||||||
|
fmt.Println(args[0])
|
||||||
|
}
|
||||||
|
os.Exit(0)
|
||||||
|
}
|
||||||
|
// builds the commit message with the selected authors
|
||||||
|
message := utils.Commit(args[0], args[1:])
|
||||||
|
// prints the commit message to the console if the print flag is set
|
||||||
|
if pflag {
|
||||||
|
fmt.Println(message)
|
||||||
|
}
|
||||||
|
// runs the git commit command
|
||||||
|
utils.GitWrapper(message)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execute adds all child commands to the root command and sets flags appropriately.
|
||||||
|
// This is called by main.main(). It only needs to happen once to the rootCmd.
|
||||||
|
func Execute() {
|
||||||
|
// author file check
|
||||||
|
author_file := utils.CheckAuthorFile()
|
||||||
|
// define users
|
||||||
|
utils.Define_users(author_file)
|
||||||
|
|
||||||
|
err := rootCmd.Execute()
|
||||||
|
if err != nil {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.Flags().BoolP("print", "p", false, "Prints the commit message to the console")
|
||||||
|
|
||||||
|
}
|
||||||
@@ -0,0 +1,39 @@
|
|||||||
|
package cmd
|
||||||
|
|
||||||
|
import (
|
||||||
|
"main/src_code/go_src/cmd/utils"
|
||||||
|
"os"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
)
|
||||||
|
|
||||||
|
var authorfile = utils.Find_authorfile()
|
||||||
|
|
||||||
|
// usersCmd represents the users command
|
||||||
|
var usersCmd = &cobra.Command{
|
||||||
|
Use: "users",
|
||||||
|
Short: "Displays all users from the author file located at: " + authorfile,
|
||||||
|
Long: `Displays all users from the author file located at: ` + authorfile,
|
||||||
|
Run: func(cmd *cobra.Command, args []string) {
|
||||||
|
//TODO: make this print a bit prettier (sort it and maybe use a table)
|
||||||
|
println("List of users:\nFormat: <shortname>/<name> -> Username: <username> Email: <email>")
|
||||||
|
seen_users := []utils.User{}
|
||||||
|
user_sb := []string{}
|
||||||
|
for name, usr := range utils.Users {
|
||||||
|
if !slices.Contains(seen_users, usr) {
|
||||||
|
user_sb = append(user_sb, utils.Users[name].Names+" ->"+" Username: "+usr.Username+" Email: "+usr.Email+"\n")
|
||||||
|
seen_users = append(seen_users, usr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(user_sb)
|
||||||
|
println(strings.Join(user_sb, ""))
|
||||||
|
os.Exit(1)
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
rootCmd.AddCommand(usersCmd)
|
||||||
|
}
|
||||||
@@ -0,0 +1,44 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Author file utils is a package that contains functions that are used to read
|
||||||
|
// check, and potentially write to the author file. The author file is a file
|
||||||
|
// that contains the names and emails of the users that are allowed to commit
|
||||||
|
// An example of the author file can be found in the examples folder of the repo
|
||||||
|
func Find_authorfile() string {
|
||||||
|
if os.Getenv("author_file") == "" {
|
||||||
|
authors, err := os.UserConfigDir()
|
||||||
|
if err != nil {
|
||||||
|
fmt.Println("Error getting user config directory")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
return (authors + "/cocommit/authors")
|
||||||
|
} else {
|
||||||
|
return os.Getenv("author_file")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func CheckAuthorFile() string {
|
||||||
|
authorfile := Find_authorfile()
|
||||||
|
if _, err := os.Stat(authorfile); os.IsNotExist(err) {
|
||||||
|
println("Author file not found at: ", authorfile)
|
||||||
|
println("Would you like to create one? (y/n)")
|
||||||
|
var response string
|
||||||
|
_, err := fmt.Scanln(&response)
|
||||||
|
if err != nil {
|
||||||
|
println("Error reading response")
|
||||||
|
}
|
||||||
|
if response == "y" {
|
||||||
|
//TODO: Tui response to create author file
|
||||||
|
//createAuthorFile(authorfile)
|
||||||
|
} else {
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// This string output is mostly for convenience can mostly be ignored
|
||||||
|
return authorfile
|
||||||
|
}
|
||||||
@@ -0,0 +1,110 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"fmt"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This util file is used to create a commit message using a string builder
|
||||||
|
|
||||||
|
// string builder for the commit message
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
// list of excluded authors based on the author file
|
||||||
|
var excludeMode = []string{}
|
||||||
|
|
||||||
|
// Regex pattern used to create temp users to add to the commit message
|
||||||
|
var reg, _ = regexp.Compile("([^:]+):([^:]+)")
|
||||||
|
|
||||||
|
func Commit(message string, authors []string) string {
|
||||||
|
// write the commit message to the string builder
|
||||||
|
sb.WriteString(message + "\n")
|
||||||
|
fst := authors[0]
|
||||||
|
|
||||||
|
if fst == "all" || fst == "All" {
|
||||||
|
add_x_users(excludeMode)
|
||||||
|
goto skip_loop
|
||||||
|
} else if Groups[fst] != nil {
|
||||||
|
excludeMode = group_selection(Groups[fst], excludeMode)
|
||||||
|
add_x_users(excludeMode)
|
||||||
|
goto skip_loop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop that adds users
|
||||||
|
for _, committer := range authors {
|
||||||
|
if _, ok := Users[committer]; ok {
|
||||||
|
sb_author(committer)
|
||||||
|
} else if match := reg.MatchString(committer); match {
|
||||||
|
str := strings.Split(committer, ":")
|
||||||
|
|
||||||
|
sb.WriteString("\nCo-authored-by: ")
|
||||||
|
sb.WriteString(str[0])
|
||||||
|
sb.WriteString(" <")
|
||||||
|
sb.WriteString(str[1])
|
||||||
|
sb.WriteRune('>')
|
||||||
|
|
||||||
|
} else if committer[0] == '^' { // Negations
|
||||||
|
excludeMode = append(excludeMode, Users[committer[1:]].Username)
|
||||||
|
} else {
|
||||||
|
println(committer, " was unknown. User either not defined or name typed wrong")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if len(excludeMode) > 0 {
|
||||||
|
add_x_users(excludeMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip label for edge cases at top of function
|
||||||
|
skip_loop:
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func GitWrapper(commit string) {
|
||||||
|
// commit shell command
|
||||||
|
cmd := exec.Command("git", "commit", "-m", commit)
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/18159704/how-to-debug-exit-status-1-error-when-running-exec-command-in-golang
|
||||||
|
|
||||||
|
cmd_output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
println(fmt.Sprint(err) + " : " + string(cmd_output))
|
||||||
|
} else {
|
||||||
|
println(string(cmd_output))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to add an author to the commit message
|
||||||
|
func sb_author(committer string) {
|
||||||
|
sb.WriteString("\nCo-authored-by: ")
|
||||||
|
sb.WriteString(Users[committer].Username)
|
||||||
|
sb.WriteString(" <")
|
||||||
|
sb.WriteString(Users[committer].Email)
|
||||||
|
sb.WriteRune('>')
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to add x amount of users to the commit message
|
||||||
|
func add_x_users(excludeMode []string) {
|
||||||
|
if len(DefExclude) > 0 {
|
||||||
|
excludeMode = append(excludeMode, DefExclude...)
|
||||||
|
}
|
||||||
|
for key, user := range Users {
|
||||||
|
if !slices.Contains(excludeMode, user.Username) {
|
||||||
|
sb_author(key)
|
||||||
|
excludeMode = append(excludeMode, user.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// helper function to select a group of users to exclude in the commit message
|
||||||
|
func group_selection(group []User, excludeMode []string) []string {
|
||||||
|
for _, user := range Users {
|
||||||
|
if !(slices.Contains(group, user)) {
|
||||||
|
excludeMode = append(excludeMode, user.Username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return excludeMode
|
||||||
|
}
|
||||||
@@ -0,0 +1,69 @@
|
|||||||
|
package utils
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"os"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// This util file is used to handle users and their information
|
||||||
|
type User struct {
|
||||||
|
Username string
|
||||||
|
Email string
|
||||||
|
Names string
|
||||||
|
}
|
||||||
|
|
||||||
|
var Users = map[string]User{}
|
||||||
|
var DefExclude = []string{}
|
||||||
|
var Groups = map[string][]User{}
|
||||||
|
|
||||||
|
func Define_users(author_file string) {
|
||||||
|
file, err := os.Open(author_file)
|
||||||
|
if err != nil {
|
||||||
|
print("File not found")
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
// eat a single input
|
||||||
|
scanner.Scan()
|
||||||
|
|
||||||
|
// reads the input of authors file and formats accordingly
|
||||||
|
for scanner.Scan() {
|
||||||
|
input_str := scanner.Text()
|
||||||
|
group_info := []string{}
|
||||||
|
if strings.Contains(input_str, ";;") {
|
||||||
|
input := strings.Split(input_str, ";;")
|
||||||
|
input_str = input[0]
|
||||||
|
group_info = append(group_info, strings.Split(input[1], "|")...)
|
||||||
|
}
|
||||||
|
info := strings.Split(input_str, "|")
|
||||||
|
usr := User{Username: info[2], Email: info[3], Names: info[0] + "/" + info[1]}
|
||||||
|
Users[info[0]] = usr
|
||||||
|
Users[info[1]] = usr
|
||||||
|
// Adds users with the ex tag to the defExclude list
|
||||||
|
if len(info) == 5 {
|
||||||
|
if info[4] == "ex" {
|
||||||
|
DefExclude = append(DefExclude, info[2])
|
||||||
|
}
|
||||||
|
} else if len(group_info) > 0 {
|
||||||
|
// Group assignment
|
||||||
|
for _, group := range group_info {
|
||||||
|
if Groups[group] == nil {
|
||||||
|
Groups[group] = []User{usr}
|
||||||
|
} else {
|
||||||
|
//TODO: Try and find a cleaner way of doing this
|
||||||
|
usr_lst := Groups[group]
|
||||||
|
usr_lst = append(usr_lst, usr)
|
||||||
|
Groups[group] = usr_lst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := scanner.Err(); err != nil {
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -1,153 +0,0 @@
|
|||||||
package main
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bufio"
|
|
||||||
"fmt"
|
|
||||||
"os"
|
|
||||||
"os/exec"
|
|
||||||
"regexp"
|
|
||||||
"slices"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
type user struct {
|
|
||||||
username string
|
|
||||||
email string
|
|
||||||
}
|
|
||||||
|
|
||||||
var users = make(map[string]user)
|
|
||||||
var sb strings.Builder
|
|
||||||
var all_flag = false
|
|
||||||
|
|
||||||
func main() {
|
|
||||||
|
|
||||||
|
|
||||||
// Reads a shell env variable :: author_file
|
|
||||||
authors := os.Getenv("author_file")
|
|
||||||
|
|
||||||
file, err := os.Open(authors)
|
|
||||||
if err != nil {
|
|
||||||
print("File not found")
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
defer file.Close()
|
|
||||||
|
|
||||||
scanner := bufio.NewScanner(file)
|
|
||||||
|
|
||||||
// eat a single input
|
|
||||||
scanner.Scan()
|
|
||||||
|
|
||||||
// reads the input of authors file and formats accordingly
|
|
||||||
for scanner.Scan() {
|
|
||||||
info := strings.Split(scanner.Text(), "|")
|
|
||||||
usr := user{username: info[2], email: info[3]}
|
|
||||||
users[info[0]] = usr
|
|
||||||
users[info[1]] = usr
|
|
||||||
}
|
|
||||||
|
|
||||||
if err := scanner.Err(); err != nil {
|
|
||||||
os.Exit(2)
|
|
||||||
}
|
|
||||||
|
|
||||||
args := os.Args[1:]
|
|
||||||
|
|
||||||
NoInput(args, users)
|
|
||||||
|
|
||||||
excludeMode := []string{}
|
|
||||||
|
|
||||||
// builds the commit message with the selected authors
|
|
||||||
sb.WriteString(string(args[0]) + "\n")
|
|
||||||
reg, _ := regexp.Compile("([^:]+):([^:]+)")
|
|
||||||
|
|
||||||
if args[1] == "all" || args[1] == "All" {
|
|
||||||
all_flag = true
|
|
||||||
goto skip_loop
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
for _, committer := range args[1:] {
|
|
||||||
if _, ok := users[committer]; ok {
|
|
||||||
sb_author(committer)
|
|
||||||
} else if match := reg.MatchString(committer); match {
|
|
||||||
str := strings.Split(committer, ":")
|
|
||||||
|
|
||||||
sb.WriteString("\nCo-authored-by: ")
|
|
||||||
sb.WriteString(str[0])
|
|
||||||
sb.WriteString(" <")
|
|
||||||
sb.WriteString(str[1])
|
|
||||||
sb.WriteRune('>')
|
|
||||||
|
|
||||||
} else if committer[0] == '^' {
|
|
||||||
excludeMode = append(excludeMode, users[committer[1:]].username)
|
|
||||||
|
|
||||||
} else {
|
|
||||||
println(committer, " was unknown. User either not defined or name typed wrong")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
skip_loop:
|
|
||||||
|
|
||||||
if len(excludeMode) > 0 || all_flag {
|
|
||||||
add_x_users(excludeMode)
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
// commit msg built
|
|
||||||
commit := sb_build()
|
|
||||||
|
|
||||||
//NOTE: Uncomment for testing
|
|
||||||
//print(commit)
|
|
||||||
|
|
||||||
// commit shell command
|
|
||||||
cmd := exec.Command("git", "commit", "-m", commit)
|
|
||||||
|
|
||||||
// https://stackoverflow.com/questions/18159704/how-to-debug-exit-status-1-error-when-running-exec-command-in-golang
|
|
||||||
|
|
||||||
cmd_output, err := cmd.CombinedOutput()
|
|
||||||
|
|
||||||
if err != nil {
|
|
||||||
println(fmt.Sprint(err) + " : " + string(cmd_output))
|
|
||||||
} else {
|
|
||||||
println(string(cmd_output))
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
func add_x_users(excludeMode []string) {
|
|
||||||
for key, user := range users {
|
|
||||||
if !slices.Contains(excludeMode, user.username) {
|
|
||||||
sb_author(key)
|
|
||||||
excludeMode = append(excludeMode, user.username)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
func sb_build() string {
|
|
||||||
return sb.String()
|
|
||||||
}
|
|
||||||
|
|
||||||
func sb_author(committer string) {
|
|
||||||
sb.WriteString("\nCo-authored-by: ")
|
|
||||||
sb.WriteString(users[committer].username)
|
|
||||||
sb.WriteString(" <")
|
|
||||||
sb.WriteString(users[committer].email)
|
|
||||||
sb.WriteRune('>')
|
|
||||||
}
|
|
||||||
|
|
||||||
// TODO: move half this into another function and call before building users to improve performance
|
|
||||||
func NoInput(args []string, users map[string]user) {
|
|
||||||
if len(args) < 2 {
|
|
||||||
// If you call binary with users prints users
|
|
||||||
if len(args) == 1 && args[0] == "users" {
|
|
||||||
println("List of users:")
|
|
||||||
for name, usr := range users {
|
|
||||||
println(name, " ->", " Username:", usr.username, " Email:", usr.email)
|
|
||||||
}
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
// if calling binary with nothing or only string
|
|
||||||
print("Usage: cocommit <commit message> <co-author1> [co-author2] [co-author3] || \n cocommit <commit message> <co-author1:email> [co-author2:email] [co-author3:email] || Mixes of both")
|
|
||||||
|
|
||||||
os.Exit(1)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
@@ -0,0 +1,309 @@
|
|||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bufio"
|
||||||
|
"fmt"
|
||||||
|
"os"
|
||||||
|
"os/exec"
|
||||||
|
"regexp"
|
||||||
|
"slices"
|
||||||
|
"sort"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
type user struct {
|
||||||
|
username string
|
||||||
|
email string
|
||||||
|
names string
|
||||||
|
}
|
||||||
|
//TODO: Remove later once everything is up and running with the new version
|
||||||
|
|
||||||
|
// Map of all th users in the author file
|
||||||
|
var users = make(map[string]user)
|
||||||
|
|
||||||
|
// String builder for building the commit message
|
||||||
|
var sb strings.Builder
|
||||||
|
|
||||||
|
// Flag that can be toggled to include all users in a commit message (excluding defExclude)
|
||||||
|
var all_flag = false
|
||||||
|
|
||||||
|
// DefaultExclude -> A list that contains users marked with ex meaning
|
||||||
|
// they should not be included in all and negations
|
||||||
|
var defExclude = []string{}
|
||||||
|
|
||||||
|
// Group map for adding people as a group
|
||||||
|
var groups = make(map[string][]user)
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
|
||||||
|
// Reads a shell env variable :: author_file
|
||||||
|
var authors string
|
||||||
|
envVar := os.Getenv("author_file")
|
||||||
|
if envVar == "" {
|
||||||
|
var err error
|
||||||
|
authors, err = os.UserConfigDir()
|
||||||
|
authors += "/cocommit/authors"
|
||||||
|
if err != nil {
|
||||||
|
println("Error: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
authors = envVar
|
||||||
|
}
|
||||||
|
|
||||||
|
file, err := os.Open(authors)
|
||||||
|
if err != nil {
|
||||||
|
authors, _ = os.UserConfigDir()
|
||||||
|
authors += "/cocommit/authors"
|
||||||
|
println("Authors file cannot be found. Please check the path to the file. \nEither set the author_file env variable or place the file in the default location. \nDefault location: " + authors)
|
||||||
|
println("If you want to create a blank template file at the default location type y|yes or cancel with n|no")
|
||||||
|
var input string
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
if input == "y" || input == "yes" {
|
||||||
|
create_author_file("yes")
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
println("Cancelled")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
|
||||||
|
scanner := bufio.NewScanner(file)
|
||||||
|
|
||||||
|
// eat a single input
|
||||||
|
scanner.Scan()
|
||||||
|
|
||||||
|
// reads the input of authors file and formats accordingly
|
||||||
|
for scanner.Scan() {
|
||||||
|
input_str := scanner.Text()
|
||||||
|
group_info := []string{}
|
||||||
|
if strings.Contains(input_str, ";;") {
|
||||||
|
input := strings.Split(input_str, ";;")
|
||||||
|
input_str = input[0]
|
||||||
|
group_info = append(group_info, strings.Split(input[1], "|")...)
|
||||||
|
}
|
||||||
|
info := strings.Split(input_str, "|")
|
||||||
|
usr := user{username: info[2], email: info[3], names: info[0] + "/" + info[1]}
|
||||||
|
users[info[0]] = usr
|
||||||
|
users[info[1]] = usr
|
||||||
|
// Adds users with the ex tag to the defExclude list
|
||||||
|
if len(info) == 5 {
|
||||||
|
if info[4] == "ex" {
|
||||||
|
defExclude = append(defExclude, info[2])
|
||||||
|
}
|
||||||
|
} else if len(group_info) > 0 {
|
||||||
|
// Group assignment
|
||||||
|
for _, group := range group_info {
|
||||||
|
if groups[group] == nil {
|
||||||
|
groups[group] = []user{usr}
|
||||||
|
} else {
|
||||||
|
//TODO: Try and find a cleaner way of doing this
|
||||||
|
usr_lst := groups[group]
|
||||||
|
usr_lst = append(usr_lst, usr)
|
||||||
|
groups[group] = usr_lst
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
check_err(scanner.Err())
|
||||||
|
// Removes the call command for the program
|
||||||
|
args := os.Args[1:]
|
||||||
|
|
||||||
|
// Checks if the user called the program with any inputs or with non commit args
|
||||||
|
NoInput(args, users)
|
||||||
|
|
||||||
|
// This list is used when doing negations and for removing duplicate users during string building
|
||||||
|
excludeMode := []string{}
|
||||||
|
|
||||||
|
// builds the commit message with the selected authors
|
||||||
|
sb.WriteString(string(args[0]) + "\n")
|
||||||
|
|
||||||
|
// Regex that catches one off authors
|
||||||
|
reg, _ := regexp.Compile("([^:]+):([^:]+)")
|
||||||
|
|
||||||
|
if args[1] == "all" || args[1] == "All" {
|
||||||
|
all_flag = true
|
||||||
|
goto skip_loop
|
||||||
|
} else if groups[args[1]] != nil {
|
||||||
|
// Selects everybody that isn't the group members and adds them to the defExclude
|
||||||
|
excludeMode = group_selection(groups[args[1]], excludeMode)
|
||||||
|
goto skip_loop
|
||||||
|
}
|
||||||
|
|
||||||
|
// Loop that adds users
|
||||||
|
for _, committer := range args[1:] {
|
||||||
|
if _, ok := users[committer]; ok {
|
||||||
|
sb_author(committer)
|
||||||
|
} else if match := reg.MatchString(committer); match {
|
||||||
|
str := strings.Split(committer, ":")
|
||||||
|
|
||||||
|
sb.WriteString("\nCo-authored-by: ")
|
||||||
|
sb.WriteString(str[0])
|
||||||
|
sb.WriteString(" <")
|
||||||
|
sb.WriteString(str[1])
|
||||||
|
sb.WriteRune('>')
|
||||||
|
|
||||||
|
} else if committer[0] == '^' { // Negations
|
||||||
|
excludeMode = append(excludeMode, users[committer[1:]].username)
|
||||||
|
|
||||||
|
} else {
|
||||||
|
println(committer, " was unknown. User either not defined or name typed wrong")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Skip label for adding all
|
||||||
|
skip_loop:
|
||||||
|
|
||||||
|
if len(excludeMode) > 0 || all_flag {
|
||||||
|
// adds all users not in the excludeMode list
|
||||||
|
add_x_users(excludeMode)
|
||||||
|
}
|
||||||
|
|
||||||
|
// commit msg built
|
||||||
|
commit := sb_build()
|
||||||
|
|
||||||
|
print(commit)
|
||||||
|
|
||||||
|
//NOTE: Uncomment for testing
|
||||||
|
//print(commit)
|
||||||
|
|
||||||
|
// commit shell command
|
||||||
|
cmd := exec.Command("git", "commit", "-m", commit)
|
||||||
|
|
||||||
|
// https://stackoverflow.com/questions/18159704/how-to-debug-exit-status-1-error-when-running-exec-command-in-golang
|
||||||
|
|
||||||
|
cmd_output, err := cmd.CombinedOutput()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
println(fmt.Sprint(err) + " : " + string(cmd_output))
|
||||||
|
} else {
|
||||||
|
println(string(cmd_output))
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
func group_selection(group []user, excludeMode []string) []string {
|
||||||
|
for _, user := range users {
|
||||||
|
if !(slices.Contains(group, user)) {
|
||||||
|
excludeMode = append(excludeMode, user.username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return excludeMode
|
||||||
|
}
|
||||||
|
|
||||||
|
func add_x_users(excludeMode []string) {
|
||||||
|
if len(defExclude) > 0 {
|
||||||
|
excludeMode = append(excludeMode, defExclude...)
|
||||||
|
}
|
||||||
|
for key, user := range users {
|
||||||
|
if !slices.Contains(excludeMode, user.username) {
|
||||||
|
sb_author(key)
|
||||||
|
excludeMode = append(excludeMode, user.username)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func sb_build() string {
|
||||||
|
return sb.String()
|
||||||
|
}
|
||||||
|
|
||||||
|
func sb_author(committer string) {
|
||||||
|
sb.WriteString("\nCo-authored-by: ")
|
||||||
|
sb.WriteString(users[committer].username)
|
||||||
|
sb.WriteString(" <")
|
||||||
|
sb.WriteString(users[committer].email)
|
||||||
|
sb.WriteRune('>')
|
||||||
|
}
|
||||||
|
|
||||||
|
// TODO: move half this into another function and call before building users to improve performance
|
||||||
|
func NoInput(args []string, users map[string]user) {
|
||||||
|
if len(args) < 2 {
|
||||||
|
// If you call binary with users prints users
|
||||||
|
if len(args) == 1 && args[0] == "users" {
|
||||||
|
println("List of users:\nFormat: <shortname>/<name> -> Username: <username> Email: <email>")
|
||||||
|
seen_users := []user{}
|
||||||
|
user_sb := []string{}
|
||||||
|
for name, usr := range users {
|
||||||
|
if !slices.Contains(seen_users, usr) {
|
||||||
|
user_sb = append(user_sb, users[name].names+" ->"+" Username: "+usr.username+" Email: "+usr.email+"\n")
|
||||||
|
seen_users = append(seen_users, usr)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
sort.Strings(user_sb)
|
||||||
|
println(strings.Join(user_sb, ""))
|
||||||
|
os.Exit(1)
|
||||||
|
} else if len(args) == 1 && args[0] == "config" {
|
||||||
|
create_author_file()
|
||||||
|
}
|
||||||
|
// if calling binary with nothing or only string
|
||||||
|
command_options := []string{
|
||||||
|
"cocommit <commit message> <co-author1> [co-author2] [co-author3]",
|
||||||
|
"cocommit <commit message> <co-author1:email> [co-author2:email] [co-author3:email]",
|
||||||
|
"cocommit <commit message> all",
|
||||||
|
"cocommit <commit message> ^<co-author1> ^[co-author2]",
|
||||||
|
"cocommit <commit message> <group>",
|
||||||
|
"cocommit users",
|
||||||
|
}
|
||||||
|
println("Usage:")
|
||||||
|
for _, v := range command_options {
|
||||||
|
print(v)
|
||||||
|
println(" ||")
|
||||||
|
}
|
||||||
|
println("Mixes of both")
|
||||||
|
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func create_author_file(param ...string) {
|
||||||
|
var input string
|
||||||
|
authors, err := os.UserConfigDir()
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
println("Error: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
if len(param) > 0 {
|
||||||
|
input = "yes"
|
||||||
|
goto skip
|
||||||
|
}
|
||||||
|
println("This command will create a blank template auhtor file in the default location. \nDefault location: " + authors + "\nConfirm by typing y|yes or cancel with n|no")
|
||||||
|
fmt.Scanln(&input)
|
||||||
|
if err != nil {
|
||||||
|
println("Error: ", err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
skip:
|
||||||
|
if input == "y" || input == "yes" {
|
||||||
|
// create folder cocommit in .config
|
||||||
|
authors += "/cocommit"
|
||||||
|
err := os.MkdirAll(authors, 0755)
|
||||||
|
if err != nil {
|
||||||
|
println("Error in dir creation: ", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
authors += "/authors"
|
||||||
|
file, err := os.Create(authors)
|
||||||
|
if err != nil {
|
||||||
|
println("Error: ", err.Error())
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
defer file.Close()
|
||||||
|
file.WriteString("name_short|Name|Username|email (opt: |ex) (opt: ;;group1 or ;;group1|group2|group3...)\n")
|
||||||
|
println("File created successfully at: " + authors)
|
||||||
|
os.Exit(1)
|
||||||
|
} else {
|
||||||
|
println("Cancelled")
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func check_err(e error) {
|
||||||
|
if e != nil {
|
||||||
|
fmt.Println(e.Error())
|
||||||
|
os.Exit(2)
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -0,0 +1,11 @@
|
|||||||
|
/*
|
||||||
|
Copyright © 2024 NAME HERE <EMAIL ADDRESS>
|
||||||
|
|
||||||
|
*/
|
||||||
|
package main
|
||||||
|
|
||||||
|
import "main/src_code/go_src/cmd"
|
||||||
|
|
||||||
|
func main() {
|
||||||
|
cmd.Execute()
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user