master
Adam Veldhousen 4 years ago
commit 6b5ab437c4
Signed by: adam
GPG Key ID: 6DB29003C6DD1E4B

1
.gitignore vendored

@ -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…
Cancel
Save