Compare commits

..

No commits in common. "main" and "v0.2.1" have entirely different histories.
main ... v0.2.1

31 changed files with 519 additions and 386 deletions

23
.golangci.yml 100644
View File

@ -0,0 +1,23 @@
linters:
enable:
- deadcode
- dogsled
- dupl
- errcheck
- funlen
- gocognit
- goconst
- gocritic
- gocyclo
- gofmt
- golint
- gosimple
- govet
- misspell
- prealloc
- staticcheck
- structcheck
- typecheck
- unparam
- unused
- varcheck

View File

@ -1,26 +0,0 @@
builds:
- env:
- CGO_ENABLED=0
goos:
- linux
- windows
- darwin
main: ./cmd/gpm
ldflags:
- "-s -w -X main.Version={{.Version}}"
archives:
- replacements:
386: i386
amd64: x86_64
format_overrides:
- goos: windows
format: zip
checksum:
name_template: 'checksums.txt'
release:
gitea:
owner: jolheiser
name: gpm
gitea_urls:
api: https://git.jojodev.com/api/v1/
download: https://git.jojodev.com

View File

@ -1,39 +0,0 @@
clone:
git:
image: woodpeckerci/plugin-git
settings:
tags: true
pipeline:
compliance:
image: golang:1.18
commands:
- go test -race ./...
- go vet ./...
- go run github.com/rs/zerolog/cmd/lint@latest go.jolheiser.com/gpm/cmd/gpm
when:
event: pull_request
build:
image: goreleaser/goreleaser
commands:
- goreleaser build --snapshot
when:
event: pull_request
release:
image: goreleaser/goreleaser
commands:
- goreleaser release
secrets: [ gitea_token ]
when:
event: tag
prune:
image: jolheiser/drone-gitea-prune
settings:
base: https://git.jojodev.com
token:
from_secret: gitea_token
when:
event: tag

66
DOCS.md
View File

