Skip to content

Commit

Permalink
Add archive command to zip a sketch and its files (#931)
Browse files Browse the repository at this point in the history
  • Loading branch information
silvanocerza authored Sep 3, 2020
1 parent e6f1947 commit b8cf32d
Show file tree
Hide file tree
Showing 24 changed files with 5,713 additions and 321 deletions.
77 changes: 77 additions & 0 deletions cli/sketch/archive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,77 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package sketch

import (
"context"
"os"

"github.com/arduino/arduino-cli/cli/errorcodes"
"github.com/arduino/arduino-cli/cli/feedback"
"github.com/arduino/arduino-cli/commands/sketch"
rpc "github.com/arduino/arduino-cli/rpc/commands"
"github.com/sirupsen/logrus"
"github.com/spf13/cobra"
)

var includeBuildDir bool

// initArchiveCommand creates a new `archive` command
func initArchiveCommand() *cobra.Command {
command := &cobra.Command{
Use: "archive <sketchPath> <archivePath>",
Short: "Creates a zip file containing all sketch files.",
Long: "Creates a zip file containing all sketch files.",
Example: "" +
" " + os.Args[0] + " archive\n" +
" " + os.Args[0] + " archive .\n" +
" " + os.Args[0] + " archive . MySketchArchive.zip\n" +
" " + os.Args[0] + " archive /home/user/Arduino/MySketch\n" +
" " + os.Args[0] + " archive /home/user/Arduino/MySketch /home/user/MySketchArchive.zip",
Args: cobra.MaximumNArgs(2),
Run: runArchiveCommand,
}

command.Flags().BoolVar(&includeBuildDir, "include-build-dir", false, "Includes build directory in the archive.")

return command
}

func runArchiveCommand(cmd *cobra.Command, args []string) {
logrus.Info("Executing `arduino sketch archive`")

sketchPath := ""
if len(args) >= 1 {
sketchPath = args[0]
}

archivePath := ""
if len(args) == 2 {
archivePath = args[1]
}

_, err := sketch.ArchiveSketch(context.Background(),
&rpc.ArchiveSketchReq{
SketchPath: sketchPath,
ArchivePath: archivePath,
IncludeBuildDir: includeBuildDir,
})

if err != nil {
feedback.Errorf("Error archiving: %v", err)
os.Exit(errorcodes.ErrGeneric)
}
}
1 change: 1 addition & 0 deletions cli/sketch/sketch.go
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ func NewCommand() *cobra.Command {
}

cmd.AddCommand(initNewCommand())
cmd.AddCommand(initArchiveCommand())

return cmd
}
6 changes: 6 additions & 0 deletions commands/daemon/daemon.go
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ import (
"github.com/arduino/arduino-cli/commands/compile"
"github.com/arduino/arduino-cli/commands/core"
"github.com/arduino/arduino-cli/commands/lib"
"github.com/arduino/arduino-cli/commands/sketch"
"github.com/arduino/arduino-cli/commands/upload"
rpc "github.com/arduino/arduino-cli/rpc/commands"
)
Expand Down Expand Up @@ -337,3 +338,8 @@ func (s *ArduinoCoreServerImpl) LibrarySearch(ctx context.Context, req *rpc.Libr
func (s *ArduinoCoreServerImpl) LibraryList(ctx context.Context, req *rpc.LibraryListReq) (*rpc.LibraryListResp, error) {
return lib.LibraryList(ctx, req)
}

// ArchiveSketch FIXMEDOC
func (s *ArduinoCoreServerImpl) ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchReq) (*rpc.ArchiveSketchResp, error) {
return sketch.ArchiveSketch(ctx, req)
}
152 changes: 152 additions & 0 deletions commands/sketch/archive.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,152 @@
// This file is part of arduino-cli.
//
// Copyright 2020 ARDUINO SA (http://www.arduino.cc/)
//
// This software is released under the GNU General Public License version 3,
// which covers the main part of arduino-cli.
// The terms of this license can be found at:
// https://www.gnu.org/licenses/gpl-3.0.en.html
//
// You can be released from the requirements of the above licenses by purchasing
// a commercial license. Buying such a license is mandatory if you want to
// modify or otherwise use the software for commercial activities involving the
// Arduino software without disclosing the source code of your own applications.
// To purchase a commercial license, send an email to license@arduino.cc.

package sketch

import (
"archive/zip"
"context"
"fmt"
"io"
"path/filepath"
"strings"

rpc "github.com/arduino/arduino-cli/rpc/commands"
paths "github.com/arduino/go-paths-helper"
)

