Skip to content

Commit

Permalink
[nexus-cli] upload jar into repository
Browse files Browse the repository at this point in the history
- command line
- docker to run
  • Loading branch information
sjeandeaux committed Sep 19, 2017
0 parents commit cbf1b89
Show file tree
Hide file tree
Showing 6 changed files with 596 additions and 0 deletions.
9 changes: 9 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
FROM golang:1.9

WORKDIR /go/src/github.com/sjeandeaux/nexus-cli
COPY . .

RUN go-wrapper download # "go get -d -v ./..."
RUN go-wrapper install # "go install -v ./..."

ENTRYPOINT ["go-wrapper" , "run"]
29 changes: 29 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
# nexus-cli

## upload

I share a volume where i have my upload.jar file.

```bash

#run your own nexus
docker run -d -P --name nexus sonatype/nexus3:3.5.1
docker build --tag sjeandeaux/nexus-cli .
docker run --link nexus:nexus -ti -v $(pwd):$(pwd):ro sjeandeaux/nexus-cli \
-repo=http://nexus:8081/repository/maven-releases \
-user=admin \
-password=admin123 \
-file=$(pwd)/upload.jar \
-groupID=com.jeandeaux \
-artifactID=elyne \
-version=0.1.0 \
-hash md5 \
-hash sha1

```

## TODOs

- [ ] interface for artifact
- [ ] interface for repository
- [ ] integration tests
9 changes: 9 additions & 0 deletions log/log.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package log

import (
"io/ioutil"
"log"
)

// Logger logger of application.
var Logger *log.Logger = log.New(ioutil.Discard, "[☯ nexus-cli ☯] ⇒ ", log.Ldate|log.Ltime|log.LUTC)
72 changes: 72 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
package main

import (
"flag"

"fmt"

"github.com/sjeandeaux/nexus-cli/log"
"github.com/sjeandeaux/nexus-cli/repositorymanager"

"os"
)

type enableHash []string

func (i *enableHash) String() string {
return fmt.Sprintf("%s", *i)
}

func (i *enableHash) Set(value string) error {
*i = append(*i, value)
return nil
}

//commandLineArgs all parameters in command line
type commandLineArgs struct {
//url of repository to upload file
urlOfRepository string
user string
password string
//file to upload
file string
//groupID of artifact
groupID string
//artifactID of artifact
artifactID string
//version of artifact
version string
//hashs the hashs which you want to upload
hash enableHash
}

var commandLine = &commandLineArgs{}

//init configuration
func init() {
log.Logger.SetOutput(os.Stdout)
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.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")
flag.Var(&commandLine.hash, "hash", "md5 or/and sha1")
flag.Parse()
}

//main upload artifact
func main() {

repo := repositorymanager.NewRepository(commandLine.urlOfRepository, commandLine.user, commandLine.password)

artifact, err := repositorymanager.NewArtifact(commandLine.groupID, commandLine.artifactID, commandLine.version, commandLine.file)
if err != nil {
log.Logger.Fatal(err)
}

if err := repo.UploadArtifact(artifact, commandLine.hash...); err != nil {
log.Logger.Fatal(err)
}
}
264 changes: 264 additions & 0 deletions repositorymanager/repositorymanager.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,264 @@
//Package repositorymanager upload a file in this repository
package repositorymanager

import (
"bytes"
"crypto/md5"
"crypto/sha1"
"encoding/hex"
"fmt"
"hash"
"html/template"
"io"
"net/http"
"os"
"path/filepath"
"strings"

"net/url"

"github.com/sjeandeaux/nexus-cli/log"

"errors"
)

const (
dot = "."
suffixPom = "pom"
)

//Repository where we want put the file
type Repository struct {
url string
user string
password string
client *http.Client
hash map[string]*repositoryHash
}

// repositoryHash create hash and has the suffix for the file on repository
type repositoryHash struct {
suffix string
//TODO see if we need to a func or variable
hash func() hash.Hash
}

//NewRepository create a Repository with default client HTTP.
func NewRepository(url, user, password string) *Repository {
const (
nameMd5 = "md5"
nameSha1 = "sha1"
)

shaOneAndMdFive := make(map[string]*repositoryHash)

shaOneAndMdFive[nameMd5] = &repositoryHash{
suffix: nameMd5,
hash: func() hash.Hash { return md5.New() },
}

shaOneAndMdFive[nameSha1] = &repositoryHash{
suffix: nameSha1,
hash: func() hash.Hash { return sha1.New() },
}

return &Repository{
url: url,
user: user,
password: password,
client: &http.Client{},
hash: shaOneAndMdFive}

}

