gig-gitignore工具实战开发(三):gig add基础实现
✨ 前言: 在上一篇我们已经设计了多源模板系统,本篇我们来进行实战。先实现gig add命令基础代码。
一、项目初始化
暂略,主要包含如下工作
- go mod init 项目初始化
- 添加cobra、viper等相关库
- 配置初始化等等
参考依赖
require (
github.com/manifoldco/promptui v0.9.0
github.com/nicksnyder/go-i18n/v2 v2.6.0
github.com/rivo/tview v0.0.0-20240307173318-e804876934a1
github.com/sergi/go-diff v1.4.0
github.com/spf13/afero v1.12.0
github.com/spf13/cobra v1.9.1
github.com/spf13/viper v1.20.1
golang.org/x/text v0.23.0
gopkg.in/yaml.v3 v3.0.1
)
二、add命令实现
添加add.go
命令行中调用
cobra-cli add add
添加参数
func init() {
rootCmd.AddCommand(addCmd)
addCmd.Flags().BoolVar(&yesFlag, "yes", false, "自动确认所有交互,无需人工输入")
addCmd.Flags().BoolVarP(&updateFlag, "update", "u", false, "强制更新github模板")
}
- yes参数可以忽略交互,适合自动化流程
- update主要针对github模板,默认不进行更新
业务逻辑处理
根据传入参数不同,分别进行处理
func(cmd *cobra.Command, args []string) {
var languages []string
if len(args) == 0 {
detected := utils.DetectionUtil.DetectProjectTypes(models.AppConfig.Detection.FileMap)
if len(detected) > 0 {
languages = promptUserForTypes(detected)
}
if len(languages) == 0 {
fmt.Println("未检测到项目类型,请手动输入语言参数。例如:gig add python")
os.Exit(1)
}
} else {
languages = args
}
switch genType {
case "ai":
result, err := utils.AI.StreamChat("add", strings.Join(languages, ", "))
if err != nil {
fmt.Println("AI 生成失败:", err)
os.Exit(1)
}
cleanedContent := cleanupAIOutput(result)
updateGitignoreWithContent(cleanedContent)
case "github":
handleAdd(languages, "github", updateFlag)
case "local":
handleAdd(languages, "local", false)
case "api":
result, err := getGitignoreFromAPI(languages)
if err != nil {
fmt.Println("API 获取失败:", err)
os.Exit(1)
}
updateGitignoreWithContent(result)
default:
fmt.Printf("Error: Invalid type '%s'. Please use 'local', 'github', 'ai' 或 'api'.\n", genType)
}
},
}
其中代码需要进行识别项目语言,我们进行如下工具封装,根据核心特征进行判断。由于项目繁多,不可能包括所有,用户可以自行进行核心特征拓展。
// 自动识别项目类型(递归遍历,仅根据配置文件匹配,特殊目录优化)
func (d Detection) DetectProjectTypes(fileMap map[string]string) []string {
fmt.Println("typeMap:", fileMap)
if len(fileMap) == 0 {
// 配置为空时兜底
return nil
}
langSet := make(map[string]struct{})
// 特殊目录与类型的映射(可扩展)
specialDirs := map[string]string{
"node_modules": "node",
// 可按需添加:
// ".venv": "python",
// "vendor": "go",
// "target": "rust",
}
filepath.Walk(".", func(path string, info os.FileInfo, err error) error {
if err != nil {
return nil // 忽略错误
}
if info.IsDir() {
base := filepath.Base(path)
// 跳过隐藏目录(如.git、.idea等)
if strings.HasPrefix(base, ".") && path != "." {
return filepath.SkipDir
}
// 特殊目录:遇到直接判定类型并跳过递归
if lang, ok := specialDirs[base]; ok {
langSet[lang] = struct{}{}
return filepath.SkipDir
}
return nil
}
name := info.Name()
if lang, ok := fileMap[name]; ok {
langSet[lang] = struct{}{}
}
return nil
})
var langs []string
for lang := range langSet {
langs = append(langs, lang)
}
return langs
}
识别出的语言有可能有误,这时候用户可以进行修正
func promptUserForTypes(detected []string) []string {
if yesFlag {
fmt.Printf("检测到以下项目类型:%s\n", strings.Join(detected, ", "))
fmt.Println("已自动确认项目类型 (--yes)")
return detected
}
fmt.Printf("检测到以下项目类型:%s\n", strings.Join(detected, ", "))
fmt.Print("是否确认?(直接回车确认,或输入新的类型列表,如:go,python):")
input, _ := bufio.NewReader(os.Stdin).ReadString('\n')
input = strings.TrimSpace(input)
if input == "" {
return detected
}
var result []string
for _, t := range strings.Split(input, ",") {
t = strings.TrimSpace(t)
if t != "" {
result = append(result, t)
}
}
return result
}