// ArchiveSketch FIXMEDOC
func ArchiveSketch(ctx context.Context, req *rpc.ArchiveSketchReq) (*rpc.ArchiveSketchResp, error) {
// sketchName is the name of the sketch without extension, for example "MySketch"
var sketchName string

sketchPath := paths.New(req.SketchPath)
if sketchPath == nil {
sketchPath = paths.New(".")
}

sketchPath, err := sketchPath.Clean().Abs()
if err != nil {
return nil, fmt.Errorf("Error getting absolute sketch path %v", err)
}

// Get the sketch name and make sketchPath point to the ino file
if sketchPath.IsDir() {
sketchName = sketchPath.Base()
sketchPath = sketchPath.Join(sketchName + ".ino")
} else if sketchPath.Ext() == ".ino" {
sketchName = strings.TrimSuffix(sketchPath.Base(), ".ino")
}

// Checks if it's really a sketch
if sketchPath.NotExist() {
return nil, fmt.Errorf("specified path is not a sketch: %v", sketchPath.String())
}

archivePath := paths.New(req.ArchivePath)
if archivePath == nil {
archivePath = sketchPath.Parent().Parent()
}

archivePath, err = archivePath.Clean().Abs()
if err != nil {
return nil, fmt.Errorf("Error getting absolute archive path %v", err)
}

// Makes archivePath point to a zip file
if archivePath.IsDir() {
archivePath = archivePath.Join(sketchName + ".zip")
} else if archivePath.Ext() == "" {
archivePath = paths.New(archivePath.String() + ".zip")
}

if archivePath.Exist() {
return nil, fmt.Errorf("archive already exists")
}

filesToZip, err := sketchPath.Parent().ReadDirRecursive()
if err != nil {
return nil, fmt.Errorf("Error retrieving sketch files: %v", err)
}
filesToZip.FilterOutDirs()

archive, err := archivePath.Create()
if err != nil {
return nil, fmt.Errorf("Error creating archive: %v", err)
}
defer archive.Close()

zipWriter := zip.NewWriter(archive)
defer zipWriter.Close()

for _, f := range filesToZip {

if !req.IncludeBuildDir {
filePath, err := sketchPath.Parent().Parent().RelTo(f)
if err != nil {
return nil, fmt.Errorf("Error calculating relative file path: %v", err)
}

// Skips build folder
if strings.HasPrefix(filePath.String(), sketchName+string(filepath.Separator)+"build") {
continue
}
}

// We get the parent path since we want the archive to unpack as a folder.
// If we don't do this the archive would contain all the sketch files as top level.
err = addFileToSketchArchive(zipWriter, f, sketchPath.Parent().Parent())
if err != nil {
return nil, fmt.Errorf("Error adding file to archive: %v", err)
}
}

return &rpc.ArchiveSketchResp{}, nil
}

// Adds a single file to an existing zip file
func addFileToSketchArchive(zipWriter *zip.Writer, filePath, sketchPath *paths.Path) error {
f, err := filePath.Open()
if err != nil {
return err
}
defer f.Close()

info, err := f.Stat()
if err != nil {
return err
}

header, err := zip.FileInfoHeader(info)
if err != nil {
return err
}

filePath, err = sketchPath.RelTo(filePath)
if err != nil {
return err
}

header.Name = filePath.String()
header.Method = zip.Deflate

writer, err := zipWriter.CreateHeader(header)
if err != nil {
return err
}

_, err = io.Copy(writer, f)
return err
}
2 changes: 1 addition & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ require (
bou.ke/monkey v1.0.1
github.com/GeertJohan/go.rice v1.0.0
github.com/arduino/board-discovery v0.0.0-20180823133458-1ba29327fb0c
github.com/arduino/go-paths-helper v1.2.0
github.com/arduino/go-paths-helper v1.3.1
github.com/arduino/go-properties-orderedmap v1.3.0
github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b
github.com/arduino/go-win32-utils v0.0.0-20180330194947-ed041402e83b
Expand Down
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,8 @@ github.com/arduino/go-paths-helper v1.0.1 h1:utYXLM2RfFlc9qp/MJTIYp3t6ux/xM6mWje
github.com/arduino/go-paths-helper v1.0.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
github.com/arduino/go-paths-helper v1.2.0 h1:qDW93PR5IZUN/jzO4rCtexiwF8P4OIcOmcSgAYLZfY4=
github.com/arduino/go-paths-helper v1.2.0/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
github.com/arduino/go-paths-helper v1.3.1 h1:Gz+PVt0luQyH4nffDePd8WBs/O5P05jADtJsY8NqvCM=
github.com/arduino/go-paths-helper v1.3.1/go.mod h1:HpxtKph+g238EJHq4geEPv9p+gl3v5YYu35Yb+w31Ck=
github.com/arduino/go-properties-orderedmap v1.3.0 h1:4No/vQopB36e7WUIk6H6TxiSEJPiMrVOCZylYmua39o=
github.com/arduino/go-properties-orderedmap v1.3.0/go.mod h1:DKjD2VXY/NZmlingh4lSFMEYCVubfeArCsGPGDwb2yk=
github.com/arduino/go-timeutils v0.0.0-20171220113728-d1dd9e313b1b h1:9hDi4F2st6dbLC3y4i02zFT5quS4X6iioWifGlVwfy4=
Expand Down
Loading

0 comments on commit b8cf32d

Please sign in to comment.