summaryrefslogtreecommitdiff
path: root/internal/codeberg/codeberg.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-27 23:24:36 +0300
committerPaul Buetow <paul@buetow.org>2025-06-27 23:24:36 +0300
commit79e882713b195e4674dc693169ecf7dbf956a91e (patch)
treeb00325ea3f519676bc389cce735447249bef69a6 /internal/codeberg/codeberg.go
parent59d7b258cff47b0640f44f1068460403459a4bde (diff)
feat: implement --create-codeberg-repos
Diffstat (limited to 'internal/codeberg/codeberg.go')
-rw-r--r--internal/codeberg/codeberg.go105
1 files changed, 103 insertions, 2 deletions
diff --git a/internal/codeberg/codeberg.go b/internal/codeberg/codeberg.go
index 4f0a76d..1cf5659 100644
--- a/internal/codeberg/codeberg.go
+++ b/internal/codeberg/codeberg.go
@@ -1,9 +1,12 @@
package codeberg
import (
+ "bytes"
"encoding/json"
"fmt"
"net/http"
+ "os"
+ "path/filepath"
"time"
)
@@ -28,14 +31,45 @@ type Repository struct {
type Client struct {
baseURL string
org string
+ token string
}
// NewClient creates a new Codeberg API client
-func NewClient(org string) Client {
- return Client{
+func NewClient(org, token string) Client {
+ c := Client{
baseURL: "https://codeberg.org/api/v1",
org: org,
}
+ c.loadToken(token)
+ return c
+}
+
+// loadToken loads the Codeberg API token from config, env, or file
+func (c *Client) loadToken(tokenFromConfig string) {
+ if tokenFromConfig != "" {
+ c.token = tokenFromConfig
+ return
+ }
+
+ // Check environment variable
+ if token := os.Getenv("CODEBERG_TOKEN"); token != "" {
+ c.token = token
+ return
+ }
+
+ // Check token file
+ home, err := os.UserHomeDir()
+ if err == nil {
+ tokenFile := filepath.Join(home, ".gitsyncer_codeberg_token")
+ if data, err := os.ReadFile(tokenFile); err == nil {
+ c.token = string(data)
+ }
+ }
+}
+
+// HasToken returns true if a token is loaded
+func (c *Client) HasToken() bool {
+ return c.token != ""
}
// ListPublicRepos lists all public repositories for an organization
@@ -130,3 +164,70 @@ func GetRepoNames(repos []Repository) []string {
}
return names
}
+
+// RepoExists checks if a repository exists on Codeberg
+func (c *Client) RepoExists(repoName string) (bool, error) {
+ url := fmt.Sprintf("%s/repos/%s/%s", c.baseURL, c.org, repoName)
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return false, err
+ }
+
+ if c.HasToken() {
+ req.Header.Set("Authorization", "token "+c.token)
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return false, err
+ }
+ defer resp.Body.Close()
+
+ return resp.StatusCode == 200, nil
+}
+
+// CreateRepo creates a new repository on Codeberg
+func (c *Client) CreateRepo(repoName, description string, private bool) error {
+ exists, err := c.RepoExists(repoName)
+ if err != nil {
+ return fmt.Errorf("failed to check if repo exists: %w", err)
+ }
+ if exists {
+ return nil // Repository already exists
+ }
+
+ url := fmt.Sprintf("%s/user/repos", c.baseURL)
+
+ payload := map[string]interface{}{
+ "name": repoName,
+ "description": description,
+ "private": private,
+ }
+
+ body, err := json.Marshal(payload)
+ if err != nil {
+ return err
+ }
+
+ req, err := http.NewRequest("POST", url, bytes.NewBuffer(body))
+ if err != nil {
+ return err
+ }
+
+ req.Header.Set("Content-Type", "application/json")
+ if c.HasToken() {
+ req.Header.Set("Authorization", "token "+c.token)
+ }
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != http.StatusCreated {
+ return fmt.Errorf("failed to create repository: status code %d", resp.StatusCode)
+ }
+
+ return nil
+}