Skip to content

Commit b0a002d

Browse files
authored
Simplify command line arguments (#2)
1 parent 0ed85c4 commit b0a002d

File tree

6 files changed

+139
-86
lines changed

6 files changed

+139
-86
lines changed

go.sum

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,6 @@ github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwc
77
github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
88
github.com/fsnotify/fsnotify v1.4.7/go.mod h1:jwhsz4b93w/PPRr/qN1Yymfu8t87LnFCMoQvtojpjFo=
99
github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ=
10-
github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM=
1110
github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8=
1211
github.com/magiconair/properties v1.8.0/go.mod h1:PppfXfuXeibc/6YijjN8zIbojt8czPbwD3XqdrwzmxQ=
1312
github.com/mitchellh/go-homedir v1.1.0/go.mod h1:SfyaCUpYCn1Vlf4IUYiD9fPX4A5wJrkLzIz1N1q0pr0=

internal/commands/default.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,9 @@ package commands
22

33
import "github.com/spf13/cobra"
44

5-
// NewDefaultCommand creates a new default command
5+
// NewDefaultCommand creates a new default command for when
6+
// the user does not provide a command
67
func NewDefaultCommand() *cobra.Command {
7-
88
cmd := cobra.Command{
99
Use: "pacmod <subcommand>",
1010
Short: "Command line tool to assist with packaging Go modules",

internal/commands/pack.go

Lines changed: 8 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,18 +4,18 @@ import (
44
"fmt"
55
"log"
66
"os"
7-
"path/filepath"
87

98
"github.com/plexsystems/pacmod/pack"
109
"github.com/spf13/cobra"
1110
)
1211

13-
// NewPackCommand creates a new pack command
12+
// NewPackCommand creates a new pack command which allows
13+
// the user to package their Go modules
1414
func NewPackCommand() *cobra.Command {
1515
cmd := cobra.Command{
16-
Use: "pack <module> <version> <outputdirectory>",
16+
Use: "pack <version> <outputdirectory>",
1717
Short: "Package your Go module",
18-
Args: cobra.MinimumNArgs(3),
18+
Args: cobra.MinimumNArgs(2),
1919

2020
RunE: func(cmd *cobra.Command, args []string) error {
2121
return runPackCommand(args)
@@ -31,19 +31,11 @@ func runPackCommand(args []string) error {
3131
return fmt.Errorf("could not get working directory: %w", err)
3232
}
3333

34-
path = filepath.ToSlash(path)
35-
name := args[0]
36-
version := args[1]
37-
outputDirectory := args[2]
34+
version := args[0]
35+
outputDirectory := args[1]
3836

39-
module := pack.Module{
40-
Path: path,
41-
Name: name,
42-
Version: version,
43-
}
44-
45-
log.Printf("Packing module %s...", name)
46-
if err := module.PackageModule(outputDirectory); err != nil {
37+
log.Printf("Packing module in path %s...", path)
38+
if err := pack.Module(path, version, outputDirectory); err != nil {
4739
return fmt.Errorf("could not package module: %w", err)
4840
}
4941

main.go

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -7,8 +7,7 @@ import (
77
)
88

99
func main() {
10-
err := commands.NewDefaultCommand().Execute()
11-
if err != nil {
10+
if err := commands.NewDefaultCommand().Execute(); err != nil {
1211
os.Exit(1)
1312
}
1413
}

pack/pack.go

Lines changed: 98 additions & 47 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package pack
22

33
import (
44
"archive/zip"
5+
"bufio"
56
"encoding/json"
67
"fmt"
78
"io"
@@ -11,100 +12,145 @@ import (
1112
"time"
1213
)
1314

14-
// Module represents a Go module
15-
type Module struct {
16-
Path string
17-
Name string
18-
Version string
19-
}
15+
// Module packs the module at the given path and version then
16+
// outputs the result to the specified output directory
17+
func Module(path string, version string, outputDirectory string) error {
18+
moduleName, err := getModuleName(path)
19+
if err != nil {
20+
return fmt.Errorf("could not get module name: %w", err)
21+
}
2022

21-
// PackageModule packs the module and outputs the result to the specified output path
22-
func (m Module) PackageModule(outputDirectory string) error {
23-
if err := m.createZipArchive(outputDirectory); err != nil {
23+
if err := createZipArchive(path, moduleName, version, outputDirectory); err != nil {
2424
return fmt.Errorf("could not create zip archive: %w", err)
2525
}
2626

27-
if err := m.createInfoFile(outputDirectory); err != nil {
27+
if err := createInfoFile(version, outputDirectory); err != nil {
2828
return fmt.Errorf("could not create info file: %w", err)
2929
}
3030

31-
if err := m.copyModuleFile(outputDirectory); err != nil {
31+
if err := copyModuleFile(path, outputDirectory); err != nil {
3232
return fmt.Errorf("could not copy module file: %w", err)
3333
}
3434

3535
return nil
3636
}
3737

38-
func (m Module) createZipArchive(outputDirectory string) error {
39-
outputPath := filepath.Join(outputDirectory, m.Version+".zip")
38+
func getModuleName(path string) (string, error) {
39+
moduleFilePath := filepath.Join(path, "go.mod")
4040

41-
zipFile, err := os.Create(outputPath)
41+
file, err := os.Open(moduleFilePath)
4242
if err != nil {
43-
return fmt.Errorf("unable to create empty zip file: %w", err)
43+
return "", fmt.Errorf("unable to open module file: %w", err)
4444
}
45-
defer zipFile.Close()
45+
defer file.Close()
4646

47-
zipWriter := zip.NewWriter(zipFile)
48-
err = filepath.Walk(m.Path, func(currentFilePath string, fileInfo os.FileInfo, err error) error {
47+
moduleFileReader := bufio.NewReader(file)
48+
moduleHeader, err := moduleFileReader.ReadString('\n')
49+
if err != nil {
50+
return "", fmt.Errorf("unable to read module header: %w", err)
51+
}
52+
53+
moduleHeaderParts := strings.Split(moduleHeader, " ")
54+
if len(moduleHeaderParts) <= 1 {
55+
return "", fmt.Errorf("unable to parse module header: %w", err)
56+
}
57+
58+
return moduleHeaderParts[1], nil
59+
}
60+
61+
func getFilesToArchive(path string) ([]string, error) {
62+
var files []string
63+
err := filepath.Walk(path, func(currentFilePath string, fileInfo os.FileInfo, err error) error {
4964
if err != nil {
5065
return fmt.Errorf("unable to walk path: %w", err)
5166
}
5267

68+
// We do not want to include the .git directory in the archived module
69+
// filepath.SkipDir tells the Walk() function to ignore everything inside of the directory
5370
if fileInfo.IsDir() && fileInfo.Name() == ".git" {
5471
return filepath.SkipDir
5572
}
5673

57-
if fileInfo.IsDir() || filepath.Ext(currentFilePath) == ".zip" {
74+
// Do not process directories
75+
// returning nil tells the Walk() function to ignore this file
76+
if fileInfo.IsDir() {
5877
return nil
5978
}
6079

61-
file, err := os.Open(currentFilePath)
62-
if err != nil {
63-
return fmt.Errorf("unable to open file: %w", err)
64-
}
65-
defer file.Close()
80+
files = append(files, currentFilePath)
81+
82+
return nil
83+
})
84+
if err != nil {
85+
return nil, err
86+
}
87+
88+
return files, nil
89+
}
90+
91+
func createZipArchive(path string, moduleName string, version string, outputDirectory string) error {
92+
outputPath := filepath.Join(outputDirectory, version+".zip")
6693

67-
zipPath := m.getZipPath(currentFilePath)
68-
zipFileWriter, err := zipWriter.Create(zipPath)
94+
zipFile, err := os.Create(outputPath)
95+
if err != nil {
96+
return fmt.Errorf("unable to create zip file: %w", err)
97+
}
98+
defer zipFile.Close()
99+
100+
zipWriter := zip.NewWriter(zipFile)
101+
defer zipWriter.Close()
102+
103+
filesToArchive, err := getFilesToArchive(path)
104+
if err != nil {
105+
return fmt.Errorf("unable to get files to archive: %w", err)
106+
}
107+
108+
for _, file := range filesToArchive {
109+
zippedFilePath := getZipPath(path, file, moduleName, version)
110+
zippedFileWriter, err := zipWriter.Create(zippedFilePath)
69111
if err != nil {
70112
return fmt.Errorf("unable to add file to zip archive: %w", err)
71113
}
72114

73-
if _, err := io.Copy(zipFileWriter, file); err != nil {
74-
return fmt.Errorf("unable to copy file to zip archive: %w", err)
115+
fileToZip, err := os.Open(file)
116+
if err != nil {
117+
return fmt.Errorf("unable to open file: %w", err)
75118
}
119+
defer fileToZip.Close()
76120

77-
return nil
78-
})
79-
if err != nil {
80-
return fmt.Errorf("unable to zip all files: %w", err)
121+
if _, err := io.Copy(zippedFileWriter, fileToZip); err != nil {
122+
return fmt.Errorf("unable to copy file contents to zip archive: %w", err)
123+
}
81124
}
82125

83-
return zipWriter.Close()
126+
return nil
84127
}
85128

86-
func (m Module) getZipPath(currentFilePath string) string {
87-
fileName := strings.TrimPrefix(currentFilePath, m.Path)
88-
moduleName := fmt.Sprintf("%s@%s", m.Name, m.Version)
89-
90-
return filepath.Join(moduleName, fileName)
129+
func getZipPath(path string, currentFilePath string, moduleName string, version string) string {
130+
filePath := strings.TrimPrefix(currentFilePath, path)
131+
return filepath.Join(fmt.Sprintf("%s@%s", moduleName, version), filePath)
91132
}
92133

93-
func (m Module) createInfoFile(outputDirectory string) error {
94-
infoFilePath := filepath.Join(outputDirectory, m.Version+".info")
134+
func createInfoFile(version string, outputDirectory string) error {
135+
infoFilePath := filepath.Join(outputDirectory, version+".info")
95136
file, err := os.Create(infoFilePath)
96137
if err != nil {
97138
return fmt.Errorf("could not create info file: %w", err)
98139
}
99140
defer file.Close()
100141

101-
infoBytes, err := json.Marshal(struct {
142+
type infoFile struct {
102143
Version string
103144
Time string
104-
}{
105-
Version: m.Version,
106-
Time: time.Now().Format("2006-01-02T15:04:05Z"),
107-
})
145+
}
146+
147+
currentTime := getInfoFileFormattedTime(time.Now())
148+
info := infoFile{
149+
Version: version,
150+
Time: currentTime,
151+
}
152+
153+
infoBytes, err := json.Marshal(info)
108154
if err != nil {
109155
return fmt.Errorf("could not marshal info file: %w", err)
110156
}
@@ -116,8 +162,13 @@ func (m Module) createInfoFile(outputDirectory string) error {
116162
return nil
117163
}
118164

119-
func (m Module) copyModuleFile(outputDirectory string) error {
120-
sourcePath := filepath.Join(m.Path, "go.mod")
165+
func getInfoFileFormattedTime(currentTime time.Time) string {
166+
const infoFileTimeFormat = "2006-01-02T15:04:05Z"
167+
return currentTime.Format(infoFileTimeFormat)
168+
}
169+
170+
func copyModuleFile(modulePath string, outputDirectory string) error {
171+
sourcePath := filepath.Join(modulePath, "go.mod")
121172
destinationPath := filepath.Join(outputDirectory, "go.mod")
122173

123174
sourceModule, err := os.Open(sourcePath)

pack/pack_test.go

Lines changed: 30 additions & 18 deletions
Original file line numberDiff line numberDiff line change
@@ -1,32 +1,44 @@
11
package pack
22

3-
import "testing"
3+
import (
4+
"testing"
5+
"time"
6+
)
47

5-
func Test_GetZipPath_PathAndModulePathsAreSame(t *testing.T) {
6-
module := Module{
7-
Path: "/root/",
8-
Name: "root",
9-
Version: "v1.0.0",
8+
func Test_GetInfoFile_ReturnsCorrectTimeFormat(t *testing.T) {
9+
goLaunchDate := time.Date(2009, time.November, 10, 23, 0, 0, 0, time.UTC)
10+
actual := getInfoFileFormattedTime(goLaunchDate)
11+
expected := "2009-11-10T23:00:00Z"
12+
13+
if expected != actual {
14+
t.Errorf("invalid infofile time format: expected %v actual %v", expected, actual)
1015
}
16+
}
1117

12-
actual := module.getZipPath("/root/app.go")
18+
func Test_GetZipPath_PathAndModulePathsAreSame(t *testing.T) {
19+
modulePath := "/root/"
20+
currentFilePath := "/root/app.go"
21+
name := "root"
22+
version := "v1.0.0"
23+
24+
actual := getZipPath(modulePath, currentFilePath, name, version)
1325
expected := "root@v1.0.0/app.go"
1426

15-
if actual != expected {
16-
t.Errorf("expected %v, got %v", expected, actual)
27+
if expected != actual {
28+
t.Errorf("invalid zip path: expected %v actual %v", expected, actual)
1729
}
1830
}
1931

20-
func Test_GetZipPath_ModulePathChildOfPath(t *testing.T) {
21-
module := Module{
22-
Path: "/root/repository/username/app",
23-
Name: "repository/username/app",
24-
Version: "v1.0.0",
25-
}
26-
actual := module.getZipPath("/root/repository/username/app/app.go")
32+
func Test_GetZipPath_ModulePathIsChildOfPath(t *testing.T) {
33+
modulePath := "/root/repository/username/app"
34+
currentFilePath := "/root/repository/username/app/app.go"
35+
name := "repository/username/app"
36+
version := "v1.0.0"
37+
38+
actual := getZipPath(modulePath, currentFilePath, name, version)
2739
expected := "repository/username/app@v1.0.0/app.go"
2840

29-
if actual != expected {
30-
t.Errorf("expected %v, got %v", expected, actual)
41+
if expected != actual {
42+
t.Errorf("invalid zip path: expected %v actual %v", expected, actual)
3143
}
3244
}

0 commit comments

Comments
 (0)