Better config options, change package structure

Signed-off-by: jolheiser <john.olheiser@gmail.com>
pull/3/head
jolheiser 2020-09-11 20:38:18 -05:00
parent 6d7150e0a2
commit 35b809dc85
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
20 changed files with 422 additions and 247 deletions

View File

@ -4,7 +4,7 @@ VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
.PHONY: build
build:
$(GO) build -ldflags '-s -w -X "go.jolheiser.com/vanity/version.Version=$(VERSION)"'
$(GO) build -ldflags '-s -w -X "go.jolheiser.com/vanity/api.Version=$(VERSION)"'
.PHONY: fmt
fmt:
@ -16,4 +16,12 @@ test:
.PHONY: vet
vet:
$(GO) vet ./...
$(GO) vet ./...
.PHONY: docker-build
docker-build:
docker build -f docker/Dockerfile -t jolheiser/vanity .
.PHONY: docker-push
docker-push:
docker push jolheiser/vanity

View File

@ -4,6 +4,20 @@ A simple web service to serve vanity Go imports. Feel free to check it out using
## Configuration
When choosing a service, the default `base-url` will be the default server of that service:
| Service | Default |
|:-------:|:------------------:|
| Gitea | https://gitea.com |
| GitHub | https://github.com |
| GitLab | https://gitlab.com |
Configuration will be set, in order of priority
1. Flags
2. Environment
3. Config file
```
NAME:
vanity - Vanity Go Imports
@ -12,15 +26,15 @@ USAGE:
vanity [global options] command [command options] [arguments...]
VERSION:
0.1.0+2-g49cd123
0.1.0+3-g6d7150e
COMMANDS:
help, h Shows a list of commands or help for one command
GLOBAL OPTIONS:
--config value Path to a config file (default: ".vanity.toml") [$VANITY_CONFIG]
--config value Path to a config file [$VANITY_CONFIG]
--port value Port to run the vanity server on (default: 7777) [$VANITY_PORT]
--domain value Domain, e.g. go.domain.tld [$VANITY_DOMAIN]
--domain value Vanity domain, e.g. go.domain.tld [$VANITY_DOMAIN]
--service value Service type (Gitea, GitHub, GitLab) (default: "gitea") [$VANITY_SERVICE]
--base-url value Base URL to service [$VANITY_BASE_URL]
--namespace value Owner namespace [$VANITY_NAMESPACE]
@ -31,11 +45,33 @@ GLOBAL OPTIONS:
--fork Include forked repositories (default: false) [$VANITY_FORK]
--mirror Include mirrored repositories (default: false) [$VANITY_MIRROR]
--archive Include archived repositories (default: false) [$VANITY_ARCHIVE]
--override value Repository name to override (NAME=OVERRIDE) [$VANITY_OVERRIDE]
--interval value Interval between updating repositories (default: 15m0s) [$VANITY_INTERVAL]
--debug Debug logging (default: false) [$VANITY_DEBUG]
--help, -h show help (default: false)
--version, -v print the version (default: false)
```
Vanity also supports [git-import](https://gitea.com/jolheiser/git-import).
Vanity also supports [git-import](https://gitea.com/jolheiser/git-import).
## Docker
```sh
docker run \
--env VANITY_DOMAIN=go.domain.tld \
--env VANITY_NAMESPACE=<jolheiser> \
--env VANITY_TOKEN=<token> \
--publish 80:7777 \
--restart always
jolheiser/vanity:latest
```
## Overrides
Certain modules may not align perfectly with their repository name.
Overrides are available via config or by setting an environment variable `VANITY_OVERRIDE_PACKAGE=NAME`
## Config-only Mode
To run Vanity in config-only mode for packages, set `--service` to `off`.

24
api/package.go 100644
View File

@ -0,0 +1,24 @@
package api
import (
"fmt"
"strings"
)
type Package struct {
Name string `toml:"name"`
Description string `toml:"description"`
Branch string `toml:"branch"`
WebURL string `toml:"web_url"`
CloneHTTP string `toml:"clone_http"`
CloneSSH string `toml:"clone_ssh"`
Private bool `toml:"-"`
Fork bool `toml:"-"`
Mirror bool `toml:"-"`
Archive bool `toml:"-"`
}
func (p *Package) Module(domain string) string {
return fmt.Sprintf("%s/%s", domain, strings.ToLower(p.Name))
}

View File

@ -1,3 +1,3 @@
package version
package api
var Version = "develop"

View File

@ -1,12 +0,0 @@
# For each package, an entry like the following
[[package]]
# The name of the package (can be anything)
name = "Go-Vanity"
# The path to the package (this NEEDS to match the import path)
path = "vanity"
# The repository to direct go-import to
repo = "https://gitea.com/jolheiser/vanity.git"
# git-import for SSH (optional)
ssh = "git@gitea.com:jolheiser/vanity.git"
# A description of the project (optional)
description = "The code responsible for hosting this service!"

11
docker/Dockerfile 100644
View File

@ -0,0 +1,11 @@
FROM golang:1.15-alpine as builder
RUN apk --no-cache add build-base git
COPY . /app
WORKDIR /app
RUN make build
FROM alpine:latest
LABEL maintainer="john.olheiser@gmail.com"
COPY --from=builder /app/vanity vanity
EXPOSE 7777
ENTRYPOINT ["/vanity"]

View File

@ -0,0 +1,14 @@
version: "2"
services:
vanity:
image: jolheiser/vanity:latest
environment:
- VANITY_DOMAIN=go.domain.tld
- VANITY_NAMESPACE=<jolheiser>
- VANITY_TOKEN=<token>\
#- VANITY_SERVICE=gitea
#- VANITY_BASE_URL=https://gitea.com
restart: always
ports:
- "80:7777"

101
flags/config.go 100644
View File

@ -0,0 +1,101 @@
package flags
import (
"os"
"strings"
"time"
"go.jolheiser.com/vanity/api"
"github.com/BurntSushi/toml"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
type tomlConfig struct {
Port int `toml:"port"`
Domain string `toml:"domain"`
Service string `toml:"service"`
BaseURL string `toml:"base_url"`
Namespace string `toml:"namespace"`
Token string `toml:"token"`
Include []string `toml:"include"`
Exclude []string `toml:"exclude"`
Private bool `toml:"private"`
Fork bool `toml:"fork"`
Mirror bool `toml:"mirror"`
Archive bool `toml:"archive"`
Override []string `toml:"override"`
Interval time.Duration `toml:"interval"`
Debug bool `toml:"debug"`
Packages []*api.Package `toml:"packages"`
}
func setConfig(ctx *cli.Context) {
for _, env := range os.Environ() {
kv := strings.Split(env, "=")
if strings.HasPrefix(kv[0], "VANITY_OVERRIDES_") {
override := strings.ToLower(strings.TrimPrefix(kv[0], "VANITY_OVERRIDES_"))
Override[override] = kv[1]
}
}
var cfg tomlConfig
if configPath != "" {
beaver.Infof("Loading configuration from %s", configPath)
_, err := toml.DecodeFile(configPath, &cfg)
if err != nil {
beaver.Errorf("Could not load configuration from %s: %v", configPath, err)
return
}
}
if !ctx.IsSet("port") && cfg.Port > 0 {
Port = cfg.Port
}
if !ctx.IsSet("domain") && cfg.Domain != "" {
Domain = cfg.Domain
}
if !ctx.IsSet("service") && cfg.Service != "" {
Service = cfg.Service
}
if !ctx.IsSet("base-url") && cfg.BaseURL != "" {
baseURL = cfg.BaseURL
}
if !ctx.IsSet("namespace") && cfg.Namespace != "" {
Namespace = cfg.Namespace
}
if !ctx.IsSet("token") && cfg.Token != "" {
Token = cfg.Token
}
if !ctx.IsSet("include") && len(cfg.Include) > 0 {
_ = include.Set(strings.Join(cfg.Include, ","))
}
if !ctx.IsSet("exclude") && len(cfg.Exclude) > 0 {
_ = exclude.Set(strings.Join(cfg.Exclude, ","))
}
if !ctx.IsSet("override") && len(cfg.Override) > 0 {
_ = override.Set(strings.Join(cfg.Override, ","))
}
if !ctx.IsSet("private") && cfg.Private {
Private = cfg.Private
}
if !ctx.IsSet("fork") && cfg.Fork {
Fork = cfg.Fork
}
if !ctx.IsSet("mirror") && cfg.Mirror {
Mirror = cfg.Mirror
}
if !ctx.IsSet("archive") && cfg.Archive {
Archive = cfg.Archive
}
if !ctx.IsSet("interval") && cfg.Interval.Seconds() > 0 {
Interval = cfg.Interval
}
if !ctx.IsSet("debug") && cfg.Debug {
Debug = cfg.Debug
}
ConfigPackages = cfg.Packages
}

View File

@ -1,49 +1,43 @@
package flags
import (
"errors"
"fmt"
"net/url"
"os"
"regexp"
"strings"
"time"
"github.com/BurntSushi/toml"
"go.jolheiser.com/vanity/api"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
type flagConfig struct {
Port int `toml:"port"`
Domain string `toml:"domain"`
Service string `toml:"service"`
BaseURL string `toml:"base_url"`
URL *url.URL `toml:"-"`
Namespace string `toml:"namespace"`
Token string `toml:"token"`
Include []*regexp.Regexp `toml:"-"`
Exclude []*regexp.Regexp `toml:"-"`
Private bool `toml:"private"`
Fork bool `toml:"fork"`
Mirror bool `toml:"mirror"`
Archive bool `toml:"archive"`
Interval time.Duration `toml:"interval"`
Debug bool `toml:"debug"`
Overrides map[string]nameOverride `toml:"overrides"`
}
type nameOverride struct {
Name string `toml:"name"`
}
var (
Config = flagConfig{
Overrides: make(map[string]nameOverride),
}
configPath string
baseURL string
include cli.StringSlice
exclude cli.StringSlice
override cli.StringSlice
Port int
Domain string
Service string
BaseURL *url.URL
Namespace string
Token string
Include []*regexp.Regexp
Exclude []*regexp.Regexp
Private bool
Fork bool
Mirror bool
Archive bool
Override = make(map[string]string)
Interval time.Duration
Debug bool
ConfigPackages []*api.Package
)
var Flags = []cli.Flag{
@ -58,41 +52,39 @@ var Flags = []cli.Flag{
Usage: "Port to run the vanity server on",
Value: 7777,
EnvVars: []string{"VANITY_PORT"},
Destination: &Config.Port,
Destination: &Port,
},
&cli.StringFlag{
Name: "domain",
Usage: "Vanity domain, e.g. go.domain.tld",
EnvVars: []string{"VANITY_DOMAIN"},
Required: true,
Destination: &Config.Domain,
Destination: &Domain,
},
&cli.StringFlag{
Name: "service",
Usage: "Service type (Gitea, GitHub, GitLab)",
Value: "gitea",
EnvVars: []string{"VANITY_SERVICE"},
Destination: &Config.Service,
Destination: &Service,
},
&cli.StringFlag{
Name: "base-url",
Usage: "BaseURL URL to service",
Usage: "Base URL to service",
EnvVars: []string{"VANITY_BASE_URL"},
Destination: &Config.BaseURL,
Destination: &baseURL,
},
&cli.StringFlag{
Name: "namespace",
Usage: "Owner namespace",
EnvVars: []string{"VANITY_NAMESPACE"},
Required: true,
Destination: &Config.Namespace,
Destination: &Namespace,
},
&cli.StringFlag{
Name: "token",
Usage: "Access token",
EnvVars: []string{"VANITY_TOKEN"},
Required: true,
Destination: &Config.Token,
Destination: &Token,
},
&cli.StringSliceFlag{
Name: "include",
@ -110,128 +102,103 @@ var Flags = []cli.Flag{
Name: "private",
Usage: "Include private repositories",
EnvVars: []string{"VANITY_PRIVATE"},
Destination: &Config.Private,
Destination: &Private,
},
&cli.BoolFlag{
Name: "fork",
Usage: "Include forked repositories",
EnvVars: []string{"VANITY_FORK"},
Destination: &Config.Private,
Destination: &Fork,
},
&cli.BoolFlag{
Name: "mirror",
Usage: "Include mirrored repositories",
EnvVars: []string{"VANITY_MIRROR"},
Destination: &Config.Mirror,
Destination: &Mirror,
},
&cli.BoolFlag{
Name: "archive",
Usage: "Include archived repositories",
EnvVars: []string{"VANITY_ARCHIVE"},
Destination: &Config.Archive,
Destination: &Archive,
},
&cli.StringSliceFlag{
Name: "override",
Usage: "Repository name to override (NAME=OVERRIDE)",
EnvVars: []string{"VANITY_OVERRIDE"},
Destination: &override,
},
&cli.DurationFlag{
Name: "interval",
Usage: "Interval between updating repositories",
Value: time.Minute * 15,
EnvVars: []string{"VANITY_INTERVAL"},
Destination: &Config.Interval,
Destination: &Interval,
},
&cli.BoolFlag{
Name: "debug",
Usage: "Debug logging",
EnvVars: []string{"VANITY_DEBUG"},
Destination: &Config.Debug,
Destination: &Debug,
},
}
func Before(ctx *cli.Context) error {
setConfig(ctx)
if Config.BaseURL == "" {
switch strings.ToLower(Config.Service) {
case "gitea":
Config.BaseURL = "https://gitea.com"
case "github":
Config.BaseURL = "https://github.com"
case "gitlab":
Config.BaseURL = "https://gitlab.com"
}
var defaultURL string
var configOnly bool
switch strings.ToLower(Service) {
case "gitea":
defaultURL = "https://gitea.com"
case "github":
defaultURL = "https://github.com"
case "gitlab":
defaultURL = "https://gitlab.com"
case "off":
configOnly = true
beaver.Infof("Running in config-only mode")
defaultURL = "https://domain.tld"
default:
return errors.New("unrecognized service type")
}
u, err := url.Parse(Config.BaseURL)
if baseURL == "" {
baseURL = defaultURL
}
var err error
BaseURL, err = url.Parse(baseURL)
if err != nil {
return err
}
Config.URL = u
Config.Include = make([]*regexp.Regexp, len(include.Value()))
if !configOnly {
errs := make([]string, 0, 2)
if Namespace == "" {
errs = append(errs, "namespace")
}
if Token == "" {
errs = append(errs, "token")
}
if len(errs) > 0 {
return fmt.Errorf("%s is required with a service", strings.Join(errs, ", "))
}
}
Include = make([]*regexp.Regexp, len(include.Value()))
for idx, i := range include.Value() {
Config.Include[idx] = regexp.MustCompile(i)
Include[idx] = regexp.MustCompile(i)
}
Config.Exclude = make([]*regexp.Regexp, len(exclude.Value()))
Exclude = make([]*regexp.Regexp, len(exclude.Value()))
for idx, e := range exclude.Value() {
Config.Exclude[idx] = regexp.MustCompile(e)
Exclude[idx] = regexp.MustCompile(e)
}
if Config.Debug {
if Debug {
beaver.Console.Level = beaver.DEBUG
}
return nil
}
func setConfig(ctx *cli.Context) {
for _, env := range os.Environ() {
kv := strings.Split(env, "=")
if strings.HasPrefix(kv[0], "VANITY_OVERRIDES_") {
override := strings.ToLower(strings.TrimPrefix(kv[0], "VANITY_OVERRIDES_"))
Config.Overrides[override] = nameOverride{kv[1]}
}
}
var cfg flagConfig
if configPath != "" {
beaver.Infof("Loading configuration from %s", configPath)
_, err := toml.DecodeFile(configPath, &cfg)
if err != nil {
beaver.Errorf("Could not load configuration from %s: %v", configPath, err)
return
}
}
if !ctx.IsSet("port") && cfg.Port > 0 {
Config.Port = cfg.Port
}
if !ctx.IsSet("domain") && cfg.Domain != "" {
Config.Domain = cfg.Domain
}
if !ctx.IsSet("service") && cfg.Service != "" {
Config.Service = cfg.Service
}
if !ctx.IsSet("base-url") && cfg.BaseURL != "" {
Config.BaseURL = cfg.BaseURL
}
if !ctx.IsSet("namespace") && cfg.Namespace != "" {
Config.Namespace = cfg.Namespace
}
if !ctx.IsSet("token") && cfg.Token != "" {
Config.Token = cfg.Token
}
if !ctx.IsSet("private") && cfg.Private {
Config.Private = cfg.Private
}
if !ctx.IsSet("fork") && cfg.Fork {
Config.Fork = cfg.Fork
}
if !ctx.IsSet("mirror") && cfg.Mirror {
Config.Mirror = cfg.Mirror
}
if !ctx.IsSet("archive") && cfg.Archive {
Config.Archive = cfg.Archive
}
if !ctx.IsSet("debug") && cfg.Debug {
Config.Debug = cfg.Debug
}
}

View File

@ -5,9 +5,9 @@ import (
"net/http"
"os"
"go.jolheiser.com/vanity/api"
"go.jolheiser.com/vanity/flags"
"go.jolheiser.com/vanity/router"
"go.jolheiser.com/vanity/version"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
@ -17,7 +17,7 @@ func main() {
app := cli.NewApp()
app.Name = "vanity"
app.Usage = "Vanity Go Imports"
app.Version = version.Version
app.Version = api.Version
app.Action = doAction
app.Flags = flags.Flags
app.Before = flags.Before

View File

@ -3,19 +3,19 @@ package router
import (
"sync"
"go.jolheiser.com/vanity/service"
"go.jolheiser.com/vanity/api"
)
var cache = &packageCache{
packages: make(map[string]*service.Package),
packages: make(map[string]*api.Package),
}
type packageCache struct {
packages map[string]*service.Package
packages map[string]*api.Package
sync.Mutex
}
func (c *packageCache) Update(packages map[string]*service.Package) {
func (c *packageCache) Update(packages map[string]*api.Package) {
c.Lock()
c.packages = packages
c.Unlock()

View File

@ -13,7 +13,7 @@ import (
var svc service.Service
func cronStart() {
ticker := time.NewTicker(flags.Config.Interval)
ticker := time.NewTicker(flags.Interval)
for {
<-ticker.C
beaver.Debug("Running package update...")
@ -36,8 +36,16 @@ func cronUpdate() {
delete(packages, name)
continue
}
if !svc.GoMod(pkg) {
beaver.Debugf("%s isn't a Go project", pkg.Name)
goMod, err := svc.GoMod(pkg)
if err != nil {
beaver.Debugf("No go.mod could be found in the root directory of %s", pkg.Name)
delete(packages, name)
continue
}
lines := strings.Split(goMod, "\n")
line := strings.Fields(lines[0])
if !strings.HasPrefix(line[1], flags.Domain) {
beaver.Debugf("%s is a Go project, however its module does not include this domain", pkg.Name)
delete(packages, name)
continue
}
@ -46,15 +54,20 @@ func cronUpdate() {
// Overrides
for name, pkg := range packages {
for key, override := range flags.Config.Overrides {
for key, override := range flags.Override {
if strings.EqualFold(name, key) {
beaver.Debugf("Overriding %s -> %s", name, override.Name)
beaver.Debugf("Overriding %s -> %s", name, override)
delete(packages, key)
pkg.Name = override.Name
packages[override.Name] = pkg
pkg.Name = override
packages[override] = pkg
}
}
}
// Add packages manually added to config
for _, pkg := range flags.ConfigPackages {
packages[pkg.Name] = pkg
}
cache.Update(packages)
}

View File

@ -8,10 +8,10 @@ import (
"strings"
"time"
"go.jolheiser.com/vanity/api"
"go.jolheiser.com/vanity/flags"
"go.jolheiser.com/vanity/router/templates"
"go.jolheiser.com/vanity/service"
"go.jolheiser.com/vanity/version"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
@ -39,14 +39,14 @@ func Init() *chi.Mux {
beaver.Infof("Finished warming up cache: %s", cache.Names())
go cronStart()
beaver.Infof("Running vanity server at http://localhost:%d", flags.Config.Port)
beaver.Infof("Running vanity server at http://localhost:%d", flags.Port)
return r
}
func doIndex(res http.ResponseWriter, _ *http.Request) {
if err := index.Execute(res, map[string]interface{}{
"Packages": cache.packages,
"AppVer": version.Version,
"AppVer": api.Version,
"GoVer": runtime.Version(),
}); err != nil {
beaver.Error(err)
@ -63,9 +63,9 @@ func doVanity(res http.ResponseWriter, req *http.Request) {
if err := vanity.Execute(res, map[string]interface{}{
"Package": pkg,
"AppVer": version.Version,
"AppVer": api.Version,
"GoVer": runtime.Version(),
"GoSource": fmt.Sprintf("%s %s %s %s", pkg.Module(), pkg.HTTP, svc.GoDir(pkg), svc.GoFile(pkg)),
"GoSource": fmt.Sprintf("%s %s %s %s", pkg.Module(flags.Domain), pkg.CloneHTTP, svc.GoDir(pkg), svc.GoFile(pkg)),
}); err != nil {
beaver.Error(err)
}

View File

@ -11,11 +11,11 @@ var Head = `
<meta name="og:description" content="{{.Package.Description}}"/>
<!-- Go -->
<meta name="go-import" content="{{.Package.Module}} git {{.Package.HTTP}}"/>
<meta name="go-import" content="{{.Package.Module}} git {{.Package.CloneHTTP}}"/>
<meta name="go-source" content="{{.GoSource}}">
<!-- Git Import -->
<meta name="git-import" content="{{.Package.Name}} {{.Package.HTTP}} {{.Package.SSH}}"/>
<meta name="git-import" content="{{.Package.Name}} {{.Package.CloneHTTP}} {{.Package.CloneSSH}}"/>
{{end}}
<title>Vanity - {{if .Package}}{{.Package.Name}}{{else}}Index{{end}}</title>
</head>

View File

@ -4,7 +4,7 @@ var Vanity = `
<h1><a href="../">Index</a></h1>
<hr/>
<p><strong>Name: </strong>{{.Package.Name}}</p>
<p><strong>Source: </strong><a href="{{.Package.URL}}">{{.Package.URL}}</a></p>
<p><strong>Source: </strong><a href="{{.Package.WebURL}}">{{.Package.WebURL}}</a></p>
{{if .Package.Description}}<p><strong>Description: </strong>{{.Package.Description}}</p>{{end}}
<p><strong>Documentation: <a href="https://pkg.go.dev/{{.Package.Module}}">https://pkg.go.dev/{{.Package.Module}}</a></strong></p>
`

View File

@ -3,6 +3,7 @@ package service
import (
"fmt"
"go.jolheiser.com/vanity/api"
"go.jolheiser.com/vanity/flags"
"code.gitea.io/sdk/gitea"
@ -11,7 +12,7 @@ import (
var _ Service = &Gitea{}
func NewGitea() *Gitea {
client := gitea.NewClient(flags.Config.BaseURL, flags.Config.Token)
client := gitea.NewClient(flags.BaseURL.String(), flags.Token)
return &Gitea{
client: client,
}
@ -21,8 +22,8 @@ type Gitea struct {
client *gitea.Client
}
func (g Gitea) Packages() (map[string]*Package, error) {
packages := make(map[string]*Package)
func (g Gitea) Packages() (map[string]*api.Package, error) {
packages := make(map[string]*api.Package)
page := 0
for {
opts := gitea.ListReposOptions{
@ -32,23 +33,23 @@ func (g Gitea) Packages() (map[string]*Package, error) {
},
}
repos, err := g.client.ListUserRepos(flags.Config.Namespace, opts)
repos, err := g.client.ListUserRepos(flags.Namespace, opts)
if err != nil {
return nil, err
}
for _, repo := range repos {
packages[repo.Name] = &Package{
packages[repo.Name] = &api.Package{
Name: repo.Name,
Description: repo.Description,
Branch: repo.DefaultBranch,
URL: repo.HTMLURL,
HTTP: repo.CloneURL,
SSH: repo.SSHURL,
private: repo.Private,
fork: repo.Fork,
mirror: repo.Mirror,
archive: repo.Archived,
WebURL: repo.HTMLURL,
CloneHTTP: repo.CloneURL,
CloneSSH: repo.SSHURL,
Private: repo.Private,
Fork: repo.Fork,
Mirror: repo.Mirror,
Archive: repo.Archived,
}
}
@ -61,15 +62,15 @@ func (g Gitea) Packages() (map[string]*Package, error) {
return packages, nil
}
func (g Gitea) GoDir(pkg *Package) string {
return fmt.Sprintf("%s/src/branch/%s{/dir}", pkg.URL, pkg.Branch)
func (g Gitea) GoDir(pkg *api.Package) string {
return fmt.Sprintf("%s/src/branch/%s{/dir}", pkg.WebURL, pkg.Branch)
}
func (g Gitea) GoFile(pkg *Package) string {
return fmt.Sprintf("%s/src/branch/%s{/dir}/{file}#L{line}", pkg.URL, pkg.Branch)
func (g Gitea) GoFile(pkg *api.Package) string {
return fmt.Sprintf("%s/src/branch/%s{/dir}/{file}#L{line}", pkg.WebURL, pkg.Branch)
}
func (g Gitea) GoMod(pkg *Package) bool {
_, err := g.client.GetFile(flags.Config.Namespace, pkg.Name, pkg.Branch, "go.mod")
return err == nil
func (g Gitea) GoMod(pkg *api.Package) (string, error) {
content, err := g.client.GetFile(flags.Namespace, pkg.Name, pkg.Branch, "go.mod")
return string(content), err
}

View File

@ -4,6 +4,7 @@ import (
"context"
"fmt"
"go.jolheiser.com/vanity/api"
"go.jolheiser.com/vanity/flags"
"github.com/google/go-github/v32/github"
@ -14,11 +15,11 @@ var _ Service = &GitHub{}
func NewGitHub() *GitHub {
ts := oauth2.StaticTokenSource(
&oauth2.Token{AccessToken: flags.Config.Token},
&oauth2.Token{AccessToken: flags.Token},
)
client := oauth2.NewClient(context.Background(), ts)
ghClient := github.NewClient(client)
ghClient.BaseURL = flags.Config.URL
ghClient.BaseURL = flags.BaseURL
return &GitHub{
client: ghClient,
}
@ -28,8 +29,8 @@ type GitHub struct {
client *github.Client
}
func (g GitHub) Packages() (map[string]*Package, error) {
packages := make(map[string]*Package)
func (g GitHub) Packages() (map[string]*api.Package, error) {
packages := make(map[string]*api.Package)
page := 0
for {
opts := github.RepositoryListOptions{
@ -39,23 +40,23 @@ func (g GitHub) Packages() (map[string]*Package, error) {
},
}
repos, _, err := g.client.Repositories.List(context.Background(), flags.Config.Namespace, &opts)
repos, _, err := g.client.Repositories.List(context.Background(), flags.Namespace, &opts)
if err != nil {
return nil, err
}
for _, repo := range repos {
packages[repo.GetName()] = &Package{
packages[repo.GetName()] = &api.Package{
Name: repo.GetName(),
Description: repo.GetDescription(),
Branch: repo.GetDefaultBranch(),
URL: repo.GetHTMLURL(),
HTTP: repo.GetCloneURL(),
SSH: repo.GetSSHURL(),
private: repo.GetPrivate(),
fork: repo.GetFork(),
mirror: false,
archive: repo.GetArchived(),
WebURL: repo.GetHTMLURL(),
CloneHTTP: repo.GetCloneURL(),
CloneSSH: repo.GetSSHURL(),
Private: repo.GetPrivate(),
Fork: repo.GetFork(),
Mirror: false,
Archive: repo.GetArchived(),
}
}
@ -68,18 +69,21 @@ func (g GitHub) Packages() (map[string]*Package, error) {
return packages, nil
}
func (g GitHub) GoDir(pkg *Package) string {
return fmt.Sprintf("%s/tree/%s{/dir}", pkg.URL, pkg.Branch)
func (g GitHub) GoDir(pkg *api.Package) string {
return fmt.Sprintf("%s/tree/%s{/dir}", pkg.WebURL, pkg.Branch)
}
func (g GitHub) GoFile(pkg *Package) string {
return fmt.Sprintf("%s/blob/%s{/dir}/{file}#L{line}", pkg.URL, pkg.Branch)
func (g GitHub) GoFile(pkg *api.Package) string {
return fmt.Sprintf("%s/blob/%s{/dir}/{file}#L{line}", pkg.WebURL, pkg.Branch)
}
func (g GitHub) GoMod(pkg *Package) bool {
_, _, _, err := g.client.Repositories.GetContents(context.Background(), flags.Config.Namespace, pkg.Name, "go.mod",
func (g GitHub) GoMod(pkg *api.Package) (string, error) {
content, _, _, err := g.client.Repositories.GetContents(context.Background(), flags.Namespace, pkg.Name, "go.mod",
&github.RepositoryContentGetOptions{
Ref: pkg.Branch,
})
return err == nil
if err != nil {
return "", err
}
return content.GetContent()
}

View File

@ -4,6 +4,7 @@ import (
"fmt"
"html"
"go.jolheiser.com/vanity/api"
"go.jolheiser.com/vanity/flags"
"github.com/xanzy/go-gitlab"
@ -13,7 +14,7 @@ import (
var _ Service = &GitLab{}
func NewGitLab() *GitLab {
client, err := gitlab.NewClient(flags.Config.Token, gitlab.WithBaseURL(flags.Config.BaseURL))
client, err := gitlab.NewClient(flags.Token, gitlab.WithBaseURL(flags.BaseURL.String()))
if err != nil {
beaver.Errorf("could not create GitLab client: %v", err)
}
@ -26,8 +27,8 @@ type GitLab struct {
client *gitlab.Client
}
func (g GitLab) Packages() (map[string]*Package, error) {
packages := make(map[string]*Package)
func (g GitLab) Packages() (map[string]*api.Package, error) {
packages := make(map[string]*api.Package)
page := 0
for {
opts := gitlab.ListProjectsOptions{
@ -37,23 +38,23 @@ func (g GitLab) Packages() (map[string]*Package, error) {
},
}
repos, _, err := g.client.Projects.ListUserProjects(flags.Config.Namespace, &opts)
repos, _, err := g.client.Projects.ListUserProjects(flags.Namespace, &opts)
if err != nil {
return nil, err
}
for _, repo := range repos {
packages[repo.Name] = &Package{
packages[repo.Name] = &api.Package{
Name: repo.Name,
Description: repo.Description,
Branch: repo.DefaultBranch,
URL: repo.WebURL,
HTTP: repo.HTTPURLToRepo,
SSH: repo.SSHURLToRepo,
private: repo.Visibility != gitlab.PublicVisibility,
fork: repo.ForkedFromProject != nil,
mirror: repo.Mirror,
archive: repo.Archived,
WebURL: repo.WebURL,
CloneHTTP: repo.HTTPURLToRepo,
CloneSSH: repo.SSHURLToRepo,
Private: repo.Visibility != gitlab.PublicVisibility,
Fork: repo.ForkedFromProject != nil,
Mirror: repo.Mirror,
Archive: repo.Archived,
}
}
@ -66,18 +67,18 @@ func (g GitLab) Packages() (map[string]*Package, error) {
return packages, nil
}
func (g GitLab) GoDir(pkg *Package) string {
return fmt.Sprintf("%s/-/tree/%s{/dir}", pkg.URL, pkg.Branch)
func (g GitLab) GoDir(pkg *api.Package) string {
return fmt.Sprintf("%s/-/tree/%s{/dir}", pkg.WebURL, pkg.Branch)
}
func (g GitLab) GoFile(pkg *Package) string {
return fmt.Sprintf("%s/-/blob/%s{/dir}/{file}#L{line}", pkg.URL, pkg.Branch)
func (g GitLab) GoFile(pkg *api.Package) string {
return fmt.Sprintf("%s/-/blob/%s{/dir}/{file}#L{line}", pkg.WebURL, pkg.Branch)
}
func (g GitLab) GoMod(pkg *Package) bool {
id := fmt.Sprintf("%s/%s", flags.Config.Namespace, pkg.Name)
_, _, err := g.client.RepositoryFiles.GetRawFile(html.EscapeString(id), "go.mod", &gitlab.GetRawFileOptions{
func (g GitLab) GoMod(pkg *api.Package) (string, error) {
id := fmt.Sprintf("%s/%s", flags.Namespace, pkg.Name)
content, _, err := g.client.RepositoryFiles.GetRawFile(html.EscapeString(id), "go.mod", &gitlab.GetRawFileOptions{
Ref: &pkg.Branch,
})
return err == nil
return string(content), err
}

23
service/off.go 100644
View File

@ -0,0 +1,23 @@
package service
import "go.jolheiser.com/vanity/api"
var _ Service = Off{}
type Off struct{}
func (o Off) Packages() (map[string]*api.Package, error) {
return make(map[string]*api.Package), nil
}
func (o Off) GoDir(*api.Package) string {
return ""
}
func (o Off) GoFile(*api.Package) string {
return ""
}
func (o Off) GoMod(*api.Package) (string, error) {
return "", nil
}

View File

@ -4,78 +4,62 @@ import (
"fmt"
"strings"
"go.jolheiser.com/vanity/api"
"go.jolheiser.com/vanity/flags"
)
type Package struct {
Name string
Description string
Branch string
URL string
HTTP string
SSH string
private bool
fork bool
mirror bool
archive bool
}
func (p *Package) Module() string {
return fmt.Sprintf("%s/%s", flags.Config.Domain, strings.ToLower(p.Name))
}
type Service interface {
Packages() (map[string]*Package, error)
GoDir(*Package) string
GoFile(*Package) string
GoMod(*Package) bool
Packages() (map[string]*api.Package, error)
GoDir(*api.Package) string
GoFile(*api.Package) string
GoMod(*api.Package) (string, error)
}
func New() Service {
switch strings.ToLower(flags.Config.Service) {
switch strings.ToLower(flags.Service) {
case "gitea":
return NewGitea()
case "github":
return NewGitHub()
case "gitlab":
return NewGitLab()
default:
return Off{}
}
return nil
}
func Check(pkg *Package) error {
func Check(pkg *api.Package) error {
// Private
if pkg.private && !flags.Config.Private {
if pkg.Private && !flags.Private {
return fmt.Errorf("%s is private and --private wasn't used", pkg.Name)
}
// Forked
if pkg.fork && !flags.Config.Fork {
if pkg.Fork && !flags.Fork {
return fmt.Errorf("%s is a fork and --fork wasn't used", pkg.Name)
}
// Mirrored
if pkg.mirror && !flags.Config.Mirror {
if pkg.Mirror && !flags.Mirror {
return fmt.Errorf("%s is a mirror and --mirror wasn't used", pkg.Name)
}
// Archived
if pkg.archive && !flags.Config.Archive {
if pkg.Archive && !flags.Archive {
return fmt.Errorf("%s is archived and --archive wasn't used", pkg.Name)
}
// Exclusions
for _, exclude := range flags.Config.Exclude {
for _, exclude := range flags.Exclude {
if exclude.MatchString(pkg.Name) {
return fmt.Errorf("%s is was excluded by the rule %s", pkg.Name, exclude.String())
}
}
// Inclusions
if len(flags.Config.Include) > 0 {
for _, include := range flags.Config.Include {
if len(flags.Include) > 0 {
for _, include := range flags.Include {
if include.MatchString(pkg.Name) {
return fmt.Errorf("%s is was included by the rule %s", pkg.Name, include.String())
}