150 lines
3.1 KiB
Go
150 lines
3.1 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.LoadAppManifest(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 {
|
||
|
r, err := os.Open(srcPath)
|
||
|
if err != nil {
|
||
|
return errors.WithStack(err)
|
||
|
}
|
||
|
|
||
|
defer func() {
|
||
|
if err := r.Close(); err != nil {
|
||
|
panic(errors.WithStack(err))
|
||
|
}
|
||
|
}()
|
||
|
|
||
|
f, err := writer.Create(zipPath)
|
||
|
if err != nil {
|
||
|
return errors.WithStack(err)
|
||
|
}
|
||
|
|
||
|
if _, err = io.Copy(f, r); err != nil {
|
||
|
return errors.WithStack(err)
|
||
|
}
|
||
|
|
||
|
return nil
|
||
|
}
|