Skip to content

Commit bf26b85

Browse files
committed
Complete JFrog publisher
1 parent 0729642 commit bf26b85

File tree

2 files changed

+164
-0
lines changed

2 files changed

+164
-0
lines changed

main.go

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,7 @@ package main
33
import (
44
"archive/zip"
55
"errors"
6+
"fmt"
67
"io"
78
"io/ioutil"
89
"log"
@@ -15,6 +16,8 @@ import (
1516
"github.com/jessevdk/go-flags"
1617
)
1718

19+
const version = "v0.1.0"
20+
1821
var commonOpts options
1922
var parser = flags.NewParser(&commonOpts, flags.HelpFlag|flags.PassDoubleDash)
2023

@@ -38,7 +41,16 @@ func init() {
3841

3942
_, _ = parser.AddCommand("publish-folder", "Publish archive to a folder so it can be used as proxy source.",
4043
"Publish archive to a folder so it can be used as proxy source.", &FolderPublishCmd{})
44+
45+
_, _ = parser.AddCommand("publish-jfrog", "Publish archive to jfrog artifactory (requires installed and configured jfrog-cli).",
46+
"Publish archive to jfrog artifactory (requires installed and configured jfrog-cli).", &JFrogPublishCmd{})
47+
48+
_, _ = parser.AddCommand("version", "Show version.", "Show version.", &versionCmd{})
49+
4150
if p, err := exec.LookPath("go"); err == nil {
51+
if !filepath.IsAbs(p) {
52+
p, _ = filepath.Abs(p)
53+
}
4254
commonOpts.GoBinPath = p
4355
}
4456
}
@@ -118,6 +130,7 @@ func checkGo() {
118130
}
119131

120132
func extractZipArchive(src, dst string) error {
133+
verboseF("extracting to: %v\n", color.BlueString(dst))
121134
if _, err := os.Stat(dst); err != nil {
122135
if !errors.Is(err, os.ErrNotExist) {
123136
return err
@@ -225,3 +238,15 @@ func createZipArchive(dir, dst string) error {
225238

226239
return <-done
227240
}
241+
242+
type versionCmd struct{}
243+
244+
// Execute will be called for the last active (sub)command. The
245+
// args argument contains the remaining command line arguments. The
246+
// error that Execute returns will be eventually passed out of the
247+
// Parse method of the Parser.
248+
func (v versionCmd) Execute(args []string) error {
249+
fmt.Println(version)
250+
251+
return nil
252+
}

publish.go

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,13 +2,16 @@ package main
22

33
import (
44
"errors"
5+
"fmt"
56
"io"
67
"io/ioutil"
78
"log"
89
"os"
10+
"os/exec"
911
"path/filepath"
1012
"strings"
1113
"sync"
14+
"unicode"
1215

1316
"github.com/go-sharp/color"
1417
)
@@ -19,6 +22,120 @@ type publishCmd struct {
1922
} `positional-args:"yes" required:"1"`
2023
}
2124

25+
type JFrogPublishCmd struct {
26+
publishCmd
27+
JFrogBinPath string `long:"jfrog-bin" env:"GOP_JFROG_BIN" description:"Set full path to the jfrog-cli binary"`
28+
Repo string `short:"r" long:"repo" required:"yes" description:"Artifactory go repository name ex. go-local."`
29+
}
30+
31+
// Execute will be called for the last active (sub)command. The
32+
// args argument contains the remaining command line arguments. The
33+
// error that Execute returns will be eventually passed out of the
34+
// Parse method of the Parser.
35+
func (j *JFrogPublishCmd) Execute(args []string) error {
36+
log.SetPrefix("Publish-JFrog: ")
37+
if j.JFrogBinPath == "" {
38+
if p, err := exec.LookPath("jfrog"); err == nil {
39+
if !filepath.IsAbs(p) {
40+
p, _ = filepath.Abs(p)
41+
}
42+
j.JFrogBinPath = p
43+
}
44+
}
45+
46+
if j.JFrogBinPath == "" {
47+
log.Fatalln(errorRedPrefix, "missing jfrog cli: install jfrog-cli or specify valid binary path with --jfrog-bin")
48+
}
49+
50+
cfg := j.getJFrogCfg()
51+
if len(cfg) == 0 {
52+
log.Fatalln(errorRedPrefix, "jfrog is not configured")
53+
}
54+
55+
// Print config used
56+
for _, i := range cfg {
57+
log.Println("config:", color.BlueString(i))
58+
}
59+
60+
workDir, cleanFn := createTempWorkDir()
61+
defer cleanFn()
62+
63+
log.Println("extracting archive")
64+
if err := extractZipArchive(j.PosArgs.Archive, workDir); err != nil {
65+
log.Fatalln(errorRedPrefix, " failed to extract archive:", err)
66+
}
67+
68+
workCh := make(chan string, 10)
69+
doneCh := make(chan struct{})
70+
go func() {
71+
for mod := range workCh {
72+
pkg := strings.Split(filepath.Base(mod), "@")
73+
if len(pkg) != 2 {
74+
log.Println(color.YellowString("warning:"), "invalid module directory:", filepath.Base(mod))
75+
continue
76+
}
77+
78+
goModF := filepath.Join(mod, "go.mod")
79+
if _, err := os.Stat(goModF); errors.Is(err, os.ErrNotExist) {
80+
modName := filepath.Dir(strings.TrimPrefix(mod, workDir+string(filepath.Separator)))
81+
modName = strToModuleName(modName + "/" + pkg[0])
82+
if err := ioutil.WriteFile(goModF, []byte(fmt.Sprintf("module %v\n", modName)), 0664); err != nil {
83+
verboseF("%v: %v\n", errorRedPrefix, err)
84+
}
85+
}
86+
87+
cmd := exec.Command(j.JFrogBinPath, "rt", "gp", j.Repo, pkg[1])
88+
cmd.Dir = mod
89+
90+
verboseF("publishing module %v %v\n", color.BlueString(pkg[0]), color.BlueString(pkg[1]))
91+
if output, err := cmd.CombinedOutput(); err != nil {
92+
log.Println(errorRedPrefix, "failed publish module:", pkg[0], pkg[1], err)
93+
if len(output) > 0 {
94+
verboseF("%v\n%v", errorRedPrefix, string(output))
95+
}
96+
continue
97+
}
98+
}
99+
doneCh <- struct{}{}
100+
}()
101+
102+
log.Println("publishing modules")
103+
filepath.Walk(workDir, func(path string, info os.FileInfo, err error) error {
104+
if strings.HasPrefix(info.Name(), "cache") {
105+
return filepath.SkipDir
106+
}
107+
108+
if !info.IsDir() || !strings.Contains(info.Name(), "@") {
109+
return nil
110+
}
111+
workCh <- path
112+
return filepath.SkipDir
113+
})
114+
close(workCh)
115+
116+
<-doneCh
117+
118+
log.Println("modules successfully uploaded")
119+
return nil
120+
}
121+
122+
func (j JFrogPublishCmd) getJFrogCfg() (config []string) {
123+
data, err := exec.Command(j.JFrogBinPath, "rt", "c", "show").Output()
124+
if err != nil {
125+
log.Fatalln(errorRedPrefix, "failed to get jfrog config:", err)
126+
}
127+
128+
for _, v := range strings.Split(string(data), "\n") {
129+
if strings.HasPrefix(strings.ToLower(v), "server") || strings.HasPrefix(strings.ToLower(v), "url") ||
130+
strings.HasPrefix(strings.ToLower(v), "user") {
131+
config = append(config, v)
132+
}
133+
}
134+
135+
return config
136+
}
137+
138+
// FolderPublishCmd publishes an archive of modules to a folder.
22139
type FolderPublishCmd struct {
23140
publishCmd
24141
Output string `short:"o" long:"out" required:"yes" description:"Output folder for the archive."`
@@ -182,3 +299,25 @@ func (f FolderPublishCmd) handleCopyFile(path, relPath string) {
182299
return
183300
}
184301
}
302+
303+
func strToModuleName(name string) string {
304+
name = filepath.ToSlash(name)
305+
var modName []rune
306+
307+
nextToUpper := false
308+
for _, v := range name {
309+
if nextToUpper {
310+
modName = append(modName, unicode.ToUpper(v))
311+
nextToUpper = false
312+
continue
313+
}
314+
315+
if v == '!' {
316+
nextToUpper = true
317+
continue
318+
}
319+
modName = append(modName, v)
320+
}
321+
322+
return string(modName)
323+
}

0 commit comments

Comments
 (0)