//UploadArtifact upload ar on repository TODO goroutine to upload
//ar the artifact to upload
//hashs list of hash to send
func (n *Repository) UploadArtifact(ar *Artifact, hashs ...string) error {
pomURL := n.generateURL(ar, suffixPom)
if err := n.upload(pomURL, bytes.NewReader(ar.Pom)); err != nil {
return err
}

fOpen, err := os.Open(ar.File)
if err != nil {
return err
}

fileURL := n.generateURL(ar, ar.extension())
if err := n.upload(fileURL, fOpen); err != nil {
return err
}

for _, h := range hashs {
if iGetIt := n.hash[h]; iGetIt != nil {
n.uploadHash(ar, iGetIt)
} else {
urlIssue := generateURLIssue(h)
log.Logger.Printf("%q is not managed by the client %q", h, urlIssue)
}
}

return nil
}

//generateURLIssue generate the URL on github to create issue on hash method
func generateURLIssue(h string) string {
const (
title = "Move your ass"
urlFormat = "https://github.com/sjeandeaux/nexus-cli/issues/new?title=%s&body=%s"
bodyFormat = "Could you add the hash %q lazy man?"
)
escapedTitle := url.QueryEscape(title)
body := fmt.Sprintf(bodyFormat, h)
escapedBody := url.QueryEscape(body)
urlIssue := fmt.Sprintf(urlFormat, escapedTitle, escapedBody)
return urlIssue
}

//generateURL generate the url of ar
// <url>/<groupID/<arID>/<version>/<arID>-<version>.<endOfFile>
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)
}

//uploadHash upload the hash
func (n *Repository) uploadHash(ar *Artifact, h *repositoryHash) error {
p, f, err := generateHash(ar.Pom, ar.File, h.hash)
if err != nil {
return err
}

hashedPom := n.generateURL(ar, fmt.Sprint(suffixPom, dot, h.suffix))
if err = n.upload(hashedPom, p); err != nil {
return err
}

hashedFile := n.generateURL(ar, fmt.Sprint(ar.extension(), dot, h.suffix))
return n.upload(hashedFile, f)
}

func (n *Repository) upload(url string, data io.Reader) error {
const PUT = "PUT"
log.Logger.Print(url)
req, _ := http.NewRequest(PUT, url, data)
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 != 201 {
return fmt.Errorf(res.Status)
}
return nil
}

//generateHash generate the
func generateHash(pom []byte, file string, h func() hash.Hash) (io.Reader, io.Reader, error) {
hashedPom, err := hashSum(bytes.NewReader(pom), h())
if err != nil {
return nil, nil, err
}

f, errOnFile := os.Open(file)
defer f.Close()
if errOnFile != nil {
return nil, nil, errOnFile
}

hashedFile, err := hashSum(f, h())
if err != nil {
return nil, nil, err
}

return hashedPom, hashedFile, nil
}

//generate the hash of io.Reader
func hashSum(data io.Reader, h hash.Hash) (io.Reader, error) {
if _, err := io.Copy(h, data); err != nil {
return nil, err
}
return strings.NewReader(hex.EncodeToString(h.Sum(nil))), nil
}

//Artifact the artifact
type Artifact struct {
//GroupID of artifact
GroupID string
//ArtifactID of artifact
ArtifactID string
//Version of artifact
Version string
//file to upload
File string
//pom of this artifact
Pom []byte
}

//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,
Version: version,
File: file,
}

pom, err := a.writePom()
if err != nil {
return nil, err
}
a.Pom = pom
return a, nil
}

// extension extension of file
func (artifact *Artifact) extension() string {
const unknown = "unknown"

if ex := filepath.Ext(artifact.File); ex != "" {
return ex[1:]
}
return unknown
}

// writePom write a wonderful pom
func (artifact *Artifact) writePom() ([]byte, error) {
const templateName = "pom"
const templateValue = `<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd"><modelVersion>4.0.0</modelVersion><groupId>{{.GroupID}}</groupId><artifactId>{{.ArtifactID}}</artifactId><version>{{.Version}}</version></project>`

pomTemplate, err := template.New(templateName).Parse(templateValue)
if err != nil {
return nil, err
}

var buf bytes.Buffer
err = pomTemplate.Execute(&buf, artifact)
if err != nil {
return nil, err
}
return buf.Bytes(), nil
}
Loading

0 comments on commit cbf1b89

Please sign in to comment.