@ -1,66 +0,0 @@
# NAME
gpm - Go Package Manager
# SYNOPSIS
gpm
```
[--database|-d]=[value]
[--server|-s]=[value]
[--token|-t]=[value]
```
**Usage**:
```
gpm [GLOBAL OPTIONS] command [COMMAND OPTIONS] [ARGUMENTS...]
```
# GLOBAL OPTIONS
**--database, -d**="": path to gpm database for server (default: /home/jolheiser/.config/gpm.db)
**--server, -s**="": gpm server to use (default: https://gpm.jolheiser.com)
**--token, -t**="": gpm auth token to use
# COMMANDS
## add, a
Add a package
**--force, -f**: Overwrite existing package without prompt
**--local, -l**: local mode
## get, g
Get package(s)
## list, ls, l
List local packages
## remove, rm
Remove package(s)
## search, s
Search packages
## server, web
Start the gpm server
**--port, -p**="": Port to run the gpm server on (default: 3333)
## update, u
Update a package
**--local, -l**: local mode

14
Earthfile 100644
View File

@ -0,0 +1,14 @@
# To lint, install Earthly and run `earth +lint`
# This ensures the usage of the same version of golangci-lint
FROM golangci/golangci-lint:v1.37
WORKDIR /gpm
lint-cli:
COPY . .
RUN golangci-lint run
lint-lib:
COPY ./go-gpm .
RUN golangci-lint run

41
Makefile 100644
View File

@ -0,0 +1,41 @@
GO ?= go
VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
.PHONY: build
build:
$(GO) build -ldflags '-s -w -X "go.jolheiser.com/gpm/router.Version=$(VERSION)"'
.PHONY: lint
lint:
earth +lint-cli
earth +lint-lib
.PHONY: fmt
fmt: fmt-cli fmt-lib
.PHONY: test
test: test-cli test-lib
.PHONY: fmt-cli
fmt-cli:
$(GO) fmt ./...
.PHONY: test-cli
test-cli:
$(GO) test -race ./...
.PHONY: fmt-lib
fmt-lib:
@cd go-gpm && $(GO) fmt ./...
.PHONY: test-lib
test-lib:
@cd go-gpm && $(GO) test -race ./...
.PHONY: docker-build
docker-build:
docker build -f docker/Dockerfile -t jolheiser/gpm .
.PHONY: docker-push
docker-push: docker-build
docker push jolheiser/gpm

View File

@ -18,7 +18,7 @@ Using either a GPM server or local config, I can instead `gpm get cli` which fin
* `list` - List local packages
* `config` - Change local configuration
* `get` - Get a list of packages
* e.g. `gpm get zerolog survey bbolt cli chi` to get all the modules needed for gpm itself (assuming the map resolves to the same packages)
* e.g. `gpm get beaver survey bbolt cli chi` to get all the modules needed for gpm itself (assuming the map resolves to the same packages)
* `server` - Start a gpm server

View File

@ -1,16 +1,17 @@
package cli
package cmd
import (
"context"
"regexp"
"strings"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/cmd/flags"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/go-gpm"
"github.com/AlecAivazis/survey/v2"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Add = cli.Command{
@ -22,13 +23,13 @@ var Add = cli.Command{
Name: "force",
Aliases: []string{"f"},
Usage: "Overwrite existing package without prompt",
Destination: &forceFlag,
Destination: &flags.Force,
},
&cli.BoolFlag{
Name: "local",
Aliases: []string{"l"},
Usage: "local mode",
Destination: &localFlag,
Destination: &flags.Local,
},
},
Before: localOrToken,
@ -69,8 +70,8 @@ func doAdd(_ *cli.Context) error {
Import: goGetAnswer,
}
if localFlag {
db, err := database.Load(databaseFlag)
if flags.Local {
db, err := database.Load(flags.Database)
if err != nil {
return err
}
@ -78,12 +79,12 @@ func doAdd(_ *cli.Context) error {
return err
}
} else {
client := gpm.New(tokenFlag, gpm.WithServer(serverFlag))
client := gpm.New(flags.Token, gpm.WithServer(flags.Server))
if err := client.Add(context.Background(), pkg); err != nil {
return err
}
}
log.Info().Msgf("Added %q", nameAnswer)
beaver.Infof("Added %s", yellow.Format(nameAnswer))
return nil
}

View File

@ -1,4 +1,4 @@
package cli
package cmd
import (
"context"
@ -6,17 +6,22 @@ import (
"os"
"path/filepath"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/cmd/flags"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/go-gpm"
"go.jolheiser.com/gpm/router"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver/color"
)
func New(version string) *cli.App {
var yellow = color.FgYellow
func New() *cli.App {
app := cli.NewApp()
app.Name = "gpm"
app.Usage = "Go Package Manager"
app.Version = version
app.Version = router.Version
app.Commands = []*cli.Command{
&Add,
&Get,
@ -33,7 +38,7 @@ func New(version string) *cli.App {
Usage: "gpm server to use",
Value: gpm.DefaultServer,
EnvVars: []string{"GPM_SERVER"},
Destination: &serverFlag,
Destination: &flags.Server,
},
&cli.StringFlag{
Name: "token",
@ -41,16 +46,16 @@ func New(version string) *cli.App {
Usage: "gpm auth token to use",
DefaultText: "${GPM_TOKEN}",
EnvVars: []string{"GPM_TOKEN"},
Destination: &tokenFlag,
Destination: &flags.Token,
},
&cli.StringFlag{
Name: "database",
Aliases: []string{"d"},
Usage: "path to gpm database for server",
Value: dbPath(),
DefaultText: "`${USERCONFIG}/gpm.db` or `${BINPATH}/gpm.db`",
DefaultText: "`${HOME}/gpm.db` or `${BINPATH}/gpm.db`",
EnvVars: []string{"GPM_DATABASE"},
Destination: &databaseFlag,
Destination: &flags.Database,
},
}
return app
@ -58,7 +63,7 @@ func New(version string) *cli.App {
func dbPath() string {
fn := "gpm.db"
home, err := os.UserConfigDir()
home, err := os.UserHomeDir()
if err != nil {
bin, err := os.Executable()
if err != nil {
@ -70,7 +75,7 @@ func dbPath() string {
}
func localOrToken(_ *cli.Context) error {
if localFlag && tokenFlag == "" {
if flags.Local && flags.Token == "" {
return errors.New("server interaaction requires --token")
}
return nil
@ -78,8 +83,8 @@ func localOrToken(_ *cli.Context) error {
func listPackages() ([]gpm.Package, error) {
var pkgs []gpm.Package
if localFlag {
db, err := database.Load(databaseFlag)
if flags.Local {
db, err := database.Load(flags.Database)
if err != nil {
return pkgs, err
}
@ -88,7 +93,7 @@ func listPackages() ([]gpm.Package, error) {
return pkgs, err
}
} else {
client := gpm.New(tokenFlag, gpm.WithServer(serverFlag))
client := gpm.New(flags.Token, gpm.WithServer(flags.Server))
info, err := client.Info(context.Background())
if err != nil {
return pkgs, err

11
cmd/flags/flags.go 100644
View File

@ -0,0 +1,11 @@
package flags
var (
Server string
Token string
Database string
Local bool
Force bool
Port int
)

View File

@ -1,4 +1,4 @@
package cli
package cmd
import (
"context"
@ -6,11 +6,12 @@ import (
"os/exec"
"strings"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/cmd/flags"
"go.jolheiser.com/gpm/go-gpm"
"github.com/AlecAivazis/survey/v2"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Get = cli.Command{
@ -35,17 +36,17 @@ func doGet(ctx *cli.Context) error {
pkgs = strings.Split(pkgsAnswer, "\n")
}
client := gpm.New(tokenFlag, gpm.WithServer(serverFlag))
client := gpm.New(flags.Token, gpm.WithServer(flags.Server))
for _, p := range pkgs {
pkg, err := client.Get(context.Background(), p)
if err != nil {
log.Err(err).Msg("")
beaver.Error(err)
continue
}
log.Info().Msgf("getting %q...", pkg.Import)
beaver.Infof("getting `%s`...", pkg)
if err := goGet(pkg.Import); err != nil {
log.Err(err).Msg("")
beaver.Error(err)
}
}

View File

@ -1,20 +0,0 @@
package main
import (
"os"
"go.jolheiser.com/gpm/internal/cli"
"github.com/rs/zerolog"
"github.com/rs/zerolog/log"
)
var Version = "develop"
func main() {
log.Logger = log.Output(zerolog.ConsoleWriter{Out: os.Stderr})
if err := cli.New(Version).Run(os.Args); err != nil {
log.Err(err).Msg("")
}
}

View File

@ -1,4 +1,4 @@
package cli
package cmd
import (
"fmt"

View File

@ -1,14 +1,15 @@
package cli
package cmd
import (
"context"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/cmd/flags"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/go-gpm"
"github.com/AlecAivazis/survey/v2"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Remove = cli.Command{
@ -47,8 +48,8 @@ func doRemove(_ *cli.Context) error {
Import: pkgMap[pkgName].Import,
}
if localFlag {
db, err := database.Load(databaseFlag)
if flags.Local {
db, err := database.Load(flags.Database)
if err != nil {
return err
}
@ -56,12 +57,12 @@ func doRemove(_ *cli.Context) error {
return err
}
} else {
client := gpm.New(tokenFlag, gpm.WithServer(serverFlag))
client := gpm.New(flags.Token, gpm.WithServer(flags.Server))
if err := client.Remove(context.Background(), pkg); err != nil {
return err
}
}
log.Info().Msgf("Removed %q", pkgName)
beaver.Infof("Removed %s", yellow.Format(pkgName))
return nil
}

View File

@ -1,11 +1,11 @@
package cli
package cmd
import (
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/go-gpm"
"github.com/AlecAivazis/survey/v2"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Search = cli.Command{
@ -41,12 +41,12 @@ func doSearch(_ *cli.Context) error {
for _, name := range a {
pkg, ok := pkgMap[name]
if !ok {
log.Error().Msgf("could not find package for %q", name)
beaver.Errorf("could not find package for `%s`", name)
continue
}
log.Info().Msgf("getting %q...", name)
beaver.Infof("getting `%s`...", name)
if err := goGet(pkg.Import); err != nil {
log.Err(err).Msg("")
beaver.Error(err)
}
}

48
cmd/server.go 100644
View File

@ -0,0 +1,48 @@
package cmd
import (
"errors"
"fmt"
"net/http"
"go.jolheiser.com/gpm/cmd/flags"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/router"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Server = cli.Command{
Name: "server",
Aliases: []string{"web"},
Usage: "Start the gpm server",
Flags: []cli.Flag{
&cli.IntFlag{
Name: "port",
Aliases: []string{"p"},
Usage: "Port to run the gpm server on",
Value: 3333,
EnvVars: []string{"GPM_PORT"},
Destination: &flags.Port,
},
},
Action: doServer,
}
func doServer(_ *cli.Context) error {
if flags.Token == "" {
return errors.New("gpm server requires --token")
}
db, err := database.Load(flags.Database)
if err != nil {
beaver.Fatalf("could not load database at %s: %v", flags.Database, err)
}
beaver.Infof("Running gpm server at http://localhost:%d", flags.Port)
if err := http.ListenAndServe(fmt.Sprintf(":%d", flags.Port), router.New(flags.Token, db)); err != nil {
return err
}
return nil
}

View File

@ -1,14 +1,15 @@
package cli
package cmd
import (
"context"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/cmd/flags"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/go-gpm"
"github.com/AlecAivazis/survey/v2"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
"go.jolheiser.com/beaver"
)
var Update = cli.Command{
@ -20,7 +21,7 @@ var Update = cli.Command{
Name: "local",
Aliases: []string{"l"},
Usage: "local mode",
Destination: &localFlag,
Destination: &flags.Local,
},
},
Before: localOrToken,
@ -65,8 +66,8 @@ func doUpdate(_ *cli.Context) error {
Import: importPath,
}
if localFlag {
db, err := database.Load(databaseFlag)
if flags.Local {
db, err := database.Load(flags.Database)
if err != nil {
return err
}
@ -74,12 +75,12 @@ func doUpdate(_ *cli.Context) error {
return err
}
} else {
client := gpm.New(tokenFlag, gpm.WithServer(serverFlag))
client := gpm.New(flags.Token, gpm.WithServer(flags.Server))
if err := client.Update(context.Background(), pkg); err != nil {
return err
}
}
log.Info().Msgf("Updated %q", pkgName)
beaver.Infof("Updated %s", yellow.Format(pkgName))
return nil
}

View File

@ -6,7 +6,7 @@ import (
"os"
"path/filepath"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/go-gpm"
"go.etcd.io/bbolt"
)

View File

@ -5,7 +5,7 @@ import (
"path/filepath"
"testing"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/go-gpm"
)
var db *Database
@ -33,6 +33,7 @@ func TestMain(m *testing.M) {
}
func TestPackage(t *testing.T) {
// Does not exist
_, err := db.Package("test")
if err == nil {

View File

@ -2,7 +2,7 @@ FROM golang:1.16-alpine as builder
RUN apk --no-cache add build-base git
COPY . /app
WORKDIR /app
RUN go build ./cmd/gpm
RUN make build
FROM alpine:latest
LABEL maintainer="john.olheiser@gmail.com"

13
docs.go
View File

@ -1,22 +1,25 @@
//go:build generate
// +build generate
//+build docs
package main
import (
"go.jolheiser.com/gpm/internal/cli"
"os"
"strings"
"go.jolheiser.com/gpm/cmd"
)
//go:generate go run docs.go
func main() {
app := cli.New("docs")
app := cmd.New()
md, err := app.ToMarkdown()
if err != nil {
panic(err)
}
// FIXME Why is this not fixed yet??
md = md[strings.Index(md, "#"):]
fi, err := os.Create("DOCS.md")
if err != nil {
panic(err)

207
go-gpm/gpm_test.go 100644
View File

@ -0,0 +1,207 @@
package gpm
import (
"context"
"encoding/json"
"net/http"
"net/http/httptest"
"os"
"strings"
"testing"
)
var (
server *httptest.Server
token = "TestingLibrary"
version = "GPMTest"
packages = []Package{
{
Name: "test1",
Import: "gitea.com/test/testing",
},
}
)
func TestMain(m *testing.M) {
server = httptest.NewServer(http.HandlerFunc(testServer))
os.Exit(m.Run())
}
func TestClient(t *testing.T) {
ctx := context.Background()
client := New("", WithServer(server.URL))
// Info
checkInfo(t, client, 1)
pkg1 := Package{
Name: "test1",
Import: "gitea.com/test/testing",
}
pkg2 := Package{
Name: "test2",
Import: "gitea.com/testing/test",
}
// Add (without token)
if err := client.Add(ctx, pkg1); err == nil {
t.Log("adding without token should fail")
t.Fail()
}
// Add (with token)
client = New(token, WithServer(server.URL))
checkAdd(t, client, pkg1, pkg2)
// Info (after second package)
checkInfo(t, client, 2)
// Check package
checkGet(t, client, pkg2)
// Update package
checkUpdate(t, client, pkg1)
// Remove
checkRemove(t, client, pkg1)
// Info (final)
checkInfo(t, client, 1)
}
func checkInfo(t *testing.T, client *Client, numPackages int) {
info, err := client.Info(context.Background())
if err != nil {
t.Logf("info should not return error: %v\n", err)
t.Fail()
}
if info.Version != version || info.NumPackages != numPackages {
t.Log("info did not match expected")
t.Fail()
}
}
func checkGet(t *testing.T, client *Client, pkg Package) {
ctx := context.Background()
_, err := client.Get(ctx, "test3")
if err == nil {
t.Log("should not be able to get invalid package")
t.Fail()
}
// Check valid package
p, err := client.Get(ctx, "test2")
if err != nil {
t.Logf("should not be able to get invalid package: %v\n", err)
t.Fail()
}
if p != pkg {
t.Log("valid package should match pkg")
t.Fail()
}
}
func checkAdd(t *testing.T, client *Client, pkg1, pkg2 Package) {
ctx := context.Background()
if err := client.Add(ctx, pkg2); err != nil {
t.Logf("pkg2 should be added: %v\n", err)
t.Fail()
}
// Duplicate package
if err := client.Add(ctx, pkg1); err == nil {
t.Log("pkg1 should already exist")
t.Fail()
}
}
func checkUpdate(t *testing.T, client *Client, pkg Package) {
ctx := context.Background()
// Update invalid package
if err := client.Update(ctx, Package{Name: "test4", Import: "gitea.com/invalid"}); err == nil {
t.Log("should not be able to update invalid package")
t.Fail()
}
// Update valid package
pkg.Import = "gitea.com/tester/testing"
if err := client.Update(ctx, pkg); err != nil {
t.Logf("should be able to update valid package: %v\n", err)
t.Fail()
}
}
func checkRemove(t *testing.T, client *Client, pkg Package) {
ctx := context.Background()
if err := client.Remove(ctx, pkg); err != nil {
t.Logf("should be able to remove package: %v\n", err)
t.Fail()
}
// Remove (idempotent)
if err := client.Remove(ctx, pkg); err != nil {
t.Logf("should be able to remove package idempotently: %v\n", err)
t.Fail()
}
}
func testServer(w http.ResponseWriter, r *http.Request) {
switch r.URL.Path {
case "/":
switch r.Method {
case http.MethodGet:
resp := Info{
Version: version,
NumPackages: len(packages),
Packages: packages,
}
_ = json.NewEncoder(w).Encode(resp)
case http.MethodPost, http.MethodPatch, http.MethodDelete:
if r.Header.Get(TokenHeader) != token {
w.WriteHeader(http.StatusUnauthorized)
return
}
var pkg Package
if err := json.NewDecoder(r.Body).Decode(&pkg); err != nil {
w.WriteHeader(http.StatusInternalServerError)
return
}
switch r.Method {
case http.MethodPost:
for _, p := range packages {
if p.Name == pkg.Name {
w.WriteHeader(http.StatusConflict)
return
}
}
packages = append(packages, pkg)
w.WriteHeader(http.StatusCreated)
case http.MethodPatch:
for idx, p := range packages {
if p.Name == pkg.Name {
packages[idx] = pkg
return
}
}
w.WriteHeader(http.StatusNotFound)
case http.MethodDelete:
for idx, p := range packages {
if p.Name == pkg.Name {
packages = append(packages[:idx], packages[idx+1:]...)
}
}
}
return
}
return
default:
name := strings.TrimPrefix(r.URL.Path, "/")
for _, pkg := range packages {
if pkg.Name == name {
_ = json.NewEncoder(w).Encode(pkg)
return
}
}
}
w.WriteHeader(http.StatusNotImplemented)
}

24
go.mod
View File

@ -3,19 +3,17 @@ module go.jolheiser.com/gpm
go 1.15
require (
github.com/AlecAivazis/survey/v2 v2.3.5
github.com/go-chi/chi/v5 v5.0.7
github.com/go-chi/render v1.0.1
github.com/kr/pty v1.1.4 // indirect
github.com/AlecAivazis/survey/v2 v2.2.7
github.com/cpuguy83/go-md2man/v2 v2.0.0 // indirect
github.com/go-chi/chi v1.5.2
github.com/mattn/go-colorable v0.1.8 // indirect
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d // indirect
github.com/rs/zerolog v1.27.0
github.com/russross/blackfriday/v2 v2.1.0 // indirect
github.com/shurcooL/sanitized_anchor_name v1.0.0 // indirect
github.com/urfave/cli/v2 v2.11.1
go.etcd.io/bbolt v1.3.6
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa // indirect
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f // indirect
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 // indirect
golang.org/x/tools v0.1.7 // indirect
gopkg.in/yaml.v2 v2.2.3 // indirect
github.com/urfave/cli/v2 v2.3.0
go.etcd.io/bbolt v1.3.5
go.jolheiser.com/beaver v1.1.1
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad // indirect
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 // indirect
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf // indirect
golang.org/x/text v0.3.5 // indirect
)

92
go.sum
View File

@ -1,31 +1,18 @@
github.com/AlecAivazis/survey/v2 v2.2.7 h1:5NbxkF4RSKmpywYdcRgUmos1o+roJY8duCLZXbVjoig=
github.com/AlecAivazis/survey/v2 v2.2.7/go.mod h1:9DYvHgXtiXm6nCn+jXnOXLKbH+Yo9u8fAS/SduGdoPk=
github.com/AlecAivazis/survey/v2 v2.3.5 h1:A8cYupsAZkjaUmhtTYv3sSqc7LO5mp1XDfqe5E/9wRQ=
github.com/AlecAivazis/survey/v2 v2.3.5/go.mod h1:4AuI9b7RjAR+G7v9+C4YSlX/YL3K3cWNXgWXOhllqvI=
github.com/BurntSushi/toml v0.3.1 h1:WXkYYl6Yr3qBf1K79EBnL4mak0OimBfB0XUf9Vl28OQ=
github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU=
github.com/BurntSushi/toml v1.1.0/go.mod h1:CxXYINrC8qIiEnFrOxCa7Jy5BFHlXnUU2pbicEuybxQ=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8 h1:xzYJEypr/85nBpB11F9br+3HUrpgb+fcm5iADzXXYEw=
github.com/Netflix/go-expect v0.0.0-20180615182759-c93bf25de8e8/go.mod h1:oX5x61PbNXchhh0oikYAH+4Pcfw5LKv21+Jnpr6r6Pc=
github.com/Netflix/go-expect v0.0.0-20220104043353-73e0943537d2/go.mod h1:HBCaDeC1lPdgDeDbhX8XFpy1jqjK0IBG8W5K+xYqA0w=
github.com/coreos/go-systemd/v22 v22.3.2/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/coreos/go-systemd/v22 v22.3.3-0.20220203105225-a9a7ef127534/go.mod h1:Y58oyj3AT4RCenI/lSvhwexgC+NSVTIJ3seZv2GcEnc=
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.0 h1:EoUDS0afbrsXAZ9YQ9jdu/mZ2sXgT1/2yyNng4PGlyM=
github.com/cpuguy83/go-md2man/v2 v2.0.0/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
github.com/cpuguy83/go-md2man/v2 v2.0.2 h1:p1EgwI/C7NhT0JmVkwCD2ZBK8j4aeHQX2pMHHBfMQ6w=
github.com/cpuguy83/go-md2man/v2 v2.0.2/go.mod h1:tgQtvFlXSQOSOSIRvRPT7W67SCa46tRHOmNcaadrF8o=
github.com/creack/pty v1.1.17/go.mod h1:MOBLtS5ELjhRRrroQr9kyvTxUAFNvYEK993ew/Vr4O4=
github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
github.com/go-chi/chi/v5 v5.0.7 h1:rDTPXLDHGATaeHvVlLcR4Qe0zftYethFucbjVQ1PxU8=
github.com/go-chi/chi/v5 v5.0.7/go.mod h1:DslCQbL2OYiznFReuXYUmQ2hGd1aDpCnlMNITLSKoi8=
github.com/go-chi/render v1.0.1 h1:4/5tis2cKaNdnv9zFLfXzcquC9HbeZgCnxGnKrltBS8=
github.com/go-chi/render v1.0.1/go.mod h1:pq4Rr7HbnsdaeHagklXub+p6Wd16Af5l9koip1OvJns=
github.com/godbus/dbus/v5 v5.0.4/go.mod h1:xhWf0FNVPg57R7Z0UbKHbJfkEywrmjJnf7w5xrFpKfA=
github.com/go-chi/chi v1.5.2 h1:YcLIBANL4OTaAOcTdp//sskGa0yGACQMCtbnr7YEn0Q=
github.com/go-chi/chi v1.5.2/go.mod h1:REp24E+25iKvxgeTfHmdUoL5x15kBiDBlnIl5bCwe2k=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174 h1:WlZsjVhE8Af9IcZDGgJGQpNflI3+MJSBhsgT5PCtzBQ=
github.com/hinshun/vt10x v0.0.0-20180616224451-1954e6464174/go.mod h1:DqJ97dSdRW1W22yXSB90986pcOyQ7r45iio1KN2ez1A=
github.com/hinshun/vt10x v0.0.0-20220119200601-820417d04eec/go.mod h1:Q48J4R4DvxnHolD5P8pOtXigYlRuPLGl6moFx3ulM68=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=
github.com/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51/go.mod h1:CzGEWj7cYgsdH8dAjBGEr58BoE7ScuLd+fwFZ44+/x8=
github.com/kr/pty v1.1.4 h1:5Myjjh3JY/NaAi4IsUbHADytDyl1VE1Y9PXDlL+P/VQ=
@ -33,91 +20,54 @@ github.com/kr/pty v1.1.4/go.mod h1:pFQYn66WHrOpPYNljwOMqo10TkYh1fy3cYio2l3bCsQ=
github.com/mattn/go-colorable v0.1.2/go.mod h1:U0ppj6V5qS13XJ6of8GYAs25YV2eR4EVcfRqFIhoBtE=
github.com/mattn/go-colorable v0.1.8 h1:c1ghPdyEDarC70ftn0y+A/Ee++9zz8ljHG1b13eJ0s8=
github.com/mattn/go-colorable v0.1.8/go.mod h1:u6P/XSegPjTcexA+o6vUJrdnUu04hMope9wVRipJSqc=
github.com/mattn/go-colorable v0.1.12 h1:jF+Du6AlPIjs2BiUiQlKOX0rt3SujHxPnksPKZbaA40=
github.com/mattn/go-colorable v0.1.12/go.mod h1:u5H1YNBxpqRaxsYJYSkiCWKzEfiAb1Gb520KVy5xxl4=
github.com/mattn/go-isatty v0.0.8/go.mod h1:Iq45c/XA43vh69/j3iqttzPXn0bhXyGjM0Hdxcsrc5s=
github.com/mattn/go-isatty v0.0.12 h1:wuysRhFDzyxgEmMf5xjvJ2M9dZoWAXNNr5LSBS7uHXY=
github.com/mattn/go-isatty v0.0.12/go.mod h1:cbi8OIDigv2wuxKPP5vlRcQ1OAZbq2CE4Kysco4FUpU=
github.com/mattn/go-isatty v0.0.14 h1:yVuAays6BHfxijgZPzw+3Zlu5yQgKGP2/hcQbHb7S9Y=
github.com/mattn/go-isatty v0.0.14/go.mod h1:7GGIvUiUoEMVVmxf/4nioHXj79iQHKdU27kJ6hsGG94=
github.com/mgutz/ansi v0.0.0-20170206155736-9520e82c474b/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d h1:5PJl274Y63IEHC+7izoQE9x6ikvDFZS2mDVS3drnohI=
github.com/mgutz/ansi v0.0.0-20200706080929-d51e80ef957d/go.mod h1:01TrycV0kFyexm33Z7vhZRXopbI8J3TDReVlkTgMUxE=
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
github.com/rs/xid v1.3.0/go.mod h1:trrq9SKmegXys3aeAKXMUTdJsYXVwGY3RLcfgqegfbg=
github.com/rs/zerolog v1.26.0 h1:ORM4ibhEZeTeQlCojCK2kPz1ogAY4bGs4tD+SaAdGaE=
github.com/rs/zerolog v1.26.0/go.mod h1:yBiM87lvSqX8h0Ww4sdzNSkVYZ8dL2xjZJG1lAuGZEo=
github.com/rs/zerolog v1.27.0 h1:1T7qCieN22GVc8S4Q2yuexzBb1EqjbgjSH9RohbMjKs=
github.com/rs/zerolog v1.27.0/go.mod h1:7frBqO0oezxmnO7GF86FY++uy8I0Tk/If5ni1G9Qc0U=
github.com/russross/blackfriday/v2 v2.0.1/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/russross/blackfriday/v2 v2.1.0 h1:JIOH55/0cWyOuilr9/qlrm0BSXldqnqwMsf35Ld67mk=
github.com/russross/blackfriday/v2 v2.1.0/go.mod h1:+Rmxgy9KzJVeS9/2gXHxylqXiyQDYRxCVz55jmeOWTM=
github.com/shurcooL/sanitized_anchor_name v1.0.0 h1:PdmoCO6wvbs+7yrJyMORt4/BmY5IYyJwS/kOiWx8mHo=
github.com/shurcooL/sanitized_anchor_name v1.0.0/go.mod h1:1NzhyTcUVG4SuEtjjoZeVRXNmyL/1OwPU0+IJeTBvfc=
github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
github.com/stretchr/testify v1.2.1 h1:52QO5WkIUcHGIR7EnGagH88x1bUzqGXTC5/1bDTUQ7U=
github.com/stretchr/testify v1.2.1/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
github.com/urfave/cli/v2 v2.3.0 h1:qph92Y649prgesehzOrQjdWyxFOp/QVM+6imKHad91M=
github.com/urfave/cli/v2 v2.3.0/go.mod h1:LJmUH05zAU44vOAcrfzZQKsZbVcdbOG8rtL3/XcUArI=
github.com/urfave/cli/v2 v2.11.1 h1:UKK6SP7fV3eKOefbS87iT9YHefv7iB/53ih6e+GNAsE=
github.com/urfave/cli/v2 v2.11.1/go.mod h1:f8iq5LtQ/bLxafbdBSLPPNsgaW0l/2fYYEHhAyPlwvo=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673 h1:bAn7/zixMGCfxrRTfdpNzjtPYqr8smhKouy9mxVdGPU=
github.com/xrash/smetrics v0.0.0-20201216005158-039620a65673/go.mod h1:N3UwUGtsrSj3ccvlPHLoLsHnpR27oXr4ZE984MbSER8=
github.com/yuin/goldmark v1.4.0/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
go.etcd.io/bbolt v1.3.5 h1:XAzx9gjCb0Rxj7EoqcClPD1d5ZBxZJk0jbuoPHenBt0=
go.etcd.io/bbolt v1.3.5/go.mod h1:G5EMThwa9y8QZGBClrRx5EY+Yw9kAhnjy3bSjsnlVTQ=
go.etcd.io/bbolt v1.3.6 h1:/ecaJf0sk1l4l6V4awd65v2C3ILy7MSj+s/x1ADCIMU=
go.etcd.io/bbolt v1.3.6/go.mod h1:qXsaaIqmgQH0T+OPdb99Bf+PKfBBQVAdyD6TY9G8XM4=
go.jolheiser.com/beaver v1.1.1 h1:py8Zj3tjT52dUzsvnu97aiLj1fBJjDJiK6kHjKJejMQ=
go.jolheiser.com/beaver v1.1.1/go.mod h1:7X4F5+XOGSC3LejTShoBdqtRCnPWcnRgmYGmG3EKW8g=
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
golang.org/x/crypto v0.0.0-20190530122614-20be4c3c3ed5/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550 h1:ObdrDkeb4kJdCP557AjRjq69pTHfNouLtWZG7j9rPN8=
golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa h1:zuSxTR4o9y82ebqCUJYNGJbGPo6sKVl54f/TVDObg1c=
golang.org/x/crypto v0.0.0-20220722155217-630584e8d5aa/go.mod h1:IxCIyHEi3zRg3s0A5j5BB6A9Jmi73HwBIUl50j+osU4=
golang.org/x/mod v0.4.2/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad h1:DN0cp81fZ3njFcrLCytUHRSUkqBjfTo4Tx9RJTWs0EY=
golang.org/x/crypto v0.0.0-20201221181555-eec23a3978ad/go.mod h1:jdWPYTVW3xRLrWPugEBEK3UY2ZEsg3UU495nc5E+M+I=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3 h1:0GoQqolDA55aaLxZyTzK/Y2ePZzZTUrRacwib7cNsYQ=
golang.org/x/net v0.0.0-20190404232315-eb5bcb51f2a3/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg=
golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
golang.org/x/net v0.0.0-20210805182204-aaa1db679c0d/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/net v0.0.0-20211112202133-69e39bad7dc2/go.mod h1:9nx3DQGgdP8bBQD5qxJ1jj9UTztislL4KSBs9R2vV5Y=
golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sync v0.0.0-20210220032951-036812b2e83c/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190222072716-a9d3bda3a223/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
golang.org/x/sys v0.0.0-20190412213103-97732733099d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190530182044-ad28b68e88f1/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20190813064441-fde4db37ae7a/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20191026070338-33540a1f6037/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200116001909-b77594299b42/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200202164722-d101bd2416d5/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200223170610-d5e6a3e2c0ae/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20200923182605-d9f96fdee20d/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210423082822-04245dca01da/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210630005230-0f9fa26af87c/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e h1:WUoyKPm6nCo1BnNUvPGnFG3T5DUVem42yDJZZ4CNxMA=
golang.org/x/sys v0.0.0-20210809222454-d867a43fc93e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20210927094055-39ccf1dd6fa6/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220422013727-9388b58f7150/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f h1:v4INt8xihDGvnrfjMDVXGxw9wrfxYyCjk0KbXjhR55s=
golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/term v0.0.0-20210503060354-a79de5458b56/go.mod h1:tfny5GFUkzUvx4ps4ajbZsCe5lw1metzhBm9T3x7oIY=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035 h1:Q5284mrmYTpACcm+eAKjKJH48BBwSyfJqmmGDTtT8Vc=
golang.org/x/term v0.0.0-20220722155259-a9ba230a4035/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073 h1:8qxJSnu+7dRq6upnbntrmriWByIakBuct5OM/MdQC1M=
golang.org/x/sys v0.0.0-20210225134936-a50acf3fe073/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
golang.org/x/term v0.0.0-20201117132131-f5c789dd3221/go.mod h1:Nr5EML6q2oocZ2LXRh80K7BxOlk5/8JxuGnuhpl+muw=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf h1:MZ2shdL+ZM/XzY3ZGOnh4Nlpnxz5GSOhOmtHo3iPU6M=
golang.org/x/term v0.0.0-20201210144234-2321bbc49cbf/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.6 h1:aRYxNxv6iGQlyVaZmk6ZgYEDa+Jg18DxebPSrd6bg1M=
golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
golang.org/x/text v0.3.5 h1:i6eZZ+zk0SOf0xgBpEpPD18qWcJda6q1sxt3S0kzyUQ=
golang.org/x/text v0.3.5/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e h1:FDhOuMEY4JVRztM/gsbk+IKUQ8kj74bxZrgw87eMMVc=
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
golang.org/x/tools v0.1.7/go.mod h1:LGqMHiF4EqQNHR1JncWGqT5BVaXmza+X+BDGol+dOxo=
golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20191011141410-1b5146add898/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405 h1:yhCVgyC4o1eVCa2tZl7eS0r+SDo693bJlVdllGtEeKM=
gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
gopkg.in/yaml.v2 v2.2.3 h1:fvjTMHxHEw/mxHbtzPi3JCcKXQRAnQTBRo6YCJSVHKI=
gopkg.in/yaml.v2 v2.2.3/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=

View File

@ -1,11 +0,0 @@
package cli
var (
serverFlag string
tokenFlag string
databaseFlag string
localFlag bool
forceFlag bool
portFlag int
)

View File

@ -1,47 +0,0 @@
package cli
import (
"errors"
"fmt"
"net/http"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/internal/router"
"github.com/rs/zerolog/log"
"github.com/urfave/cli/v2"
)
var Server = cli.Command{
Name: "server",
Aliases: []string{"web"},
Usage: "Start the gpm server",
Flags: []cli.Flag{
&cli.IntFlag{
Name: "port",
Aliases: []string{"p"},
Usage: "Port to run the gpm server on",
Value: 3333,
EnvVars: []string{"GPM_PORT"},
Destination: &portFlag,
},
},
Action: doServer,
}
func doServer(ctx *cli.Context) error {
if tokenFlag == "" {
return errors.New("gpm server requires --token")
}
db, err := database.Load(databaseFlag)
if err != nil {
log.Fatal().Msgf("could not load database at %q: %v", databaseFlag, err)
}
log.Info().Msgf("Running gpm server at http://localhost:%d", portFlag)
if err := http.ListenAndServe(fmt.Sprintf(":%d", portFlag), router.New(tokenFlag, ctx.App.Version, db)); err != nil {
return err
}
return nil
}

15
main.go 100644
View File

@ -0,0 +1,15 @@
package main
import (
"os"
"go.jolheiser.com/gpm/cmd"
"go.jolheiser.com/beaver"
)
func main() {
if err := cmd.New().Run(os.Args); err != nil {
beaver.Error(err)
}
}

View File

@ -1,25 +1,28 @@
package router
import (
"encoding/json"
"io"
"net/http"
"time"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/go-gpm"
"github.com/go-chi/chi/v5"
"github.com/go-chi/chi/v5/middleware"
"github.com/go-chi/render"
"github.com/rs/zerolog/log"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"go.jolheiser.com/beaver"
)
func New(token, version string, db *database.Database) *chi.Mux {
var Version = "develop"
func New(token string, db *database.Database) *chi.Mux {
r := chi.NewRouter()
r.Use(middleware.RedirectSlashes)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(30 * time.Second))
r.Get("/", handleHome(version, db))
r.Get("/", handleHome(db))
r.Post("/", addUpdatePackage(db, token))
r.Patch("/", addUpdatePackage(db, token))
r.Delete("/", removePackage(db, token))
@ -28,34 +31,41 @@ func New(token, version string, db *database.Database) *chi.Mux {
return r
}
func handleHome(version string, db *database.Database) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
func handleHome(db *database.Database) func(res http.ResponseWriter, _ *http.Request) {
return func(res http.ResponseWriter, _ *http.Request) {
pkgs, err := db.Packages()
if err != nil {
log.Err(err).Msg("")
beaver.Error(err)
return
}
render.JSON(w, r, gpm.Info{
Version: version,
status, err := json.Marshal(gpm.Info{
Version: Version,
NumPackages: len(pkgs),
Packages: pkgs,
})
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
_, _ = res.Write([]byte("{}"))
return
}
_, _ = res.Write(status)
}
}
func getPackage(db *database.Database) func(http.ResponseWriter, *http.Request) {
return func(w http.ResponseWriter, r *http.Request) {
name := chi.URLParam(r, "name")
return func(res http.ResponseWriter, req *http.Request) {
name := chi.URLParam(req, "name")
pkg, err := db.Package(name)
pkg, err := db.PackageJSON(name)
if err != nil {
w.WriteHeader(http.StatusNotFound)
render.JSON(w, r, struct{}{})
res.WriteHeader(http.StatusNotFound)
_, _ = res.Write([]byte("{}"))
return
}
render.JSON(w, r, pkg)
_, _ = res.Write(pkg)
}
}
@ -66,13 +76,19 @@ func addUpdatePackage(db *database.Database, token string) func(http.ResponseWri
return
}
var pkg gpm.Package
if err := render.DecodeJSON(req.Body, &pkg); err != nil {
data, err := io.ReadAll(req.Body)
if err != nil {
res.WriteHeader(http.StatusBadRequest)
return
}
defer req.Body.Close()
var pkg gpm.Package
if err := json.Unmarshal(data, &pkg); err != nil {
res.WriteHeader(http.StatusBadRequest)
return
}
exists, err := db.PackageJSON(pkg.Name)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
@ -112,13 +128,19 @@ func removePackage(db *database.Database, token string) func(http.ResponseWriter
return
}
var pkg gpm.Package
if err := render.DecodeJSON(req.Body, &pkg); err != nil {
data, err := io.ReadAll(req.Body)
if err != nil {
res.WriteHeader(http.StatusBadRequest)
return
}
defer req.Body.Close()
var pkg gpm.Package
if err := json.Unmarshal(data, &pkg); err != nil {
res.WriteHeader(http.StatusBadRequest)
return
}
if err := db.RemovePackage(pkg.Name); err != nil {
res.WriteHeader(http.StatusInternalServerError)
return

View File

@ -7,10 +7,10 @@ import (
"path/filepath"
"testing"
"go.jolheiser.com/gpm"
"go.jolheiser.com/gpm/internal/database"
"go.jolheiser.com/gpm/database"
"go.jolheiser.com/gpm/go-gpm"
"github.com/rs/zerolog/log"
"go.jolheiser.com/beaver"
)
var (
@ -29,10 +29,10 @@ func TestMain(m *testing.M) {
db, err := database.Load(dbPath)
if err != nil {
log.Fatal().Msgf("could not load database at %q: %v", dbPath, err)
beaver.Fatalf("could not load database at %s: %v", dbPath, err)
}
server = httptest.NewServer(New(token, "test", db))
server = httptest.NewServer(New(token, db))
code := m.Run()
@ -92,7 +92,7 @@ func checkInfo(t *testing.T, client *gpm.Client, numPackages int) {
t.Logf("info should not return error: %v\n", err)
t.Fail()
}
if info.Version != "test" || info.NumPackages != numPackages {
if info.Version != Version || info.NumPackages != numPackages {
t.Log("info did not match expected")
t.Fail()
}