summaryrefslogtreecommitdiff
path: root/internal/codeberg/codeberg.go
blob: 288d1e701289c4055cfe0efab3520abe52c0f72f (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
package codeberg

import (
	"encoding/json"
	"fmt"
	"net/http"
	"time"
)

// Repository represents a Codeberg/Gitea repository
type Repository struct {
	ID          int64     `json:"id"`
	Name        string    `json:"name"`
	FullName    string    `json:"full_name"`
	Description string    `json:"description"`
	Private     bool      `json:"private"`
	Fork        bool      `json:"fork"`
	CreatedAt   time.Time `json:"created_at"`
	UpdatedAt   time.Time `json:"updated_at"`
	CloneURL    string    `json:"clone_url"`
	SSHURL      string    `json:"ssh_url"`
	Size        int       `json:"size"`
	Archived    bool      `json:"archived"`
	Empty       bool      `json:"empty"`
}

// Client handles Codeberg API operations
type Client struct {
	baseURL string
	org     string
}

// CLAUDE: Is there a rason, that we return a pointer of Client?
// NewClient creates a new Codeberg API client
func NewClient(org string) *Client {
	return &Client{
		baseURL: "https://codeberg.org/api/v1",
		org:     org,
	}
}

// ListPublicRepos lists all public repositories for an organization
func (c *Client) ListPublicRepos() ([]Repository, error) {
	var allRepos []Repository
	page := 1
	perPage := 50

	for {
		url := fmt.Sprintf("%s/orgs/%s/repos?page=%d&limit=%d", c.baseURL, c.org, page, perPage)

		resp, err := http.Get(url)
		if err != nil {
			return nil, fmt.Errorf("failed to fetch repositories: %w", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != 200 {
			return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
		}

		var repos []Repository
		if err := json.NewDecoder(resp.Body).Decode(&repos); err != nil {
			return nil, fmt.Errorf("failed to parse response: %w", err)
		}

		// Filter only public, non-fork, non-archived, non-empty repos
		for _, repo := range repos {
			if !repo.Private && !repo.Fork && !repo.Archived && !repo.Empty {
				allRepos = append(allRepos, repo)
			}
		}

		// If we got fewer repos than requested, we've reached the end
		if len(repos) < perPage {
			break
		}

		page++
	}

	return allRepos, nil
}

// ListUserPublicRepos lists all public repositories for a user
func (c *Client) ListUserPublicRepos() ([]Repository, error) {
	var allRepos []Repository
	page := 1
	perPage := 50

	for {
		url := fmt.Sprintf("%s/users/%s/repos?page=%d&limit=%d", c.baseURL, c.org, page, perPage)

		resp, err := http.Get(url)
		if err != nil {
			return nil, fmt.Errorf("failed to fetch repositories: %w", err)
		}
		defer resp.Body.Close()

		if resp.StatusCode != 200 {
			return nil, fmt.Errorf("API returned status %d", resp.StatusCode)
		}

		var repos []Repository
		if err := json.NewDecoder(resp.Body).Decode(&repos); err != nil {
			return nil, fmt.Errorf("failed to parse response: %w", err)
		}

		// Filter only public, non-fork, non-archived, non-empty repos
		for _, repo := range repos {
			if !repo.Private && !repo.Fork && !repo.Archived && !repo.Empty {
				allRepos = append(allRepos, repo)
			}
		}

		// If we got fewer repos than requested, we've reached the end
		if len(repos) < perPage {
			break
		}

		page++
	}

	return allRepos, nil
}

// GetRepoNames returns just the repository names
func GetRepoNames(repos []Repository) []string {
	names := make([]string, 0, len(repos))
	for _, repo := range repos {
		names = append(names, repo.Name)
	}
	return names
}