package forgejo import ( "bytes" "encoding/json" "fmt" "net/http" "os" "os/exec" "path/filepath" "strings" "time" "github.com/Pingu-Studio/MaidnCLI/internal/utils" ) type RepoManager struct { BaseURL string Token string Owner string Username string ManifestsRepoName string FluxRepoName string } type createRepoRequest struct { Name string `json:"name"` Description string `json:"description"` Private bool `json:"private"` AutoInit bool `json:"auto_init"` } func NewRepoManager(baseURL, token, owner, username, manifestsRepo, fluxRepo string) *RepoManager { return &RepoManager{ BaseURL: strings.TrimRight(baseURL, "/"), Token: token, Owner: owner, Username: username, ManifestsRepoName: manifestsRepo, FluxRepoName: fluxRepo, } } func (rm *RepoManager) InitializeAll(createRepo func(string) error, createFlux func(string) error) error { if err := rm.ensureRepo(rm.ManifestsRepoName, "Centralized deployment manifests for Flux CD", createRepo); err != nil { return err } if err := rm.ensureRepo(rm.FluxRepoName, "Flux CD cluster configurations", createFlux); err != nil { return err } return nil } func (rm *RepoManager) ensureRepo(name, description string, createStructure func(string) error) error { repoURL := fmt.Sprintf("%s/%s/%s.git", rm.BaseURL, rm.Owner, name) apiURL := fmt.Sprintf("%s/api/v1/repos/%s/%s", rm.BaseURL, rm.Owner, name) if err := rm.apiRequest(http.MethodGet, apiURL, nil); err != nil { createURL := fmt.Sprintf("%s/api/v1/orgs/%s/repos", rm.BaseURL, rm.Owner) if rm.Owner == rm.Username { createURL = fmt.Sprintf("%s/api/v1/user/repos", rm.BaseURL) } body, _ := json.Marshal(createRepoRequest{Name: name, Description: description, Private: true, AutoInit: true}) if apiErr := rm.apiRequest(http.MethodPost, createURL, body); apiErr != nil { return apiErr } } return rm.setupRepository(repoURL, name, createStructure) } func (rm *RepoManager) setupRepository(repoURL, repoName string, createStructure func(string) error) error { tempDir, err := os.MkdirTemp("", "repo-setup-*") if err != nil { return err } defer os.RemoveAll(tempDir) if err := utils.RunCommand("git", "clone", repoURL, tempDir); err != nil { return err } if err := createStructure(tempDir); err != nil { return err } return commitAndPush(tempDir, repoName) } func commitAndPush(tempDir, repoName string) error { utils.RunCommandInDir(tempDir, "git", "config", "user.name", "Maidn") utils.RunCommandInDir(tempDir, "git", "config", "user.email", "maidn@free-maidn.com") utils.RunCommandInDir(tempDir, "git", "add", ".") statusCmd := exec.Command("git", "status", "--porcelain") statusCmd.Dir = tempDir output, _ := statusCmd.Output() if len(output) == 0 { return nil } if err := utils.RunCommandInDir(tempDir, "git", "commit", "-m", "feat: initialize repository structure for CI/CD"); err != nil { return err } if err := utils.RunCommandInDir(tempDir, "git", "push", "origin", "main"); err != nil { return err } fmt.Printf("[SUCCESS] Changes pushed to %s successfully.\n", repoName) return nil } func (rm *RepoManager) apiRequest(method, endpoint string, body []byte) error { client := http.Client{Timeout: 15 * time.Second} request, err := http.NewRequest(method, endpoint, bytes.NewReader(body)) if err != nil { return err } request.Header.Set("Authorization", "token "+rm.Token) request.Header.Set("Content-Type", "application/json") response, err := client.Do(request) if err != nil { return err } defer response.Body.Close() if response.StatusCode >= 300 { return fmt.Errorf("forgejo returned %s for %s", response.Status, endpoint) } return nil } func FluxSourceURL(baseURL, owner, repo string) string { return fmt.Sprintf("%s/%s/%s.git", strings.TrimRight(baseURL, "/"), owner, repo) } func CloneURL(baseURL, owner, repo string) string { return fmt.Sprintf("%s/%s/%s.git", strings.TrimRight(baseURL, "/"), owner, repo) } func RepoPath(parent, dirName string) string { return filepath.Join(parent, dirName) }