161 lines
3.3 KiB
Go
161 lines
3.3 KiB
Go
package app
|
|
|
|
import (
|
|
"archive/zip"
|
|
"fmt"
|
|
"io"
|
|
"io/ioutil"
|
|
"os"
|
|
"path/filepath"
|
|
"strings"
|
|
|
|
"forge.cadoles.com/arcad/edge/pkg/app"
|
|
"forge.cadoles.com/arcad/edge/pkg/bundle"
|
|
"github.com/pkg/errors"
|
|
"github.com/urfave/cli/v2"
|
|
)
|
|
|
|
func PackageCommand() *cli.Command {
|
|
return &cli.Command{
|
|
Name: "package",
|
|
Usage: "Generate a new app package from given directory",
|
|
Flags: []cli.Flag{
|
|
&cli.StringFlag{
|
|
Name: "directory",
|
|
Usage: "use source directory `DIR`",
|
|
Aliases: []string{"d"},
|
|
Required: true,
|
|
},
|
|
&cli.StringFlag{
|
|
Name: "output-dir",
|
|
Aliases: []string{"o"},
|
|
Usage: "use `DIR` as generated package destination",
|
|
},
|
|
},
|
|
Action: func(ctx *cli.Context) error {
|
|
appDir := ctx.String("directory")
|
|
outputDir := ctx.String("output-dir")
|
|
|
|
if outputDir == "" {
|
|
workdir, err := os.Getwd()
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not retrieve current working directory")
|
|
}
|
|
|
|
outputDir = workdir
|
|
}
|
|
|
|
bundle := bundle.NewDirectoryBundle(appDir)
|
|
|
|
manifest, err := app.LoadManifest(bundle)
|
|
if err != nil {
|
|
return errors.Wrap(err, "could not load app manifest")
|
|
}
|
|
|
|
if err := os.MkdirAll(outputDir, 0o755); err != nil {
|
|
return errors.Wrapf(err, "could not create directory ''%s'", outputDir)
|
|
}
|
|
|
|
archiveName := fmt.Sprintf(
|
|
"%s_%s%s",
|
|
strings.ToLower(string(manifest.ID)),
|
|
string(manifest.Version),
|
|
".zip",
|
|
)
|
|
packagePath := filepath.Join(outputDir, archiveName)
|
|
|
|
if err := zipDirectory(appDir, packagePath); err != nil {
|
|
return errors.Wrapf(err, "could not zip directory ''%s'", appDir)
|
|
}
|
|
|
|
return nil
|
|
},
|
|
}
|
|
}
|
|
|
|
func zipDirectory(baseDir string, outputFile string) error {
|
|
outFile, err := os.Create(outputFile)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := outFile.Close(); err != nil {
|
|
panic(errors.WithStack(err))
|
|
}
|
|
}()
|
|
|
|
w := zip.NewWriter(outFile)
|
|
|
|
if err := copyDir(w, baseDir+"/", ""); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if err := w.Close(); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func copyDir(writer *zip.Writer, baseDir string, zipBasePath string) error {
|
|
files, err := ioutil.ReadDir(baseDir)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
for _, file := range files {
|
|
if !file.IsDir() {
|
|
srcPath := baseDir + file.Name()
|
|
zipPath := zipBasePath + file.Name()
|
|
|
|
if err := copyFile(writer, srcPath, zipPath); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
} else if file.IsDir() {
|
|
newBase := baseDir + file.Name() + "/"
|
|
|
|
if err := copyDir(writer, newBase, zipBasePath+file.Name()+"/"); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
}
|
|
}
|
|
|
|
return nil
|
|
}
|
|
|
|
func copyFile(writer *zip.Writer, srcPath string, zipPath string) error {
|
|
srcFile, err := os.Open(srcPath)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
srcStat, err := os.Stat(srcPath)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
defer func() {
|
|
if err := srcFile.Close(); err != nil {
|
|
panic(errors.WithStack(err))
|
|
}
|
|
}()
|
|
|
|
fileHeader := &zip.FileHeader{
|
|
Name: zipPath,
|
|
Modified: srcStat.ModTime().UTC(),
|
|
Method: zip.Deflate,
|
|
}
|
|
|
|
file, err := writer.CreateHeader(fileHeader)
|
|
if err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
if _, err = io.Copy(file, srcFile); err != nil {
|
|
return errors.WithStack(err)
|
|
}
|
|
|
|
return nil
|
|
}
|