pkg/vproj/util.go
package vproj
/**
* SPDX-License-Identifier: Apache-2.0
* Copyright 2020 vorteil.io Pty Ltd
*/
import (
"archive/tar"
"bytes"
"io"
"io/ioutil"
"os"
"path"
"strings"
"time"
"github.com/sisatech/toml"
"github.com/vorteil/vorteil/pkg/vcfg"
"github.com/vorteil/vorteil/pkg/vio"
"github.com/vorteil/vorteil/pkg/vpkg"
)
func vcfgFromPkg(pkg vpkg.Reader) (*vcfg.VCFG, vio.File, error) {
var v *vcfg.VCFG
var err error
cfg := pkg.VCFG()
defer func() {
if err != nil {
cfg.Close()
}
}()
v, err = vcfg.LoadFile(cfg)
if err != nil {
return nil, nil, err
}
return v, cfg, nil
}
func iconFromPkg(pkg vpkg.Reader) (vio.File, *os.File, error) {
var err error
var iconFile *os.File
ico := pkg.Icon()
defer func() {
if err != nil {
ico.Close()
}
}()
iconFile, err = ioutil.TempFile(os.TempDir(), UnpackTempPattern)
if err != nil {
return nil, nil, err
}
defer func() {
if err != nil {
iconFile.Close()
os.Remove(iconFile.Name())
}
}()
_, err = io.Copy(iconFile, ico)
if err != nil {
return nil, nil, err
}
return ico, iconFile, nil
}
func walkFilesystem(pkg vpkg.Reader, files []string, tw *tar.Writer, vprjIncluded bool, vprj ProjectData) error {
return pkg.FS().Walk(func(path string, f vio.File) error {
defer f.Close()
defer io.Copy(ioutil.Discard, f)
link, err := handleSymlink(f)
if err != nil {
return err
}
var mw io.Writer
header, err := handleFile(&handleFileArgs{mw: mw, f: f, path: path, link: link, vprjIncluded: vprjIncluded, vprj: vprj, tw: tw})
if err != nil {
return err
}
files = append(files, header.Name)
return nil
})
}
// if link is not empty, file is a symlink
func handleSymlink(f vio.File) (string, error) {
var link string
if f.IsSymlink() {
data, err := ioutil.ReadAll(f)
if err != nil {
return link, err
}
link = string(data)
}
return link, nil
}
type handleFileArgs struct {
mw io.Writer
f vio.File
path string
link string
vprjIncluded bool
vprj ProjectData
tw *tar.Writer
}
func handleFile(args *handleFileArgs) (*tar.Header, error) {
header, err := tar.FileInfoHeader(vio.Info(args.f), args.link)
if err != nil {
return nil, err
}
var tf *os.File
var x bool
header.Name = strings.TrimPrefix(args.path, "./")
if header.Name == FileName && !args.vprjIncluded {
tf, err = ioutil.TempFile(os.TempDir(), UnpackTempPattern)
if err != nil {
return nil, err
}
defer os.Remove(tf.Name())
defer tf.Close()
x = true
args.mw = io.MultiWriter(tf, args.tw)
args.vprjIncluded = true
} else {
args.mw = args.tw
}
err = args.tw.WriteHeader(header)
if err != nil {
return nil, err
}
if !args.f.IsDir() {
_, err = io.Copy(args.mw, args.f)
if err != nil {
return nil, err
}
}
if x {
err = readAndUnmarshalVprj(tf.Name(), &args.vprj)
if err != nil {
return nil, err
}
}
return header, nil
}
func readAndUnmarshalVprj(name string, vprj *ProjectData) error {
b, err := ioutil.ReadFile(name)
if err != nil {
return err
}
err = toml.Unmarshal(b, vprj)
if err != nil {
return err
}
return nil
}
func handleSplitVCFGs(v *vcfg.VCFG, tw *tar.Writer) error {
cfgTmp, err := vcfgSansInfo(v, "")
if err != nil {
return err
}
defer cfgTmp.Close()
err = writeFileToTar(tw, cfgTmp)
if err != nil {
return err
}
// write info field as readme.vcfg
cfgTmp, err = vcfgInfo(v)
if err != nil {
return err
}
err = writeFileToTar(tw, cfgTmp)
return err
}
func writeFileToTar(tw *tar.Writer, f vio.File) error {
cfgHeader, err := tar.FileInfoHeader(vio.Info(f), "")
if err != nil {
return err
}
err = tw.WriteHeader(cfgHeader)
if err != nil {
return err
}
_, err = io.Copy(tw, f)
if err != nil {
return err
}
f.Close()
return nil
}
func handleIcon(iconName string, ico vio.File, tw *tar.Writer, iconFile *os.File) error {
if ico.Size() == 0 {
return nil
}
_, err := iconFile.Seek(0, 0)
if err != nil {
return err
}
icoTmp := vio.CustomFile(vio.CustomFileArgs{
ModTime: ico.ModTime(),
Name: iconName,
ReadCloser: iconFile,
Size: ico.Size(),
})
defer icoTmp.Close()
icoHeader, err := tar.FileInfoHeader(vio.Info(icoTmp), "")
if err != nil {
return err
}
err = tw.WriteHeader(icoHeader)
if err != nil {
return err
}
_, err = io.Copy(tw, icoTmp)
if err != nil {
return err
}
return nil
}
type defaultIconAndConfigArgs struct {
v *vcfg.VCFG
vprj ProjectData
tw *tar.Writer
ico vio.File
iconFile *os.File
iconName string
}
func defaultPNGAndVCFG(args *defaultIconAndConfigArgs) error {
err := handleSplitVCFGs(args.v, args.tw)
if err != nil {
return err
}
err = handleIcon(args.iconName, args.ico, args.tw, args.iconFile)
if err != nil {
return err
}
for i := range args.vprj.Targets {
if args.vprj.Targets[i].Name == "default" {
args.vprj.Targets[i].VCFGs = append(args.vprj.Targets[i].VCFGs, "readme.vcfg")
}
}
err = handleVprjTargetFile(args.vprj, args.tw)
if err != nil {
return err
}
return nil
}
func handleVprjTargetFile(vprj ProjectData, tw *tar.Writer) error {
b, err := vprj.Marshal()
if err != nil {
return err
}
vprjTmp := vio.CustomFile(vio.CustomFileArgs{
ModTime: time.Now(),
Name: FileName,
ReadCloser: ioutil.NopCloser(bytes.NewReader(b)),
Size: len(b),
})
defer vprjTmp.Close()
err = writeFileToTar(tw, vprjTmp)
if err != nil {
return err
}
return nil
}
func tarFromVorteilProject(v *vcfg.VCFG, vprj ProjectData, files []string, tw *tar.Writer) error {
for _, x := range vprj.Targets[0].VCFGs {
for _, s := range files {
err := handleVprjFile(v, vprj, tw, s, x)
if err != nil {
return err
}
}
}
return nil
}
func handleVprjFile(v *vcfg.VCFG, vprj ProjectData, tw *tar.Writer, s, x string) error {
if s == FileName {
for i := range vprj.Targets {
if vprj.Targets[i].Name == strings.TrimSuffix(x, path.Ext(x)) {
vprj.Targets[i].VCFGs = append(vprj.Targets[i].VCFGs, "readme.vcfg")
}
}
err := handleVprjTargetFile(vprj, tw)
if err != nil {
return err
}
}
if s == x {
return nil
}
// HandleSplitVCFGs ...
err := handleSplitVCFGs(v, tw)
if err != nil {
return err
}
return nil
}