commit
6b5ab437c4
@ -0,0 +1 @@
|
||||
.bin
|
@ -0,0 +1,5 @@
|
||||
module github.com/adamveld12/gpm
|
||||
|
||||
go 1.14
|
||||
|
||||
require github.com/Masterminds/semver v1.5.0
|
@ -0,0 +1,2 @@
|
||||
github.com/Masterminds/semver v1.5.0 h1:H65muMkzWKEuNDnfl9d70GUjFniHKHRbFPGBuZ3QEww=
|
||||
github.com/Masterminds/semver v1.5.0/go.mod h1:MB6lktGJrhw8PrUyiEoblNEGEQ+RzHPF078ddwwvV3Y=
|
@ -0,0 +1,63 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"archive/tar"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"net/http"
|
||||
)
|
||||
|
||||
type PackageResponse struct {
|
||||
Name string `json:"name"`
|
||||
Versions map[string]Version `json:"versions"`
|
||||
}
|
||||
|
||||
type Version struct {
|
||||
VersionNumber string `json:"version"`
|
||||
ID string `json:"_id"`
|
||||
Dependencies map[string]string `json:"dependencies"`
|
||||
DevDependencies map[string]string `json:"devDependencies"`
|
||||
OptionalDependencies map[string]string `json:"optionalDependencies"`
|
||||
Dist struct {
|
||||
Sum string `json:"shasum"`
|
||||
Tarball string `json:"tarball"`
|
||||
}
|
||||
}
|
||||
|
||||
func GetPackageInfo(name string) (PackageResponse, error) {
|
||||
url := fmt.Sprintf("https://registry.npmjs.com/%s", name)
|
||||
var pr PackageResponse
|
||||
|
||||
req, _ := http.NewRequest(http.MethodGet, url, nil)
|
||||
req.Header.Set("Accept", "application/json")
|
||||
|
||||
resp, err := http.DefaultClient.Do(req)
|
||||
if err != nil {
|
||||
return pr, fmt.Errorf("could not get package info: %w", err)
|
||||
}
|
||||
defer resp.Body.Close()
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return pr, fmt.Errorf("Got a non 200 status code '%s': %v", resp.Status, resp.StatusCode)
|
||||
}
|
||||
|
||||
if err := json.NewDecoder(resp.Body).Decode(&pr); err != nil {
|
||||
return pr, fmt.Errorf("could not decode json payload: %w", err)
|
||||
}
|
||||
return pr, nil
|
||||
}
|
||||
|
||||
func DownloadPackageTarball(sum, tarballURL string) (*tar.Reader, io.Closer, error) {
|
||||
resp, err := http.Get(tarballURL)
|
||||
if err != nil {
|
||||
return nil, nil, fmt.Errorf("could not get tarball: %w", err)
|
||||
}
|
||||
|
||||
if resp.StatusCode != http.StatusOK {
|
||||
return nil, nil, fmt.Errorf("Got a non 200 status code '%s': %v", resp.Status, resp.StatusCode)
|
||||
}
|
||||
|
||||
reader := tar.NewReader(resp.Body)
|
||||
return reader, resp.Body, nil
|
||||
}
|
@ -0,0 +1,120 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"fmt"
|
||||
"strings"
|
||||
|
||||
"github.com/Masterminds/semver"
|
||||
)
|
||||
|
||||
type DependencyNode struct {
|
||||
Version
|
||||
Name string
|
||||
ResolvedDependencies []*DependencyNode
|
||||
ResolvedDevDependencies []*DependencyNode
|
||||
Error error
|
||||
}
|
||||
|
||||
func BuildDependencyTree(rp RawPackage) (*DependencyNode, error) {
|
||||
resolved := map[string]*DependencyNode{}
|
||||
root := &DependencyNode{
|
||||
Name: rp.Name,
|
||||
ResolvedDependencies: populateDepList(rp.Dependencies, resolved),
|
||||
ResolvedDevDependencies: populateDepList(rp.DevDependencies, resolved),
|
||||
}
|
||||
|
||||
current := root
|
||||
for _, dep := range current.ResolvedDependencies {
|
||||
dep.ResolvedDependencies = populateDepList(dep.Dependencies, resolved)
|
||||
current = dep
|
||||
}
|
||||
|
||||
return root, nil
|
||||
}
|
||||
|
||||
func populateDepList(dependencies map[string]string, resolved map[string]*DependencyNode) []*DependencyNode {
|
||||
depList := make([]*DependencyNode, 0, len(dependencies))
|
||||
for name, versionConstraint := range dependencies {
|
||||
var node *DependencyNode
|
||||
var err error
|
||||
if resolvedDep, ok := resolved[name]; ok {
|
||||
c, err := semver.NewConstraint(versionConstraint)
|
||||
if err != nil {
|
||||
node = &DependencyNode{
|
||||
Name: name,
|
||||
Error: fmt.Errorf("could not create a constraint: %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
v, err := semver.NewVersion(resolvedDep.VersionNumber)
|
||||
if err != nil {
|
||||
node = &DependencyNode{
|
||||
Name: name,
|
||||
Error: fmt.Errorf("could not detect version for a dist: %w", err),
|
||||
}
|
||||
}
|
||||
|
||||
valid, errs := c.Validate(v)
|
||||
if len(errs) > 0 {
|
||||
node = &DependencyNode{
|
||||
Name: name,
|
||||
Error: fmt.Errorf("could not parse version '%s': %+v", resolvedDep.VersionNumber, errs),
|
||||
}
|
||||
} else if valid {
|
||||
node = resolvedDep
|
||||
}
|
||||
} else if strings.HasPrefix(versionConstraint, "github:") {
|
||||
// skip github packages for now
|
||||
node = &DependencyNode{
|
||||
Name: name,
|
||||
Error: errors.New("github package sources not supported."),
|
||||
}
|
||||
} else {
|
||||
node, err = createNpmDependencyNode(name, versionConstraint)
|
||||
if err != nil {
|
||||
node = &DependencyNode{Name: name, Error: err}
|
||||
} else {
|
||||
resolved[node.Name] = node
|
||||
}
|
||||
}
|
||||
|
||||
depList = append(depList, node)
|
||||
}
|
||||
|
||||
return depList
|
||||
}
|
||||
|
||||
func createNpmDependencyNode(name, versionConstraint string) (*DependencyNode, error) {
|
||||
|
||||
pinfo, err := GetPackageInfo(name)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not get package information for '%s': %w", name, err)
|
||||
}
|
||||
|
||||
c, err := semver.NewConstraint(versionConstraint)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not create a constraint: %w", err)
|
||||
}
|
||||
|
||||
var max *semver.Version
|
||||
var selectedDist Version
|
||||
for version, dist := range pinfo.Versions {
|
||||
v, err := semver.NewVersion(version)
|
||||
if err != nil {
|
||||
return nil, fmt.Errorf("could not detect version for a dist: %w", err)
|
||||
}
|
||||
|
||||
valid, errs := c.Validate(v)
|
||||
if len(errs) > 0 {
|
||||
return nil, fmt.Errorf("could not parse version '%s': %w", version, err)
|
||||
}
|
||||
|
||||
if valid && v.GreaterThan(max) {
|
||||
max = v
|
||||
selectedDist = dist
|
||||
}
|
||||
}
|
||||
|
||||
return &DependencyNode{Name: name, Version: selectedDist}, nil
|
||||
}
|
@ -0,0 +1,67 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_populateDepList(t *testing.T) {
|
||||
type Input struct {
|
||||
dependencies map[string]string
|
||||
resolved map[string]*DependencyNode
|
||||
}
|
||||
tests := []struct {
|
||||
name string
|
||||
args Input
|
||||
want []*DependencyNode
|
||||
}{
|
||||
{
|
||||
name: "resolve one package",
|
||||
args: Input{
|
||||
dependencies: map[string]string{"lodash": "4.17.15"},
|
||||
resolved: map[string]*DependencyNode{},
|
||||
},
|
||||
want: []*DependencyNode{
|
||||
&DependencyNode{
|
||||
Name: "lodash",
|
||||
Error: nil,
|
||||
Version: Version{
|
||||
ID: "lodash-4.17.15",
|
||||
VersionNumber: "4.17.15",
|
||||
},
|
||||
},
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got := populateDepList(tt.args.dependencies, tt.args.resolved)
|
||||
if len(got) != len(tt.want) {
|
||||
t.Errorf("len(populateDepList()) = (%v), want (%v)", len(got), len(tt.want))
|
||||
}
|
||||
for _, gotDep := range got {
|
||||
found := false
|
||||
for _, wantDep := range tt.want {
|
||||
if gotDep.ID == wantDep.ID {
|
||||
found = true
|
||||
} else {
|
||||
t.Logf("got: %+v\nwant:%+v", *gotDep, *wantDep)
|
||||
continue
|
||||
}
|
||||
|
||||
if !reflect.DeepEqual(*gotDep, *wantDep) {
|
||||
t.Errorf("got %v, want %v", *gotDep, *wantDep)
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
if !found {
|
||||
t.Errorf("not found")
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
})
|
||||
}
|
||||
}
|
@ -0,0 +1,31 @@
|
||||
package lib
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
type RawPackage struct {
|
||||
Name string `json:"name"`
|
||||
Dependencies map[string]string `json:"dependencies"`
|
||||
DevDependencies map[string]string `json:"devDependencies"`
|
||||
}
|
||||
|
||||
func ParsePackageJson(path string) (RawPackage, error) {
|
||||
f, err := os.Open(path)
|
||||
if os.IsNotExist(err) {
|
||||
return RawPackage{}, errors.New("file doesn't exist.")
|
||||
} else if err != nil {
|
||||
return RawPackage{}, fmt.Errorf("could not read file: %v", err)
|
||||
}
|
||||
defer f.Close()
|
||||
|
||||
var p RawPackage
|
||||
if err := json.NewDecoder(f).Decode(&p); err != nil {
|
||||
return RawPackage{}, fmt.Errorf("could not parse package.json file: %v", err)
|
||||
}
|
||||
|
||||
return p, nil
|
||||
}
|
@ -0,0 +1,59 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"log"
|
||||
"os"
|
||||
|
||||
"github.com/adamveld12/gpm/lib"
|
||||
)
|
||||
|
||||
func main() {
|
||||
if len(os.Args) < 2 || os.Args[1] != "install" {
|
||||
log.Fatal("gpm [install]")
|
||||
}
|
||||
|
||||
p, err := lib.ParsePackageJson("./package.json")
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
fmt.Println("Dependencies")
|
||||
for k, v := range p.Dependencies {
|
||||
fmt.Printf("\t%s => %s\n", k, v)
|
||||
printPackages(k)
|
||||
}
|
||||
|
||||
fmt.Println("Dev Dependencies")
|
||||
for k, v := range p.DevDependencies {
|
||||
fmt.Printf("\t%s => %s\n", k, v)
|
||||
printPackages(k)
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
n, err := lib.BuildDependencyTree(p)
|
||||
if err != nil {
|
||||
log.Fatal(err)
|
||||
}
|
||||
|
||||
log.Printf("%+v", n)
|
||||
}
|
||||
|
||||
func printPackages(k string) {
|
||||
pinfo, err := lib.GetPackageInfo(k)
|
||||
if err != nil {
|
||||
fmt.Printf("\t-- could not fetch: %v\n", err)
|
||||
}
|
||||
|
||||
fmt.Printf("\t-- %d versions available\n", len(pinfo.Versions))
|
||||
idx := 0
|
||||
for _, dep := range pinfo.Versions {
|
||||
if idx >= len(pinfo.Versions)-5 {
|
||||
fmt.Printf("\t\t%s - %s\n", dep.ID, dep.Dist.Sum)
|
||||
fmt.Printf("\t\t\t%d dependencies\n", len(dep.Dependencies))
|
||||
fmt.Printf("\t\t\t%d dev dependencies\n", len(dep.DevDependencies))
|
||||
}
|
||||
}
|
||||
fmt.Println()
|
||||
}
|
@ -0,0 +1,43 @@
|
||||
app := gpm
|
||||
PKGS := $(shell go list ./... | grep -v vendor)
|
||||
OUTDIR := .bin
|
||||
BINARY := $(OUTDIR)/$(app)
|
||||
GOBIN := $(GOPATH)/bin
|
||||
LINTBIN := $(GOBIN)/golangci-lint
|
||||
clean_list = $(OUTDIR)
|
||||
|
||||
.PHONY: build clean dev test publish lint
|
||||
|
||||
default: lint test build dev
|
||||
|
||||
$(OUTDIR):
|
||||
@mkdir .bin
|
||||
|
||||
$(BINARY): ./main.go $(OUTDIR)
|
||||
go build -o $@ $<
|
||||
|
||||
dev: clean $(BINARY)
|
||||
GPM_DEBUG=true $(BINARY) install
|
||||
|
||||
build: $(BINARY)
|
||||
|
||||
clean: $(clean_list)
|
||||
rm -rf $<
|
||||
|
||||
$(LINTBIN):
|
||||
@GO111MODULE=off go get github.com/golangci/golangci-lint/cmd/golangci-lint
|
||||
|
||||
lint: $(LINTBIN)
|
||||
go mod tidy -v
|
||||
$(LINTBIN) run -p bugs -p format -p performance -p unused
|
||||
|
||||
test: lint
|
||||
go test -v -cover $(PKGS)
|
||||
|
||||
publish: $(BINARY)
|
||||
docker build -t liveauctioneers/$(app):$${VERSION} \
|
||||
--build-arg version=$${VERSION} \
|
||||
--build-arg commit=$${COMMIT_SHA:-$${CIRCLE_SHA1}} \
|
||||
--build-arg initiator=$${INITIATOR:-$${CIRCLE_USERNAME}} \
|
||||
--build-arg created=$${CREATED_TS:-$$(date +%s)} .
|
||||
docker push liveauctioneers/$(app):$${VERSION}
|
@ -0,0 +1,36 @@
|
||||
{
|
||||
"name": "la-e2e",
|
||||
"version": "1.0.0",
|
||||
"description": "Test Cafe based End to End integration tests",
|
||||
"main": "smoke.js",
|
||||
"scripts": {
|
||||
"smoke": "testcafe puppeteer testCafeTests/smoke.js -S takeOnFail=true,fullPage=true -r spec,xunit:./test-results/xunit/smoke.xml ",
|
||||
"regressions": "testcafe puppeteer testCafeTests/regressions.js -S takeOnFail=true,fullPage=true -r spec,xunit:./test-results/xunit/regressions.xml ",
|
||||
"run-suite": "testcafe chrome:headless -e -r spec,xunit:./test-results/xunit/regressions.xml ",
|
||||
"debug-smoke": "testcafe chrome testCafeTests/smoke.js --debug-on-fail --sf",
|
||||
"smoke-selenium": "testcafe selenium:chrome -c 10 -q testCafeTests/smoke.js --ports 1337,1338 -e -r spec,json:./test-results/json/smoke.json,xunit:./test-results/xunit/smoke.xml --hostname",
|
||||
"regressions-selenium": "testcafe selenium:chrome -c 10 -q testCafeTests/regressions.js -e -S takeOnFail=true,fullPage=true --ports 1337,1338 -r spec,json:./test-results/json/regressions.json,xunit:./test-results/xunit/regressions.xml --hostname",
|
||||
"test-selenium": "testcafe selenium:chrome -c 10 -q chrome:headless testCafeTests/*.js -e -S takeOnFail=true,fullPage=true -r spec,json:./test-results/json/all.json,xunit:./test-results/xunit/all.xml --hostname",
|
||||
"docker-build": "docker build --no-cache -t testcafe .",
|
||||
"docker-dev": "docker run -it --rm --name tc --device /dev/net/tun -v $PWD:/opt/lae2e/:Z --cap-add=NET_ADMIN --cap-add=SYS_ADMIN --privileged -e UPDATE_SLACK=true -e RESULTS_SERVER=opsbot-local.liveauctioneers.com --dns 192.168.231.1 --entrypoint=bash testcafe",
|
||||
"live-bidders": "testcafe -c 4 chrome:headless testCafeTests/livebidding.js -f \"Live bidding- two bidders\" -e",
|
||||
"live-tests-visual": "testcafe chrome testCafeTests/livebidding.js -f \"Live bidding\" -e"
|
||||
},
|
||||
"keywords": [],
|
||||
"license": "UNLICENSED",
|
||||
"dependencies": {
|
||||
"base64-img": "^1.0.4",
|
||||
"core-js": "^3.6.4",
|
||||
"dotenv": "^8.2.0",
|
||||
"random-email": "github:jaronaearle/random-email",
|
||||
"request": "^2.88.0",
|
||||
"testcafe": "^1.8.1"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@liveauctioneers/eslint-config-la-react": "^0.1.4",
|
||||
"testcafe-browser-provider-puppeteer": "^1.5.2",
|
||||
"testcafe-browser-provider-selenium": "^0.4.0",
|
||||
"testcafe-reporter-json": "^2.2.0",
|
||||
"testcafe-reporter-xunit": "^2.1.0"
|
||||
}
|
||||
}
|
Loading…
Reference in new issue