Better config options, change package structure
Signed-off-by: jolheiser <john.olheiser@gmail.com>pull/3/head
parent
6d7150e0a2
commit
35b809dc85
10
Makefile
10
Makefile
|
@ -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:
|
||||
|
@ -17,3 +17,11 @@ test:
|
|||
.PHONY: vet
|
||||
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
|
44
README.md
44
README.md
|
@ -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).
|
||||
|
||||
## 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`.
|
|
@ -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))
|
||||
}
|
|
@ -1,3 +1,3 @@
|
|||
package version
|
||||
package api
|
||||
|
||||
var Version = "develop"
|
|
@ -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!"
|
|
@ -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"]
|
|
@ -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"
|
|
@ -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
|
||||
}
|
197
flags/flags.go
197
flags/flags.go
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
4
main.go
4
main.go
|
@ -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
|
||||
|
|
|
@ -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()
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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>
|
||||
|
|
|
@ -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>
|
||||
`
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue