package main
import (
"context"
"flag"
"fmt"
"log"
"os"
"code.gitea.io/sdk/gitea"
)
func main() {
baseURL := flag.String("base-url", os.Getenv("GITEA_BASE_URL"), "Gitea base URL, e.g. https://gitea.example.com")
token := flag.String("token", os.Getenv("GITEA_TOKEN"), "Gitea admin API token")
doDelete := flag.Bool("delete", false, "Actually delete empty repos (default is dry-run)")
pageSize := flag.Int("page-size", 50, "Repos per page")
flag.Parse()
if *baseURL == "" {
log.Fatal("base URL must be provided via -base-url or GITEA_BASE_URL")
}
if *token == "" {
log.Fatal("token must be provided via -token or GITEA_TOKEN")
}
client, err := gitea.NewClient(*baseURL, gitea.SetToken(*token))
if err != nil {
log.Fatalf("failed to create Gitea client: %v", err)
}
client.SetContext(context.Background())
var emptyRepos []*gitea.Repository
page := 1
for {
repos, resp, err := client.AdminListRepos(gitea.ListReposOptions{
ListOptions: gitea.ListOptions{
Page: page,
PageSize: *pageSize,
},
})
if err != nil {
log.Fatalf("AdminListRepos failed on page %d: %v", page, err)
}
if len(repos) == 0 {
break
}
for _, r := range repos {
if r.Empty {
emptyRepos = append(emptyRepos, r)
}
}
if resp == nil || resp.NextPage == 0 {
break
}
page = resp.NextPage
}
if len(emptyRepos) == 0 {
fmt.Println("✅ No empty repositories found.")
return
}
fmt.Printf("⚠️ Found %d empty repositories:\n", len(emptyRepos))
for _, r := range emptyRepos {
owner := "<unknown>"
if r.Owner != nil {
owner = r.Owner.UserName
}
fmt.Printf("- %s/%s (ID=%d)\n", owner, r.Name, r.ID)
}
if !*doDelete {
fmt.Println("\n🧪 Dry run only. Re-run with -delete to actually delete these repos.")
return
}
fmt.Println("\n🔥 Deleting empty repositories...")
for _, r := range emptyRepos {
if r.Owner == nil {
fmt.Printf("Skipping %s (ID=%d): missing owner\n", r.Name, r.ID)
continue
}
owner := r.Owner.UserName
fmt.Printf("Deleting %s/%s ... ", owner, r.Name)
if _, err := client.DeleteRepo(owner, r.Name); err != nil {
fmt.Printf("ERROR: %v\n", err)
continue
}
fmt.Println("OK")
}
fmt.Println("✅ Done.")
}