diff --git a/internal/archive/archive.go b/internal/archive/archive.go new file mode 100644 index 0000000..b173dda --- /dev/null +++ b/internal/archive/archive.go @@ -0,0 +1,103 @@ +package archive + +import ( + "archive/tar" + "compress/gzip" + "io" + "os" + "strings" + + makeshift "git.towk2.me/towk/makeshift/pkg" +) + +func Create(filenames []string, buf io.Writer, hooks []makeshift.Hook) error { + // Create new Writers for gzip and tar + // These writers are chained. Writing to the tar writer will + // write to the gzip writer which in turn will write to + // the "buf" writer + gw := gzip.NewWriter(buf) + defer gw.Close() + tw := tar.NewWriter(gw) + defer tw.Close() + + // Iterate over files and add them to the tar archive + for _, filename := range filenames { + err := addToArchive(tw, filename, hooks) + if err != nil { + return err + } + } + + return nil +} + +func Expand(path string) error { + return nil +} + +func addToArchive(tw *tar.Writer, filename string, hooks []makeshift.Hook) error { + var ( + hook makeshift.Hook + file *os.File + data any + err error + ) + // open file to write to archive + file, err = os.Open(filename) + if err != nil { + return err + } + defer file.Close() + + // run pre-hooks to modify the contents of the file + // before archiving using plugins + for _, hook := range hooks { + err = hook.Run() + if err != nil { + return err + } + } + + // get FileInfo for file size, mode, etc. + info, err := file.Stat() + if err != nil { + return err + } + + // skip file if it's a directory + if info.IsDir() { + return nil + } + + // create a tar Header from the FileInfo data + header, err := tar.FileInfoHeader(info, info.Name()) + if err != nil { + return err + } + + // use full path as name (FileInfoHeader only takes the basename) to + // preserve directory structure + // see for more info: https://golang.org/src/archive/tar/common.go?#L626 + header.Name = filename + + // Write file header to the tar archive + err = tw.WriteHeader(header) + if err != nil { + return err + } + + // take the contents from the last hook and update files + hook = hooks[len(hooks)-1] + data, err = hook.Data.Get("out") + if err != nil { + return err + } + + // copy file content to tar archive + _, err = io.Copy(tw, strings.NewReader(data.(string))) + if err != nil { + return err + } + + return nil +}