1
0
Fork 0
mirror of https://github.com/Luzifer/repo-template.git synced 2024-11-14 10:22:44 +00:00
repo-template/main.go
Knut Ahlers 89a4b6aafa
Add generic has-file filter
Signed-off-by: Knut Ahlers <knut@ahlers.me>
2021-04-11 13:50:32 +02:00

244 lines
5.2 KiB
Go

package main
import (
"bytes"
"context"
"fmt"
"io"
"io/ioutil"
"os"
"regexp"
"strings"
"golang.org/x/oauth2"
"github.com/Luzifer/go_helpers/v2/str"
"github.com/Luzifer/rconfig/v2"
"github.com/flosch/pongo2/v4"
"github.com/google/go-github/v34/github"
log "github.com/sirupsen/logrus"
)
var (
cfg = struct {
Blacklist []string `flag:"blacklist,b" default:"" description:"Repos to ignore even when matched through filters"`
ExpandMatches bool `flag:"expand-matches" default:"false" description:"Replace matched repos with their full version"`
FilterHasFile string `flag:"filter-has-file" default:"" description:"Input for 'has-file' filter: Repo needs to contain this file"`
Filters []string `flag:"filter,f" default:"" description:"Filters to match the repos against"`
GithubToken string `flag:"token" default:"" env:"GITHUB_TOKEN" description:"Token to access Github API"`
LogLevel string `flag:"log-level" default:"info" description:"Log level for output (debug, info, warn, error, fatal)"`
NameRegex string `flag:"name-regex" default:".*" description:"Regex to match the name against"`
Output string `flag:"out,o" default:"-" description:"File to write to (- = stdout)"`
Template string `flag:"template" default:"" description:"Template file to use for rendering" validate:"nonzero"`
TopicFilter []string `flag:"topic,t" default:"" description:"Filter by topic (Format: 'topic' to include, '-topic' to exclude)"`
VersionAndExit bool `flag:"version" default:"false" description:"Prints current version and exits"`
}{}
client *github.Client
version = "dev"
)
func init() {
rconfig.AutoEnv(true)
if err := rconfig.ParseAndValidate(&cfg); err != nil {
log.Fatalf("Unable to parse commandline options: %s", err)
}
if l, err := log.ParseLevel(cfg.LogLevel); err != nil {
log.WithError(err).Fatalf("Could not parse log level")
} else {
log.SetLevel(l)
}
if cfg.VersionAndExit {
fmt.Printf("repo-template %s\n", version)
os.Exit(0)
}
}
func main() {
ctx := context.Background()
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: cfg.GithubToken},
)
tc := oauth2.NewClient(ctx, ts)
client = github.NewClient(tc)
repos, err := fetchRepos()
if err != nil {
log.WithError(err).Fatal("Error while fetching repos")
}
for _, repo := range repos {
if !regexp.MustCompile(cfg.NameRegex).MatchString(*repo.FullName) {
continue
}
if str.StringInSlice(*repo.FullName, cfg.Blacklist) {
continue
}
if !matchTopicFilter(repo) {
continue
}
if !matchFilters(repo) {
continue
}
if cfg.ExpandMatches {
if err := expandRepo(repo); err != nil {
log.WithError(err).Error("Unable to expand repo")
}
}
log.WithFields(log.Fields{
"repo": *repo.FullName,
"private": *repo.Private,
"fork": *repo.Fork,
}).Print("Found repo")
if err := render(repo); err != nil {
log.WithError(err).Error("Unable to render output")
continue
}
}
}
func expandRepo(repo *github.Repository) error {
ctx := context.Background()
r, _, err := client.Repositories.Get(ctx, *repo.Owner.Login, *repo.Name)
if err != nil {
return err
}
*repo = *r
return nil
}
func fetchRepos() ([]*github.Repository, error) {
var (
ctx = context.Background()
repos = []*github.Repository{}
)
opt := &github.RepositoryListOptions{
ListOptions: github.ListOptions{PerPage: 100},
}
for {
rs, res, err := client.Repositories.List(ctx, "", opt)
if err != nil {
return nil, err
}
repos = append(repos, rs...)
if res.NextPage == 0 {
break
}
opt.Page = res.NextPage
}
return repos, nil
}
func matchFilters(repo *github.Repository) bool {
for _, f := range cfg.Filters {
if f == "" {
continue
}
var (
inverse = false
filter = f
)
if strings.HasPrefix(filter, "no-") {
inverse = true
filter = filter[3:]
}
if filters[filter](repo) == inverse {
log.WithFields(log.Fields{
"filter": filter,
"repo": *repo.FullName,
}).Debug("Repo was filtered")
return false
}
}
return true
}
func matchTopicFilter(repo *github.Repository) bool {
for _, topic := range cfg.TopicFilter {
if topic == "" {
continue
}
negate := topic[0] == '-'
if negate {
topic = topic[1:]
}
if str.StringInSlice(topic, repo.Topics) != negate {
return true
}
}
return false
}
func render(repo *github.Repository) error {
var outFile io.Writer
if cfg.Output == "-" {
outFile = os.Stdout
} else {
outName, err := getOutfile(repo)
if err != nil {
return err
}
log.WithFields(log.Fields{
"repo": *repo.FullName,
"outName": outName,
}).Debug("")
f, err := os.Create(outName)
if err != nil {
return err
}
defer f.Close()
outFile = f
}
tplRaw, err := ioutil.ReadFile(cfg.Template)
if err != nil {
return err
}
tpl, err := pongo2.FromString(string(tplRaw))
if err != nil {
return err
}
return tpl.ExecuteWriter(pongo2.Context{
"repo": repo,
}, outFile)
}
func getOutfile(repo *github.Repository) (string, error) {
tpl, err := pongo2.FromString(cfg.Output)
if err != nil {
return "", err
}
buf := new(bytes.Buffer)
err = tpl.ExecuteWriter(pongo2.Context{
"repo": repo,
}, buf)
return buf.String(), err
}