maidn-cli/internal/forgejo/repo.go

139 lines
4.2 KiB
Go

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"`
DefaultBranch string `json:"default_branch"`
}
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: false, DefaultBranch: "main"})
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 := utils.RunCommandInDir(tempDir, "git", "checkout", "-B", "main"); 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)
}