@@ -2,6 +2,7 @@ package pack
22
33import (
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 )
0 commit comments