44 "archive/zip"
55 "bytes"
66 "context"
7+ "crypto/sha256"
78 "encoding/json"
89 "fmt"
910 "io"
@@ -31,16 +32,18 @@ This creates a directory with the plugin binaries, package.json and documentatio
3132type PackageJSON struct {
3233 SchemaVersion int `json:"schema_version"`
3334 Name string `json:"name"`
35+ Message string `json:"message"`
3436 Version string `json:"version"`
3537 Protocols []int `json:"protocols"`
3638 SupportedTargets []TargetBuild `json:"supported_targets"`
3739 PackageType plugin.PackageType `json:"package_type"`
3840}
3941
4042type TargetBuild struct {
41- OS string `json:"os"`
42- Arch string `json:"arch"`
43- Path string `json:"path"`
43+ OS string `json:"os"`
44+ Arch string `json:"arch"`
45+ Path string `json:"path"`
46+ Checksum string `json:"checksum"`
4447}
4548
4649func (s * PluginServe ) writeTablesJSON (ctx context.Context , dir string ) error {
@@ -62,13 +65,13 @@ func (s *PluginServe) writeTablesJSON(ctx context.Context, dir string) error {
6265 return os .WriteFile (outputPath , buffer .Bytes (), 0644 )
6366}
6467
65- func (s * PluginServe ) build (pluginDirectory , goos , goarch , distPath , pluginVersion string ) error {
68+ func (s * PluginServe ) build (pluginDirectory , goos , goarch , distPath , pluginVersion string ) ( * TargetBuild , error ) {
6669 pluginName := fmt .Sprintf ("plugin-%s-%s-%s-%s" , s .plugin .Name (), pluginVersion , goos , goarch )
6770 pluginPath := path .Join (distPath , pluginName )
6871 args := []string {"build" , "-o" , pluginPath }
6972 importPath , err := s .getModuleName (pluginDirectory )
7073 if err != nil {
71- return err
74+ return nil , err
7275 }
7376 args = append (args , "-buildmode=exe" )
7477 args = append (args , "-ldflags" , fmt .Sprintf ("-s -w -X %s/plugin.Version=%s" , importPath , pluginVersion ))
@@ -78,19 +81,19 @@ func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersi
7881 cmd .Stderr = os .Stderr
7982 cmd .Env = os .Environ ()
8083 if err := cmd .Run (); err != nil {
81- return fmt .Errorf ("failed to build plugin with `go %v`: %w" , args , err )
84+ return nil , fmt .Errorf ("failed to build plugin with `go %v`: %w" , args , err )
8285 }
8386
8487 pluginFile , err := os .Open (pluginPath )
8588 if err != nil {
86- return fmt .Errorf ("failed to open plugin file: %w" , err )
89+ return nil , fmt .Errorf ("failed to open plugin file: %w" , err )
8790 }
8891 defer pluginFile .Close ()
8992
9093 zipPluginPath := pluginPath + ".zip"
9194 zipPluginFile , err := os .Create (zipPluginPath )
9295 if err != nil {
93- return fmt .Errorf ("failed to create zip file: %w" , err )
96+ return nil , fmt .Errorf ("failed to create zip file: %w" , err )
9497 }
9598 defer zipPluginFile .Close ()
9699
@@ -99,19 +102,52 @@ func (s *PluginServe) build(pluginDirectory, goos, goarch, distPath, pluginVersi
99102
100103 pluginZip , err := zipWriter .Create (pluginName )
101104 if err != nil {
102- return fmt .Errorf ("failed to create file in zip archive: %w" , err )
105+ zipWriter .Close ()
106+ return nil , fmt .Errorf ("failed to create file in zip archive: %w" , err )
103107 }
104108 _ , err = io .Copy (pluginZip , pluginFile )
105109 if err != nil {
106- return fmt .Errorf ("failed to copy plugin file to zip archive: %w" , err )
110+ zipWriter .Close ()
111+ return nil , fmt .Errorf ("failed to copy plugin file to zip archive: %w" , err )
112+ }
113+ err = zipWriter .Close ()
114+ if err != nil {
115+ return nil , fmt .Errorf ("failed to close zip archive: %w" , err )
107116 }
117+
108118 if err := pluginFile .Close (); err != nil {
109- return err
119+ return nil , err
110120 }
111121 if err := os .Remove (pluginPath ); err != nil {
112- return fmt .Errorf ("failed to remove plugin file: %w" , err )
122+ return nil , fmt .Errorf ("failed to remove plugin file: %w" , err )
113123 }
114- return nil
124+
125+ targetZip := fmt .Sprintf (pluginName + ".zip" )
126+ checksum , err := calcChecksum (path .Join (distPath , targetZip ))
127+ if err != nil {
128+ return nil , fmt .Errorf ("failed to calculate checksum: %w" , err )
129+ }
130+
131+ return & TargetBuild {
132+ OS : goos ,
133+ Arch : goarch ,
134+ Path : targetZip ,
135+ Checksum : "sha256:" + checksum ,
136+ }, nil
137+ }
138+
139+ func calcChecksum (p string ) (string , error ) {
140+ // calculate SHA-256 checksum
141+ f , err := os .Open (p )
142+ if err != nil {
143+ return "" , fmt .Errorf ("failed to open file: %w" , err )
144+ }
145+ defer f .Close ()
146+ hash := sha256 .New ()
147+ if _ , err := io .Copy (hash , f ); err != nil {
148+ return "" , err
149+ }
150+ return fmt .Sprintf ("%x" , hash .Sum (nil )), nil
115151}
116152
117153func (* PluginServe ) getModuleName (pluginDirectory string ) (string , error ) {
@@ -131,19 +167,11 @@ func (*PluginServe) getModuleName(pluginDirectory string) (string, error) {
131167 return strings .TrimSpace (importPath ), nil
132168}
133169
134- func (s * PluginServe ) writePackageJSON (dir , pluginVersion string ) error {
135- targets := []TargetBuild {}
136- for _ , target := range s .plugin .Targets () {
137- pluginName := fmt .Sprintf ("plugin-%s-%s-%s-%s" , s .plugin .Name (), pluginVersion , target .OS , target .Arch )
138- targets = append (targets , TargetBuild {
139- OS : target .OS ,
140- Arch : target .Arch ,
141- Path : pluginName + ".zip" ,
142- })
143- }
170+ func (s * PluginServe ) writePackageJSON (dir , pluginVersion , message string , targets []TargetBuild ) error {
144171 packageJSON := PackageJSON {
145172 SchemaVersion : 1 ,
146173 Name : s .plugin .Name (),
174+ Message : message ,
147175 Version : pluginVersion ,
148176 Protocols : s .versions ,
149177 SupportedTargets : targets ,
@@ -206,7 +234,7 @@ func copyFile(src, dst string) error {
206234
207235func (s * PluginServe ) newCmdPluginPackage () * cobra.Command {
208236 cmd := & cobra.Command {
209- Use : "package <plugin_directory> <version>" ,
237+ Use : "package -m <message> <plugin_directory> <version>" ,
210238 Short : pluginPackageShort ,
211239 Long : pluginPackageLong ,
212240 Args : cobra .ExactArgs (2 ),
@@ -221,6 +249,21 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
221249 if cmd .Flag ("docs-dir" ).Changed {
222250 docsPath = cmd .Flag ("docs-dir" ).Value .String ()
223251 }
252+ message := ""
253+ if ! cmd .Flag ("message" ).Changed {
254+ return fmt .Errorf ("message is required" )
255+ }
256+ message = cmd .Flag ("message" ).Value .String ()
257+ if strings .HasPrefix (message , "@" ) {
258+ messageFile := strings .TrimPrefix (message , "@" )
259+ messageBytes , err := os .ReadFile (messageFile )
260+ if err != nil {
261+ return err
262+ }
263+ message = string (messageBytes )
264+ }
265+ message = normalizeMessage (message )
266+
224267 if err := os .MkdirAll (distPath , 0755 ); err != nil {
225268 return err
226269 }
@@ -232,13 +275,16 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
232275 if err := s .writeTablesJSON (cmd .Context (), distPath ); err != nil {
233276 return err
234277 }
278+ targets := []TargetBuild {}
235279 for _ , target := range s .plugin .Targets () {
236280 fmt .Println ("Building for OS: " + target .OS + ", ARCH: " + target .Arch )
237- if err := s .build (pluginDirectory , target .OS , target .Arch , distPath , pluginVersion ); err != nil {
281+ targetBuild , err := s .build (pluginDirectory , target .OS , target .Arch , distPath , pluginVersion )
282+ if err != nil {
238283 return fmt .Errorf ("failed to build plugin for %s/%s: %w" , target .OS , target .Arch , err )
239284 }
285+ targets = append (targets , * targetBuild )
240286 }
241- if err := s .writePackageJSON (distPath , pluginVersion ); err != nil {
287+ if err := s .writePackageJSON (distPath , pluginVersion , message , targets ); err != nil {
242288 return fmt .Errorf ("failed to write manifest: %w" , err )
243289 }
244290 if err := s .copyDocs (distPath , docsPath ); err != nil {
@@ -249,5 +295,13 @@ func (s *PluginServe) newCmdPluginPackage() *cobra.Command {
249295 }
250296 cmd .Flags ().StringP ("dist-dir" , "D" , "" , "dist directory to output the built plugin. (default: <plugin_directory>/dist)" )
251297 cmd .Flags ().StringP ("docs-dir" , "" , "" , "docs directory containing markdown files to copy to the dist directory. (default: <plugin_directory>/docs)" )
298+ cmd .Flags ().StringP ("message" , "m" , "" , "message that summarizes what is new or changed in this version. Use @<file> to read from file. Supports markdown." )
252299 return cmd
253300}
301+
302+ func normalizeMessage (s string ) string {
303+ s = strings .TrimSpace (s )
304+ s = strings .ReplaceAll (s , "\r \n " , "\n " )
305+ s = strings .ReplaceAll (s , "\r " , "\n " )
306+ return s
307+ }
0 commit comments