edge/cmd/cli/command/app/package.go
William Petit 8ca31d05c0
All checks were successful
arcad/edge/pipeline/head This commit looks good
feat(app,manifest): validation + extendable metadatas
2023-04-11 11:05:09 +02:00

165 lines
3.4 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 valid, err := manifest.Validate(manifestMetadataValidators...); !valid {
return errors.Wrap(err, "invalid 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
}