Add server

Signed-off-by: jolheiser <john.olheiser@gmail.com>
pull/1/head
jolheiser 2020-02-19 23:46:44 -06:00
parent 41750c0200
commit a0a1189921
Signed by: jolheiser
GPG Key ID: B853ADA5DA7BBF7A
16 changed files with 174 additions and 49 deletions

View File

@ -14,7 +14,7 @@ else
LONG_VERSION ?= $(shell git describe --tags --always | sed 's/-/+/' | sed 's/^v//')
endif
LDFLAGS := $(LDFLAGS) -X "main.Version=$(LONG_VERSION)"
LDFLAGS := $(LDFLAGS) -X "gitea.com/jolheiser/gpm/modules/config.Version=$(LONG_VERSION)"
.PHONY: build
build:

View File

@ -16,16 +16,19 @@ Using either a GPM server or local config, I can instead `gpm get cli` which fin
* `add` - Add a local package
* `remove` - Remove a local package
* `list` - List local packages
* `config` - Change local configuration (GPM server)
* `config` - Change local configuration
* `export` - Export local packages to JSON
* `import` - Import JSON to local packages
* `get` - Get a list of packages
e.g. `gpm get beaver survey toml homedir cli` to get all the modules needed for gpm itself (assuming the map resolves to the same packages)
* `import` - Import JSON to local packages. Either give a path to a `.json` file, or a URL to a GPM server export endpoint
* e.g. `https://gpm.jolheiser.com/export`
* `get` - Get a list of packages
* e.g. `gpm get beaver survey toml homedir cli` to get all the modules needed for gpm itself (assuming the map resolves to the same packages)
* `server` - Start a gpm server
### Server
If GPM doesn't find a package locally, it can call out to a configurable [gpm server](https://gitea.com/jolheiser/gpm-server) to find a package there.
If GPM doesn't find a package locally, it can call out to a configurable gpm server to find a package there instead.
This makes it much simpler to have a central library of packages rather than exporting and importing between environments.
The `import` and `export` commands should work between the CLI and server for easy transitions.
Want to run your own server? It's very easy! This CLI comes packaged with the server inside, simply run `gpm server` to start up a GPM server.
Put it behind your favorite reverse proxy and it's ready to go!

View File

