summaryrefslogtreecommitdiff
path: root/internal/github/github.go
diff options
context:
space:
mode:
authorPaul Buetow <paul@buetow.org>2025-06-23 23:51:54 +0300
committerPaul Buetow <paul@buetow.org>2025-06-23 23:51:54 +0300
commitca38df1f30ddedbbdbf73d1f8d4ddd98b12d3740 (patch)
tree322d1cf903fbc86b1383361cf0370b878b61768e /internal/github/github.go
parent2caf07a82d89c8f3e0ecb3bdc6662bd757600b22 (diff)
Add --sync-github-public flag to sync GitHub repos to Codeberg
- Implement GitHub API client methods to list public repositories - Add --sync-github-public flag to sync all public GitHub repos to Codeberg - Add --create-codeberg-repos flag (placeholder for future implementation) - Support pagination for GitHub API to handle users with many repos - Filter GitHub repos to exclude forks, archived, and private repos - Update README with new sync direction and features - Add dry-run support for GitHub->Codeberg sync 🤖 Generated with [Claude Code](https://claude.ai/code) Co-Authored-By: Claude <noreply@anthropic.com>
Diffstat (limited to 'internal/github/github.go')
-rw-r--r--internal/github/github.go75
1 files changed, 75 insertions, 0 deletions
diff --git a/internal/github/github.go b/internal/github/github.go
index 38aafa0..7d6213a 100644
--- a/internal/github/github.go
+++ b/internal/github/github.go
@@ -197,4 +197,79 @@ func (c *Client) CreateRepo(repoName, description string, private bool) error {
// HasToken returns whether a token is configured
func (c *Client) HasToken() bool {
return c.token != ""
+}
+
+// Repository represents a GitHub repository
+type Repository struct {
+ Name string `json:"name"`
+ Description string `json:"description"`
+ Private bool `json:"private"`
+ Fork bool `json:"fork"`
+ Archived bool `json:"archived"`
+ Disabled bool `json:"disabled"`
+ Size int `json:"size"`
+}
+
+// ListPublicRepos lists all public repositories for the user/org
+func (c *Client) ListPublicRepos() ([]Repository, error) {
+ if c.token == "" {
+ return nil, fmt.Errorf("GitHub token required to list repositories")
+ }
+
+ var allRepos []Repository
+ page := 1
+ perPage := 100
+
+ for {
+ url := fmt.Sprintf("https://api.github.com/users/%s/repos?page=%d&per_page=%d&type=owner", c.org, page, perPage)
+ fmt.Printf(" Fetching page %d...\n", page)
+
+ req, err := http.NewRequest("GET", url, nil)
+ if err != nil {
+ return nil, err
+ }
+
+ req.Header.Set("Authorization", "Bearer "+c.token)
+ req.Header.Set("Accept", "application/vnd.github.v3+json")
+
+ resp, err := http.DefaultClient.Do(req)
+ if err != nil {
+ return nil, err
+ }
+ defer resp.Body.Close()
+
+ if resp.StatusCode != 200 {
+ body, _ := io.ReadAll(resp.Body)
+ return nil, fmt.Errorf("failed to list repos: status %d: %s", resp.StatusCode, string(body))
+ }
+
+ var repos []Repository
+ if err := json.NewDecoder(resp.Body).Decode(&repos); err != nil {
+ return nil, fmt.Errorf("failed to decode response: %w", err)
+ }
+
+ // Filter for public, non-fork, non-archived, non-empty repos
+ for _, repo := range repos {
+ if !repo.Private && !repo.Fork && !repo.Archived && !repo.Disabled && repo.Size > 0 {
+ allRepos = append(allRepos, repo)
+ }
+ }
+
+ // Check if there are more pages
+ if len(repos) < perPage {
+ break
+ }
+ page++
+ }
+
+ return allRepos, nil
+}
+
+// GetRepoNames extracts repository names from a list of repos
+func GetRepoNames(repos []Repository) []string {
+ names := make([]string, len(repos))
+ for i, repo := range repos {
+ names[i] = repo.Name
+ }
+ return names
} \ No newline at end of file