mirror of
https://github.com/chenasraf/gi_gen.git
synced 2026-05-17 17:48:01 +00:00
improve selection flow, allow fallback in auto discovery
This commit is contained in:
@@ -3,11 +3,8 @@ package cmd
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"path/filepath"
|
||||
|
||||
. "github.com/chenasraf/gi_gen/internal"
|
||||
"github.com/chenasraf/gi_gen/internal"
|
||||
)
|
||||
|
||||
func RunMainCmd() {
|
||||
@@ -18,7 +15,7 @@ func RunMainCmd() {
|
||||
return
|
||||
}
|
||||
|
||||
RunGIGen()
|
||||
internal.GIGen()
|
||||
}
|
||||
|
||||
func cleanCommand() bool {
|
||||
@@ -26,7 +23,7 @@ func cleanCommand() bool {
|
||||
flag.Parse()
|
||||
|
||||
if *clean {
|
||||
RemoveCacheDir()
|
||||
internal.RemoveCacheDir()
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -46,27 +43,3 @@ func helpCommand() {
|
||||
|
||||
flag.Bool("help", false, "Display help message")
|
||||
}
|
||||
|
||||
func RunGIGen() {
|
||||
wd, err := os.Getwd()
|
||||
HandleErr(err)
|
||||
|
||||
outFile := filepath.Join(wd, ".gitignore")
|
||||
allFiles, err := PrepareGitignores()
|
||||
HandleErr(err)
|
||||
|
||||
fileNames, files := DiscoverRelevantFiles(allFiles)
|
||||
|
||||
log.Println("Done.")
|
||||
|
||||
selected, selectedKeys := GetLanguageSelections(files, fileNames)
|
||||
cleanupSelection := AskCleanup()
|
||||
outContents := Ternary(cleanupSelection, CleanupMultipleFiles(selected, selectedKeys), GetAllRaw(selected, selectedKeys))
|
||||
|
||||
if FileExists(outFile) {
|
||||
HandleFileOverwrite(outFile, outContents)
|
||||
} else {
|
||||
log.Printf("Writing to %s", outFile)
|
||||
WriteFile(outFile, outContents, true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,17 +1,14 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/AlecAivazis/survey/v2"
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func AskLanguage(fileNames []string, selected []string, files map[string]string) ([]string, []string) {
|
||||
langPrompt := &survey.MultiSelect{
|
||||
Message: "Found " + fmt.Sprint(len(fileNames)) +
|
||||
" possible matches in your project for gitignore files.\n" +
|
||||
"Please select which you want to write to .gitignore:\n",
|
||||
Options: fileNames,
|
||||
Message: "Please select which you want to write to .gitignore:\n",
|
||||
Options: maps.Keys(files),
|
||||
}
|
||||
|
||||
var langSelections []string
|
||||
|
||||
31
internal/core.go
Normal file
31
internal/core.go
Normal file
@@ -0,0 +1,31 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
)
|
||||
|
||||
func GIGen() {
|
||||
wd, err := os.Getwd()
|
||||
handleErr(err)
|
||||
|
||||
outFile := filepath.Join(wd, ".gitignore")
|
||||
allFiles, err := prepareGitignores()
|
||||
handleErr(err)
|
||||
|
||||
fileNames, files := autoDiscover(allFiles)
|
||||
|
||||
selected, selectedKeys := getLanguageSelections(files, fileNames)
|
||||
cleanupSelection := AskCleanup()
|
||||
outContents := Ternary(cleanupSelection, cleanupMultipleFiles(selected, selectedKeys), getAllRaw(selected, selectedKeys))
|
||||
|
||||
if FileExists(outFile) {
|
||||
HandleFileOverwrite(outFile, outContents)
|
||||
} else {
|
||||
fmt.Printf("Writing to %s\n", outFile)
|
||||
WriteFile(outFile, outContents, true)
|
||||
}
|
||||
|
||||
fmt.Println("Done.")
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
@@ -9,18 +8,21 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func DiscoverRelevantFiles(allFiles []string) ([]string, map[string]string) {
|
||||
func autoDiscover(allFiles []string) ([]string, map[string]string) {
|
||||
answer := AskDiscovery()
|
||||
|
||||
if !answer {
|
||||
return allFiles, getAll(allFiles)
|
||||
return allFiles, getAllFiles(allFiles)
|
||||
}
|
||||
|
||||
byType := discoverProjectType()
|
||||
return maps.Keys(byType), byType
|
||||
list := discoverByExplicitProjectType()
|
||||
if len(list) == 0 {
|
||||
list = discoverByExistingPatterns(allFiles)
|
||||
}
|
||||
return maps.Keys(list), list
|
||||
}
|
||||
|
||||
func getAll(allFiles []string) map[string]string {
|
||||
func getAllFiles(allFiles []string) map[string]string {
|
||||
files := make(map[string]string)
|
||||
|
||||
for _, filename := range allFiles {
|
||||
@@ -34,26 +36,24 @@ func getAll(allFiles []string) map[string]string {
|
||||
return files
|
||||
}
|
||||
|
||||
func discoverExistingPatterns(allFiles []string, answer bool, files map[string]string) map[string]string {
|
||||
func discoverByExistingPatterns(allFiles []string) map[string]string {
|
||||
files := make(map[string]string)
|
||||
|
||||
for _, filename := range allFiles {
|
||||
contents := ReadFile(filename)
|
||||
basename := filepath.Base(filename)
|
||||
langName := basename[:strings.Index(basename, ".")]
|
||||
|
||||
if answer {
|
||||
if FindFileMatches(contents) {
|
||||
files[langName] = contents
|
||||
}
|
||||
} else {
|
||||
if findPatternFileMatches(contents) {
|
||||
files[langName] = contents
|
||||
}
|
||||
}
|
||||
return files
|
||||
}
|
||||
|
||||
func discoverProjectType() map[string]string {
|
||||
func discoverByExplicitProjectType() map[string]string {
|
||||
wd, err := os.Getwd()
|
||||
HandleErr(err)
|
||||
handleErr(err)
|
||||
|
||||
discoveryMap := make(map[string]string)
|
||||
discoveryMap["package.json"] = "Node"
|
||||
@@ -72,13 +72,11 @@ func discoverProjectType() map[string]string {
|
||||
|
||||
for _, key := range maps.Keys(discoveryMap) {
|
||||
langName := discoveryMap[key]
|
||||
ignoreFile := filepath.Join(GetCacheDir(), langName+".gitignore")
|
||||
ignoreFile := filepath.Join(getCacheDir(), langName+".gitignore")
|
||||
checkFile := filepath.Join(wd, key)
|
||||
|
||||
fmt.Println("Trying file " + checkFile)
|
||||
_, keyExists := results[langName]
|
||||
if !keyExists && FileExists(checkFile) {
|
||||
fmt.Println("Found lang " + langName)
|
||||
results[langName] = ReadFile(ignoreFile)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2,7 +2,6 @@ package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -11,48 +10,42 @@ import (
|
||||
"golang.org/x/exp/maps"
|
||||
)
|
||||
|
||||
func PrepareGitignores() ([]string, error) {
|
||||
gitignoresDir := GetCacheDir()
|
||||
func prepareGitignores() ([]string, error) {
|
||||
gitignoresDir := getCacheDir()
|
||||
|
||||
if !FileExists(gitignoresDir) {
|
||||
log.Println("Getting gitignore files...")
|
||||
fmt.Println("Getting gitignore files...")
|
||||
RunCmd("git", "clone", "--depth=1", repoUrl, gitignoresDir)
|
||||
} else if GetNeedsUpdate() {
|
||||
log.Println("Updating gitignore files...")
|
||||
} else if isCacheNeedsUpdate() {
|
||||
fmt.Println("Updating gitignore files...")
|
||||
RunCmd("git", "-C", gitignoresDir, "pull", "origin", "master")
|
||||
}
|
||||
|
||||
return GetGitignores(gitignoresDir)
|
||||
return getGitignoreFiles(gitignoresDir)
|
||||
}
|
||||
|
||||
func RemoveCacheDir() {
|
||||
cacheDir := GetCacheDir()
|
||||
log.Printf("Removing cache directory: %s...\n", cacheDir)
|
||||
cacheDir := getCacheDir()
|
||||
fmt.Printf("Removing cache directory: %s...\n", cacheDir)
|
||||
os.RemoveAll(cacheDir)
|
||||
log.Println("Done")
|
||||
fmt.Println("Done")
|
||||
}
|
||||
|
||||
func GetCacheDir() string {
|
||||
func getCacheDir() string {
|
||||
homeDir, _ := os.UserHomeDir()
|
||||
return filepath.Join(homeDir, ".github.gitignore")
|
||||
}
|
||||
|
||||
func GetGitignores(sourceDir string) ([]string, error) {
|
||||
func getGitignoreFiles(sourceDir string) ([]string, error) {
|
||||
return filepath.Glob(filepath.Join(sourceDir, "*.gitignore"))
|
||||
}
|
||||
|
||||
func GetNeedsUpdate() bool {
|
||||
gitignoresDir := GetCacheDir()
|
||||
func isCacheNeedsUpdate() bool {
|
||||
gitignoresDir := getCacheDir()
|
||||
localBytes, localErr := exec.Command("git", "-C", gitignoresDir, "rev-parse", "@").Output()
|
||||
handleErr(localErr)
|
||||
baseBytes, baseErr := exec.Command("git", "-C", gitignoresDir, "merge-base", "@", "@{u}").Output()
|
||||
if localErr != nil {
|
||||
log.Fatal(localErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
if baseErr != nil {
|
||||
log.Fatal(baseErr)
|
||||
os.Exit(1)
|
||||
}
|
||||
handleErr(baseErr)
|
||||
localStr := string(localBytes)
|
||||
baseStr := string(baseBytes)
|
||||
|
||||
@@ -68,7 +61,7 @@ var ignoreLines = []string{
|
||||
".idea/*",
|
||||
}
|
||||
|
||||
func FindFileMatches(patterns string) bool {
|
||||
func findPatternFileMatches(patterns string) bool {
|
||||
lines := strings.Split(patterns, "\n")
|
||||
wd, _ := os.Getwd()
|
||||
|
||||
@@ -99,7 +92,7 @@ func FindFileMatches(patterns string) bool {
|
||||
|
||||
var patternCache []string = []string{}
|
||||
|
||||
func RemoveUnusedPatterns(contents string) string {
|
||||
func removeUnusedPatterns(contents string) string {
|
||||
wd, _ := os.Getwd()
|
||||
lines := strings.Split(contents, "\n")
|
||||
keep := []string{}
|
||||
@@ -120,7 +113,7 @@ func RemoveUnusedPatterns(contents string) string {
|
||||
patternCache = append(patternCache, trimmed)
|
||||
|
||||
if i > 0 {
|
||||
keep = GatherPreviousCommentGroup(i, lastTakenIdx, lines, keep)
|
||||
keep = gatherPreviousCommentGroup(i, lastTakenIdx, lines, keep)
|
||||
}
|
||||
|
||||
keep = append(keep, line)
|
||||
@@ -130,7 +123,7 @@ func RemoveUnusedPatterns(contents string) string {
|
||||
return strings.Join(keep, "\n")
|
||||
}
|
||||
|
||||
func GatherPreviousCommentGroup(i int, lastTakenIdx int, lines []string, keep []string) []string {
|
||||
func gatherPreviousCommentGroup(i int, lastTakenIdx int, lines []string, keep []string) []string {
|
||||
j := 1
|
||||
foundComment := false
|
||||
comments := []string{}
|
||||
@@ -159,44 +152,48 @@ func GatherPreviousCommentGroup(i int, lastTakenIdx int, lines []string, keep []
|
||||
return keep
|
||||
}
|
||||
|
||||
func GetLanguageSelections(files map[string]string, fileNames []string) ([]string, []string) {
|
||||
func getLanguageSelections(files map[string]string, fileNames []string) ([]string, []string) {
|
||||
selected := []string{}
|
||||
allKeys := maps.Keys(files)
|
||||
selectedKeys := maps.Keys(files)
|
||||
|
||||
if len(allKeys) == 0 {
|
||||
selected = []string{}
|
||||
fmt.Println("Found no templates. Quitting.")
|
||||
os.Exit(1)
|
||||
} else if len(allKeys) > 1 {
|
||||
fmt.Println("Found " + fmt.Sprint(len(fileNames)) +
|
||||
" possible matches in your project for gitignore files.")
|
||||
selected, selectedKeys = AskLanguage(fileNames, selected, files)
|
||||
} else {
|
||||
fmt.Printf("Found one match for your project: %s. Proceeding...\n", allKeys[0])
|
||||
selected = []string{files[allKeys[0]]}
|
||||
}
|
||||
|
||||
return selected, selectedKeys
|
||||
}
|
||||
|
||||
func LangHeader(langName string) string {
|
||||
func langHeader(langName string) string {
|
||||
sep := "#========================================================================\n"
|
||||
header := fmt.Sprintf(sep+"# %s\n"+sep+"\n", langName)
|
||||
return header
|
||||
}
|
||||
|
||||
func GetAllRaw(selected []string, selectedKeys []string) string {
|
||||
func getAllRaw(selected []string, selectedKeys []string) string {
|
||||
for i, selection := range selected {
|
||||
header := Ternary(len(selected) > 1, LangHeader(selectedKeys[i]), "")
|
||||
header := Ternary(len(selected) > 1, langHeader(selectedKeys[i]), "")
|
||||
selected[i] = header + selection
|
||||
}
|
||||
return strings.Join(selected, "\n")
|
||||
}
|
||||
|
||||
func CleanupMultipleFiles(files []string, langKeys []string) string {
|
||||
func cleanupMultipleFiles(files []string, langKeys []string) string {
|
||||
out := []string{}
|
||||
for i, selection := range files {
|
||||
cleanSelection := RemoveUnusedPatterns(selection)
|
||||
cleanSelection := removeUnusedPatterns(selection)
|
||||
if strings.TrimSpace(cleanSelection) == "" {
|
||||
continue
|
||||
}
|
||||
header := Ternary(len(files) > 1, LangHeader(langKeys[i]), "")
|
||||
header := Ternary(len(files) > 1, langHeader(langKeys[i]), "")
|
||||
prefixNewline := Ternary(i > 0, "\n", "")
|
||||
contents := prefixNewline + header + cleanSelection
|
||||
out = append(out, contents)
|
||||
|
||||
@@ -2,7 +2,6 @@ package internal
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
"os/exec"
|
||||
"path/filepath"
|
||||
@@ -21,7 +20,7 @@ func FileExists(path string) bool {
|
||||
|
||||
func GlobExists(path string) bool {
|
||||
res, err := filepath.Glob(path)
|
||||
HandleErr(err)
|
||||
handleErr(err)
|
||||
return res != nil
|
||||
}
|
||||
|
||||
@@ -32,30 +31,30 @@ func RunCmd(cmd string, args ...string) (string, error) {
|
||||
|
||||
func ReadFile(path string) string {
|
||||
res, err := os.ReadFile(path)
|
||||
HandleErr(err)
|
||||
handleErr(err)
|
||||
return string(res)
|
||||
}
|
||||
|
||||
func WriteFile(path string, data string, overwrite bool) bool {
|
||||
var err error
|
||||
if overwrite {
|
||||
// os.Create(path)
|
||||
err = os.WriteFile(path, []byte(data), 0644)
|
||||
HandleErr(err)
|
||||
handleErr(err)
|
||||
} else {
|
||||
f, err := os.OpenFile(path, os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
|
||||
HandleErr(err)
|
||||
handleErr(err)
|
||||
defer f.Close()
|
||||
_, err = f.WriteString("\n" + data)
|
||||
HandleErr(err)
|
||||
handleErr(err)
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
func HandleErr(err error) {
|
||||
func handleErr(err error) {
|
||||
if err != nil {
|
||||
log.Println("Encountered an error while running gi_gen:")
|
||||
log.Fatalln(err)
|
||||
fmt.Println("Encountered an error while running gi_gen:")
|
||||
fmt.Println(err)
|
||||
os.Exit(1)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,11 +89,11 @@ func HandleFileOverwrite(path string, contents string) {
|
||||
Quit()
|
||||
break
|
||||
case "Overwrite":
|
||||
log.Printf("Writing to %s", path)
|
||||
fmt.Printf("Writing to %s\n", path)
|
||||
WriteFile(path, contents, true)
|
||||
break
|
||||
case "Append":
|
||||
log.Printf("Appending to %s", path)
|
||||
fmt.Printf("Appending to %s\n", path)
|
||||
WriteFile(path, contents, false)
|
||||
break
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user