@ -1,8 +1,8 @@
package cmd
import (
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
"regexp"

View File

@ -1,7 +1,7 @@
package cmd
import (
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/urfave/cli/v2"
)

View File

@ -1,8 +1,8 @@
package cmd
import (
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
)
@ -17,7 +17,7 @@ var Config = cli.Command{
func doConfig(ctx *cli.Context) error {
urlQuestion := &survey.Input{
Message: "gpm URL",
Default: "gpm.jolheiser.com",
Default: config.GPMURL,
}
var urlAnswer string

View File

@ -2,7 +2,7 @@ package cmd
import (
"fmt"
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/urfave/cli/v2"
)

View File

@ -3,8 +3,8 @@ package cmd
import (
"encoding/json"
"fmt"
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
"io/ioutil"
@ -49,7 +49,7 @@ func doGet(ctx *cli.Context) error {
for _, pkg := range pkgs {
var url string
if u, ok := local[pkg]; ok && !ctx.Bool("ignore-local") {
url = u
url = u.Import
} else if !ctx.Bool("offline") {
u, err := queryServer(ctx.String("url"), pkg)
if err != nil {
@ -73,12 +73,8 @@ func doGet(ctx *cli.Context) error {
return nil
}
type response struct {
URL string `json:"url"`
}
func queryServer(server, name string) (string, error) {
endpoint := fmt.Sprintf("%s/%s", server, name)
endpoint := fmt.Sprintf("%s/package/%s", server, name)
resp, err := http.Get(endpoint)
if err != nil {
return "", fmt.Errorf("could not query server at `%s`", endpoint)
@ -94,12 +90,12 @@ func queryServer(server, name string) (string, error) {
}
defer resp.Body.Close()
var data response
if err := json.Unmarshal(body, &data); err != nil {
var pkg config.Package
if err := json.Unmarshal(body, &pkg); err != nil {
return "", err
}
return data.URL, nil
return pkg.Import, nil
}
func goGet(url string) error {

View File

@ -3,10 +3,12 @@ package cmd
import (
"encoding/json"
"errors"
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/urfave/cli/v2"
"io/ioutil"
"net/http"
"strings"
)
var Import = cli.Command{
@ -23,26 +25,45 @@ var Import = cli.Command{
}
func doImport(ctx *cli.Context) error {
files := ctx.Args().Slice()
if len(files) == 0 {
return errors.New("must provide a list of JSON files to import from")
if ctx.NArg() == 0 {
return errors.New("must point to either a JSON file or gpm server export endpoint")
}
for _, file := range files {
body, err := ioutil.ReadFile(file)
arg := ctx.Args().First()
isJSON := strings.HasSuffix(arg, ".json")
isHTTP := strings.HasPrefix(arg, "http")
if !isJSON && !isHTTP {
return errors.New("must point to either a JSON file or gpm server export endpoint")
}
var data []byte
var err error
if isJSON {
data, err = ioutil.ReadFile(arg)
if err != nil {
beaver.Error(err)
continue
return err
}
} else if isHTTP {
resp, err := http.Get(arg)
if err != nil {
return err
}
var cfg config.Config
if err := json.Unmarshal(body, &cfg); err != nil {
beaver.Error(err)
continue
data, err = ioutil.ReadAll(resp.Body)
if err != nil {
return err
}
config.AddPackages(ctx.Bool("force"), cfg.Packages...)
defer resp.Body.Close()
}
var cfg config.Config
if err := json.Unmarshal(data, &cfg); err != nil {
return err
}
config.AddPackages(ctx.Bool("force"), cfg.Packages...)
if err := config.Save(); err != nil {
return err
}

View File

@ -1,8 +1,8 @@
package cmd
import (
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/urfave/cli/v2"
)

View File

@ -2,8 +2,8 @@ package cmd
import (
"fmt"
"gitea.com/gpm/gpm/modules/config"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/AlecAivazis/survey/v2"
"github.com/urfave/cli/v2"
"strings"

31
cmd/server.go 100644
View File

@ -0,0 +1,31 @@
package cmd
import (
"fmt"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/router"
"github.com/urfave/cli/v2"
"net/http"
)
var Server = cli.Command{
Name: "server",
Usage: "Start the gpm server",
Flags: []cli.Flag{
&cli.StringFlag{
Name: "port",
Aliases: []string{"p"},
Usage: "Port to run the gpm server on",
Value: "3333",
},
},
Action: doServer,
}
func doServer(ctx *cli.Context) error {
beaver.Infof("Running gpm server at http://localhost:%s", ctx.String("port"))
if err := http.ListenAndServe(fmt.Sprintf(":%s", ctx.String("port")), router.Init()); err != nil {
return err
}
return nil
}

3
go.mod
View File

@ -1,4 +1,4 @@
module gitea.com/gpm/gpm
module gitea.com/jolheiser/gpm
go 1.13
@ -6,6 +6,7 @@ require (
gitea.com/jolheiser/beaver v1.0.0
github.com/AlecAivazis/survey/v2 v2.0.5
github.com/BurntSushi/toml v0.3.1
github.com/go-chi/chi v4.0.3+incompatible
github.com/mitchellh/go-homedir v1.1.0
github.com/urfave/cli/v2 v2.1.1
)

2
go.sum
View File

@ -10,6 +10,8 @@ github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d h1:U+s90UTSY
github.com/cpuguy83/go-md2man/v2 v2.0.0-20190314233015-f79a8a8ca69d/go.mod h1:maD7wRr/U5Z6m/iR4s+kqSMx2CaBsrgA7czyZG/E6dU=
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 v4.0.3+incompatible h1:gakN3pDJnzZN5jqFV2TEdF66rTfKeITyR8qu6ekICEY=
github.com/go-chi/chi v4.0.3+incompatible/go.mod h1:eB3wogJHnLi3x/kFX2A+IbTBlXxmMeXJVKy9tTv1XzQ=
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/kballard/go-shellquote v0.0.0-20180428030007-95032a82bc51 h1:Z9n2FFNUXsshfwJMBgNA0RU6/i7WVaAegv3PtuIHPMs=

View File

@ -1,14 +1,13 @@
package main
import (
"gitea.com/gpm/gpm/cmd"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/cmd"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/urfave/cli/v2"
"os"
)
var Version = "develop"
func main() {
// config loads on init
@ -16,7 +15,7 @@ func main() {
app := cli.NewApp()
app.Name = "gpm"
app.Usage = "Go Package Manager"
app.Version = Version
app.Version = config.Version
app.Commands = []*cli.Command{
&cmd.Add,
&cmd.Remove,
@ -25,6 +24,7 @@ func main() {
&cmd.Import,
&cmd.Export,
&cmd.Config,
&cmd.Server,
}
app.Flags = cmd.Flags
app.EnableBashCompletion = true

View File

@ -15,6 +15,7 @@ import (
var (
configPath string
cfg *Config
Version = "develop"
// Config items
@ -42,16 +43,16 @@ func init() {
if _, err := os.Stat(configPath); os.IsNotExist(err) {
if err := os.MkdirAll(path.Dir(configPath), os.ModePerm); err != nil {
beaver.Fatalf("could not create Sip home: %v", err)
beaver.Fatalf("could not create gpm home: %v", err)
}
if _, err := os.Create(configPath); err != nil {
beaver.Fatalf("could not create Sip config: %v", err)
beaver.Fatalf("could not create gpm config: %v", err)
}
}
if _, err := toml.DecodeFile(configPath, &cfg); err != nil {
beaver.Fatalf("could not decode Sip config: %v", err)
beaver.Fatalf("could not decode gpm config: %v", err)
}
dupe := make(map[string]bool)
@ -85,14 +86,14 @@ func Save() error {
}
func Export() (string, error) {
data, err := json.Marshal(cfg)
data, err := json.Marshal(Packages)
return string(data), err
}
func PackageMap() map[string]string {
pkgs := make(map[string]string)
func PackageMap() map[string]Package {
pkgs := make(map[string]Package)
for _, pkg := range Packages {
pkgs[pkg.Name] = pkg.Import
pkgs[pkg.Name] = pkg
}
return pkgs
}

View File

@ -0,0 +1,70 @@
package router
import (
"encoding/json"
"gitea.com/jolheiser/beaver"
"gitea.com/jolheiser/gpm/modules/config"
"github.com/go-chi/chi"
"github.com/go-chi/chi/middleware"
"net/http"
"time"
)
var cache map[string]config.Package
func Init() *chi.Mux {
r := chi.NewRouter()
r.Use(middleware.RedirectSlashes)
r.Use(middleware.Recoverer)
r.Use(middleware.Timeout(30 * time.Second))
r.Get("/status", handleStatus)
r.Get("/export", handleExport)
r.Get("/package/{name}", handlePackage)
cache = config.PackageMap()
return r
}
func handleStatus(res http.ResponseWriter, _ *http.Request) {
status, err := json.Marshal(map[string]interface{}{
"version": config.Version,
"packages": len(config.Packages),
})
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
_, _ = res.Write([]byte("{}"))
return
}
_, _ = res.Write(status)
}
func handleExport(res http.ResponseWriter, _ *http.Request) {
export, err := config.Export()
if err != nil {
beaver.Error(err)
return
}
_, _ = res.Write([]byte(export))
}
func handlePackage(res http.ResponseWriter, req *http.Request) {
name := chi.URLParam(req, "name")
if pkg, ok := cache[name]; ok {
data, err := json.Marshal(pkg)
if err != nil {
res.WriteHeader(http.StatusInternalServerError)
_, _ = res.Write([]byte("{}"))
return
}
_, _ = res.Write(data)
return
}
res.WriteHeader(http.StatusNotFound)
_, _ = res.Write([]byte("{}"))
}