使用 cobra 开发 glab4f 命令行过程

我想用 cobra(golang) 做一个简单的命令行工具,用来管理 gitlab ci 里的环境变量。
使用方式如下:
1. glab4f .env1 .env2
2. glab4f envdir

具体细节
1. 命令行可以读取多个环境变量 .env1 .env2
2. 读取环境变量里的变量名和值,并将其写入 gitlab ci 的环境变量里。
3. 如果目标为 dir 的情况会依次读取当前目录下所有的.env 文件,并将其内容写入 gitlab ci 的环境变量里。
4. env如果以 env@production 这种方式命名,则当 @ 后面的内容作为 Environment scope

要实现这个功能,你可以使用 Go 语言和 Cobra 库来构建一个命令行工具。以下是一个简单的实现步骤和代码示例:

1. 安装 Cobra

首先,你需要安装 Cobra 库。你可以使用以下命令来安装:

go get -u github.com/spf13/cobra/cobra

2. 创建项目结构

创建一个新的 Go 项目,并使用 Cobra 初始化命令行工具:

cobra init --pkg-name glab4f

这将会生成一个基本的项目结构。

3. 添加命令

在生成的 cmd/root.go 文件中,添加一个新的命令来处理 .env 文件和目录。

package cmd

import (
    "fmt"
    "os"
    "path/filepath"
    "strings"

    "github.com/spf13/cobra"
)

var rootCmd = &cobra.Command{
    Use:   "glab4f",
    Short: "A tool to manage GitLab CI environment variables",
    Long:  `A tool to manage GitLab CI environment variables from .env files or directories.`,
    Run: func(cmd *cobra.Command, args []string) {
        if len(args) == 0 {
            fmt.Println("Please provide .env files or a directory")
            return
        }

        for _, arg := range args {
            if isDir(arg) {
                processDir(arg)
            } else {
                processFile(arg)
            }
        }
    },
}

func isDir(path string) bool {
    info, err := os.Stat(path)
    if err != nil {
        return false
    }
    return info.IsDir()
}

func processDir(dirPath string) {
    filepath.Walk(dirPath, func(path string, info os.FileInfo, err error) error {
        if err != nil {
            return err
        }
        if !info.IsDir() && strings.HasSuffix(info.Name(), ".env") {
            processFile(path)
        }
        return nil
    })
}

func processFile(filePath string) {
    envScope := ""
    if strings.Contains(filePath, "@") {
        parts := strings.Split(filePath, "@")
        envScope = parts[1]
    }

    file, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("Error opening file %s: %v\n", filePath, err)
        return
    }
    defer file.Close()

    // Read and parse the .env file
    // Here you would read the file line by line and extract the key-value pairs
    // Then, you would use the GitLab API to set the environment variables

    fmt.Printf("Processing file: %s with environment scope: %s\n", filePath, envScope)
}

func Execute() {
    if err := rootCmd.Execute(); err != nil {
        fmt.Println(err)
        os.Exit(1)
    }
}

4. 解析 .env 文件

processFile 函数中,你需要解析 .env 文件并提取键值对。你可以使用 bufio.Scanner 来逐行读取文件,并使用 strings.Split 来分割键值对。

func processFile(filePath string) {
    envScope := ""
    if strings.Contains(filePath, "@") {
        parts := strings.Split(filePath, "@")
        envScope = parts[1]
    }

    file, err := os.Open(filePath)
    if err != nil {
        fmt.Printf("Error opening file %s: %v\n", filePath, err)
        return
    }
    defer file.Close()

    scanner := bufio.NewScanner(file)
    for scanner.Scan() {
        line := scanner.Text()
        if strings.TrimSpace(line) == "" || strings.HasPrefix(line, "#") {
            continue
        }
        parts := strings.SplitN(line, "=", 2)
        if len(parts) != 2 {
            continue
        }
        key := strings.TrimSpace(parts[0])
        value := strings.TrimSpace(parts[1])

        // Here you would call the GitLab API to set the environment variable
        fmt.Printf("Setting variable: %s=%s with scope: %s\n", key, value, envScope)
    }

    if err := scanner.Err(); err != nil {
        fmt.Printf("Error reading file %s: %v\n", filePath, err)
    }
}

5. 调用 GitLab API

你需要使用 GitLab 的 API 来设置环境变量。你可以使用 Go 的 net/http 包或者其他 HTTP 客户端库来发送请求。

func setGitLabVariable(key, value, envScope string) {
    // Replace with your GitLab project ID and access token
    projectID := "your-project-id"
    accessToken := "your-access-token"

    url := fmt.Sprintf("https://gitlab.com/api/v4/projects/%s/variables", projectID)

    payload := map[string]string{
        "key":           key,
        "value":         value,
        "environment_scope": envScope,
    }

    jsonData, err := json.Marshal(payload)
    if err != nil {
        fmt.Printf("Error marshalling payload: %v\n", err)
        return
    }

    req, err := http.NewRequest("POST", url, bytes.NewBuffer(jsonData))
    if err != nil {
        fmt.Printf("Error creating request: %v\n", err)
        return
    }

    req.Header.Set("PRIVATE-TOKEN", accessToken)
    req.Header.Set("Content-Type", "application/json")

    client := &http.Client{}
    resp, err := client.Do(req)
    if err != nil {
        fmt.Printf("Error sending request: %v\n", err)
        return
    }
    defer resp.Body.Close()

    if resp.StatusCode != http.StatusCreated {
        fmt.Printf("Failed to set variable: %s\n", resp.Status)
        return
    }

    fmt.Printf("Successfully set variable: %s\n", key)
}

6. 编译和运行

最后,编译并运行你的命令行工具:

go build -o glab4f
./glab4f .env1 .env2
./glab4f envdir

总结

这个示例展示了如何使用 Cobra 创建一个简单的命令行工具来管理 GitLab CI 的环境变量。你可以根据需要进一步扩展和优化这个工具,例如添加错误处理、日志记录、配置文件支持等。