diff --git a/.travis.yml b/.travis.yml index cfc67dd..b043c40 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,6 +1,5 @@ language: go go: - - 1.5 - 1.6 - 1.7 - 1.8 diff --git a/Makefile b/Makefile index 06f102a..42e3e13 100644 --- a/Makefile +++ b/Makefile @@ -1,16 +1,42 @@ -os=linux -arch=amd64 +GOOS?=$(shell uname -s | tr '[:upper:]' '[:lower:]') +GOARCH?=amd64 test: - go test ./... + go test ./... +nexus: + docker run -d -p 8081:8081 --name nexus sonatype/nexus3 build: test - GOOS=$(os) GOARCH=$(arch) go build -ldflags " \ + GOOS=$(GOOS) GOARCH=$(GOARCH) go build -ldflags " \ -X github.com/sjeandeaux/nexus-cli/information.Version=$(shell cat VERSION) \ -X github.com/sjeandeaux/nexus-cli/information.BuildTime=$(shell date +"%Y-%m-%dT%H:%M:%S") \ -X github.com/sjeandeaux/nexus-cli/information.GitCommit=$(shell git rev-parse --short HEAD) \ -X github.com/sjeandeaux/nexus-cli/information.GitDescribe=$(shell git describe --tags --always) \ -X github.com/sjeandeaux/nexus-cli/information.GitDirty=$(shell test -n "`git status --porcelain`" && echo "+CHANGES" || true)" \ - -o dist/$(os)_$(arch)_nexus_cli + -o dist/$(GOOS)-$(GOARCH)-nexus-cli +upload: + touch upload.jar + go run main.go -repo=http://localhost:8081/repository/maven-releases \ + -user=admin \ + -password=admin123 \ + -action PUT \ + -file=upload.jar \ + -groupID=com.jeandeaux \ + -artifactID=elyne \ + -version=0.1.0 \ + -hash md5 \ + -hash sha1 + +delete: + go run main.go -repo=http://localhost:8081/repository/maven-releases \ + -user=admin \ + -password=admin123 \ + -file=upload.jar \ + -action DELETE \ + -groupID=com.jeandeaux \ + -artifactID=elyne \ + -version=0.1.0 \ + -hash md5 \ + -hash sha1 diff --git a/README.md b/README.md index c37837f..b9c8c87 100755 --- a/README.md +++ b/README.md @@ -1,6 +1,10 @@ # nexus-cli [![Build Status](https://travis-ci.org/sjeandeaux/nexus-cli.svg?branch=master)](https://travis-ci.org/sjeandeaux/nexus-cli) -## upload +## TODOs + +* 1.0.0 refactoring model because it is the godawful mess + +## nexus-cli I share a volume where i have my upload.jar file. @@ -23,6 +27,18 @@ docker run --link nexus:nexus -ti -v $(pwd):$(pwd):ro sjeandeaux/nexus-cli \ nexus-cli -repo=http://nexus:8081/repository/maven-releases \ -user=admin \ -password=admin123 \ + -action PUT \ + -file=upload.jar \ + -groupID=com.jeandeaux \ + -artifactID=elyne \ + -version=0.1.0 \ + -hash md5 \ + -hash sha1 + +nexus-cli -repo=http://nexus:8081/repository/maven-releases \ + -user=admin \ + -password=admin123 \ + -action DELETE \ -file=upload.jar \ -groupID=com.jeandeaux \ -artifactID=elyne \ diff --git a/VERSION b/VERSION index 1d0ba9e..8f0916f 100644 --- a/VERSION +++ b/VERSION @@ -1 +1 @@ -0.4.0 +0.5.0 diff --git a/dist/linux_amd64_nexus_cli b/dist/linux_amd64_nexus_cli deleted file mode 100755 index 64391a0..0000000 Binary files a/dist/linux_amd64_nexus_cli and /dev/null differ diff --git a/main.go b/main.go index 97fc823..4fbacbc 100755 --- a/main.go +++ b/main.go @@ -9,6 +9,7 @@ import ( "github.com/sjeandeaux/nexus-cli/repositorymanager" "os" + "github.com/sjeandeaux/nexus-cli/information" ) @@ -29,7 +30,9 @@ type commandLineArgs struct { urlOfRepository string user string password string - //file to upload + //action PUT or DELETE + action string + //file to upload or if we delete we get the extension. file string //groupID of artifact groupID string @@ -49,7 +52,8 @@ func init() { flag.StringVar(&commandLine.urlOfRepository, "repo", "http://localhost/repository/third-party", "url of repository") flag.StringVar(&commandLine.user, "user", "", "user for repository") flag.StringVar(&commandLine.password, "password", "", "password for repository") - flag.StringVar(&commandLine.file, "file", "", "your file to upload on repository") + flag.StringVar(&commandLine.action, "action", "", "action PUT or DELETE") + flag.StringVar(&commandLine.file, "file", "", "your file to upload on repository or if we delete we get the extension.") flag.StringVar(&commandLine.groupID, "groupID", "com.jeandeaux", "groupid of artifact") flag.StringVar(&commandLine.artifactID, "artifactID", "elyne", "artifactID of artifact") flag.StringVar(&commandLine.version, "version", "0.1.0-SNAPSHOT", "version of artifact") @@ -59,6 +63,10 @@ func init() { //main upload artifact func main() { + const ( + DELETE = "DELETE" + PUT = "PUT" + ) log.Logger.Println(information.Print()) repo := repositorymanager.NewRepository(commandLine.urlOfRepository, commandLine.user, commandLine.password) @@ -67,7 +75,18 @@ func main() { log.Logger.Fatal(err) } - if err := repo.UploadArtifact(artifact, commandLine.hash...); err != nil { - log.Logger.Fatal(err) + switch commandLine.action { + case PUT: + if err := repo.UploadArtifact(artifact, commandLine.hash...); err != nil { + log.Logger.Fatal(err) + } + + case DELETE: + if err := repo.DeleteArtifact(artifact, commandLine.hash...); err != nil { + log.Logger.Fatal(err) + } + default: + log.Logger.Fatal(fmt.Errorf("what do you want to do with %q ", commandLine.action)) } + } diff --git a/repositorymanager/repositorymanager.go b/repositorymanager/repositorymanager.go index 106d0f5..2949d99 100755 --- a/repositorymanager/repositorymanager.go +++ b/repositorymanager/repositorymanager.go @@ -6,6 +6,7 @@ import ( "crypto/md5" "crypto/sha1" "encoding/hex" + "errors" "fmt" "hash" "io" @@ -19,13 +20,15 @@ import ( "github.com/sjeandeaux/nexus-cli/log" - "errors" "github.com/sjeandeaux/nexus-cli/information" ) const ( - dot = "." - suffixPom = "pom" + dot = "." + suffixPom = "pom" + allReplacement = -1 + slash = "/" + dash = "-" ) //Repository where we want put the file @@ -72,6 +75,27 @@ func NewRepository(url, user, password string) *Repository { } +//DeleteArtifact deletes the artifact +//ar the artifact to delete +//hashs list of hash to delete +func (n *Repository) DeleteArtifact(ar *Artifact, hashs ...string) error { + pomURL := n.generateURL(ar, suffixPom) + if err := n.delete(pomURL); err != nil { + return err + } + + fileURL := n.generateURL(ar, ar.extension()) + if err := n.delete(fileURL); err != nil { + return err + } + + for _, h := range hashs { + //if we can't delete hash we continue + n.deleteHash(ar, h) + } + return nil +} + //UploadArtifact upload ar on repository TODO goroutine to upload //ar the artifact to upload //hashs list of hash to send @@ -120,12 +144,6 @@ func generateURLIssue(h string) string { //generateURL generate the url of ar // ///-. func (n *Repository) generateURL(ar *Artifact, endOfFile string) string { - const ( - allReplacement = -1 - slash = "/" - dash = "-" - ) - g := strings.Replace(ar.GroupID, dot, slash, allReplacement) nameOfFile := fmt.Sprint(slash, ar.ArtifactID, dash, ar.Version, dot, endOfFile) return fmt.Sprint(n.url, slash, g, slash, ar.ArtifactID, slash, ar.Version, nameOfFile) @@ -147,8 +165,23 @@ func (n *Repository) uploadHash(ar *Artifact, h *repositoryHash) error { return n.upload(hashedFile, f) } +//deleteHash delete the hash +func (n *Repository) deleteHash(ar *Artifact, h string) error { + hashedPom := n.generateURL(ar, fmt.Sprint(suffixPom, dot, h)) + if err := n.delete(hashedPom); err != nil { + return err + } + + hashedFile := n.generateURL(ar, fmt.Sprint(ar.extension(), dot, h)) + return n.delete(hashedFile) +} + func (n *Repository) upload(url string, data io.Reader) error { - const PUT = "PUT" + const ( + PUT = "PUT" + httpSuccess = 201 + ) + log.Logger.Print(url) req, _ := http.NewRequest(PUT, url, data) if n.user != "" && n.password != "" { @@ -161,7 +194,27 @@ func (n *Repository) upload(url string, data io.Reader) error { } defer res.Body.Close() - if res.StatusCode != 201 { + if res.StatusCode != httpSuccess { + return fmt.Errorf(res.Status) + } + return nil +} + +func (n *Repository) delete(url string) error { + const httpSuccess = 204 + log.Logger.Print(url) + req, _ := http.NewRequest(http.MethodDelete, url, nil) + if n.user != "" && n.password != "" { + req.SetBasicAuth(n.user, n.password) + } + + res, err := n.client.Do(req) + if err != nil { + return err + } + defer res.Body.Close() + + if res.StatusCode != httpSuccess { return fmt.Errorf(res.Status) } return nil @@ -212,15 +265,12 @@ type Artifact struct { //NewArtifact create a artifact with this own pom func NewArtifact(groupID, artifactID, version, file string) (*Artifact, error) { + if file == "" { return nil, errors.New("You must specify a file") } - if _, err := os.Stat(file); os.IsNotExist(err) { - return nil, err - } - a := &Artifact{ GroupID: groupID, ArtifactID: artifactID, diff --git a/repositorymanager/repositorymanager_test.go b/repositorymanager/repositorymanager_test.go index c301188..ebaa062 100755 --- a/repositorymanager/repositorymanager_test.go +++ b/repositorymanager/repositorymanager_test.go @@ -69,7 +69,7 @@ func TestRepository_UploadArtifact(t *testing.T) { file.WriteString(forFile.expected) extension := filepath.Ext(file.Name()) - ts := repositoryManagerImplementation(t, extension, &forPom, &forFile) + ts := repositoryManagerUpload(t, extension, &forPom, &forFile) defer ts.Close() repo := NewRepository(ts.URL, "bob", "thesponge") @@ -92,8 +92,41 @@ func TestRepository_UploadArtifact(t *testing.T) { } -//repositoryManagerImplementation TODO too big for what is it -func repositoryManagerImplementation(t *testing.T, extension string, forPom *call, forFile *call) *httptest.Server { +func TestRepository_DeleteArtifact(t *testing.T) { + forPom := call{ + called: false, + calledSha1: false, + calledMd5: false, + } + + forFile := call{ + called: false, + calledSha1: false, + calledMd5: false, + } + + ts := repositoryManagerDelete(t, ".jar", &forPom, &forFile) + defer ts.Close() + repo := NewRepository(ts.URL, "bob", "thesponge") + + a, _ := NewArtifact(groupID, artifactID, version, "bob.jar") + err := repo.DeleteArtifact(a, "sha1", "md5", "not-found") + if err != nil { + t.Fatal(err) + } + + if !forFile.allIsCalled() { + t.Errorf("Problem we are waiting more calls %v", forFile) + } + + if !forPom.allIsCalled() { + t.Errorf("Problem we are waiting more calls %v", forPom) + } + +} + +//repositoryManagerUpload TODO too big for what is it +func repositoryManagerUpload(t *testing.T, extension string, forPom *call, forFile *call) *httptest.Server { ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { w.WriteHeader(201) bodyBytes, err := ioutil.ReadAll(r.Body) @@ -154,6 +187,30 @@ func repositoryManagerImplementation(t *testing.T, extension string, forPom *cal return ts } +//repositoryManagerDelete TODO too big for what is it +func repositoryManagerDelete(t *testing.T, extension string, forPom *call, forFile *call) *httptest.Server { + ts := httptest.NewServer(http.HandlerFunc(func(w http.ResponseWriter, r *http.Request) { + w.WriteHeader(204) + path := "/com/jeandeaux/elyne/0.1.0-SNAPSHOT/elyne-0.1.0-SNAPSHOT" + switch r.URL.Path { + case fmt.Sprint(path, ".pom"): + forPom.called = true + case fmt.Sprint(path, ".pom.sha1"): + forPom.calledSha1 = true + case fmt.Sprint(path, ".pom.md5"): + forPom.calledMd5 = true + case fmt.Sprint(path, extension): + forFile.called = true + case fmt.Sprint(path, extension, ".sha1"): + forFile.calledSha1 = true + case fmt.Sprint(path, extension, ".md5"): + forFile.calledMd5 = true + } + + })) + return ts +} + func TestArtifact_writePom(t *testing.T) { a := &Artifact{GroupID: groupID, ArtifactID: artifactID, Version: version} actualBytes, err := a.writePom() @@ -193,13 +250,6 @@ func TestNewArtifact_Ok(t *testing.T) { } } -func TestNewArtifact_Ko_Because_Not_Found(t *testing.T) { - _, err := NewArtifact(groupID, artifactID, version, "") - if err == nil { - t.Fatal("I want a error") - } -} - func TestNewArtifact_Ko_Because_Name_Empty(t *testing.T) { _, err := NewArtifact(groupID, artifactID, version, "") if err == nil {