Skip to content

Instantly share code, notes, and snippets.

@17twenty
Created August 14, 2020 02:21
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save 17twenty/c492d9d75f1656385101446b22042b0c to your computer and use it in GitHub Desktop.
Save 17twenty/c492d9d75f1656385101446b22042b0c to your computer and use it in GitHub Desktop.
Generate an embeddable zip file that can be included in your Golang project that auto extracts at runtime.
package main
import (
"fmt"
"io/ioutil"
"os"
"strings"
)
func main() {
//Empty string slice to place the byte lierals in
var dataSlice []string
if len(os.Args) != 4 {
fmt.Println("usage:")
fmt.Println(os.Args[0], "<sourcefile> <generated_file.go> <extraction_target>")
os.Exit(1)
}
//This will be the generated go file
//Note: the path is ./ because we are using the go generate command
// inside the main.go file
outfile, err := os.Create(os.Args[2])
if err != nil {
panic(err.Error())
}
defer outfile.Close()
//This is the file we will turn to []bytes
//Make sure you use the name you used to build the sample data
infile, err := ioutil.ReadFile(os.Args[1])
if err != nil {
panic(err.Error())
}
//Write the initial data to the generated go file
//package main
//var data = []byte{ EOF... so far
outfile.Write([]byte(fmt.Sprintf(`package main
//Do not edit this file, it is auto generated
import "os"
func init() {
//Create the binary file
file, err := os.Create("%s")
if err != nil {
return
}
defer file.Close()
//Write the data to the the created file
file.Write(bindata)
}
// Byte data
var (
bindata = []byte{`, os.Args[3])))
//Here we loop over each byte in the []byte from the sample
//data file.
//Take the byte literal and format it as a string
//Then append it to the empty []string dataSlice we created at
//the top of the func
//Depending on the size of infile this could take a bit
for _, b := range infile {
bString := fmt.Sprintf("%v", b)
dataSlice = append(dataSlice, bString)
}
//We join the []string together separating it with commas
//Remember we are writing a go src file so everything has to be a string
dataString := strings.Join(dataSlice, ", ")
//Write all the data to the generated file
outfile.WriteString(dataString)
//Finish off the data
outfile.Write([]byte("}\n)"))
}
package anotherpackage
import (
"archive/zip"
"fmt"
"io"
"os"
"path/filepath"
"strings"
)
// Create our archive
//go:generate zip -r /tmp/migrate-out.zip migrations
//go:generate go run ./generator/cmd/generator.go /tmp/migrate-out.zip migrations_generated.go /tmp/migrate.zip
...
func main() {
...
if files, err := unzip("/tmp/migrate.zip", "/tmp/"); err == nil {
log.Println("Zip found, unzipped")
for f := range files {
log.Println(" ", files[f])
}
}
...
}
// within the zip file (parameter 1) to an output directory (parameter 2).
func unzip(src string, dest string) ([]string, error) {
var filenames []string
r, err := zip.OpenReader(src)
if err != nil {
return filenames, err
}
defer r.Close()
for _, f := range r.File {
// Store filename/path for returning and using later on
fpath := filepath.Join(dest, f.Name)
// Check for ZipSlip. More Info: http://bit.ly/2MsjAWE
if !strings.HasPrefix(fpath, filepath.Clean(dest)+string(os.PathSeparator)) {
return filenames, fmt.Errorf("%s: illegal file path", fpath)
}
filenames = append(filenames, fpath)
if f.FileInfo().IsDir() {
// Make Folder
os.MkdirAll(fpath, os.ModePerm)
continue
}
// Make File
if err = os.MkdirAll(filepath.Dir(fpath), os.ModePerm); err != nil {
return filenames, err
}
outFile, err := os.OpenFile(fpath, os.O_WRONLY|os.O_CREATE|os.O_TRUNC, f.Mode())
if err != nil {
return filenames, err
}
rc, err := f.Open()
if err != nil {
return filenames, err
}
_, err = io.Copy(outFile, rc)
// Close the file without defer to close before next iteration of loop
outFile.Close()
rc.Close()
if err != nil {
return filenames, err
}
}
return filenames, nil
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment