Commits

Miki Tebeka committed 831e34a

Using zip file

Comments (0)

Files changed (4)

iface.go

-/* "interface" code written to source file. */
-package main
-
-const iface = `
-package nrsc
-
-import (
-	"bytes"
-	"fmt"
-	"html/template"
-	"io"
-	"io/ioutil"
-	"mime"
-	"net/http"
-	"path/filepath"
-	"strings"
-	"time"
-)
-
-type Resource interface {
-	Open() io.Reader
-	Size() int64
-	ModTime() time.Time
-}
-
-type resource struct {
-	size  int64
-	mtime time.Time
-	data  []byte
-}
-
-func (rsc *resource) Open() io.Reader {
-	return bytes.NewReader(rsc.data)
-}
-
-func (rsc *resource) Size() int64 {
-	return rsc.size
-}
-
-func (rsc *resource) ModTime() time.Time {
-	return rsc.mtime
-}
-
-type handler int
-
-func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
-	rsc := resources[req.URL.Path]
-	if rsc == nil {
-		http.NotFound(w, req)
-		return
-	}
-
-	mtype := mime.TypeByExtension(filepath.Ext(req.URL.Path))
-	if len(mtype) != 0 {
-		w.Header().Set("Content-Type", mtype)
-	}
-	w.Header().Set("Content-Size", fmt.Sprintf("%d", rsc.Size()))
-	w.Header().Set("Last-Modified", rsc.ModTime().UTC().Format(http.TimeFormat))
-
-	io.Copy(w, rsc.Open())
-}
-
-// Get returns the named resource (nil if not found)
-func Get(path string) Resource {
-	return resources[path]
-}
-
-// Handle register HTTP handler under prefix
-func Handle(prefix string) {
-	if !strings.HasSuffix(prefix, "/") {
-		prefix += "/"
-	}
-	var h handler
-	http.Handle(prefix, http.StripPrefix(prefix, h))
-}
-
-// LoadTemplates loads named templates from resources.
-// If the argument "t" is nil, it is created from the first resource.
-func LoadTemplates(t *template.Template, filenames ...string) (*template.Template, error) {
-	if len(filenames) == 0 {
-		// Not really a problem, but be consistent.
-		return nil, fmt.Errorf("no files named in call to LoadTemplates")
-	}
-
-	for _, filename := range filenames {
-		rsc := Get(filename)
-		if rsc == nil {
-			return nil, fmt.Errorf("can't find %s", filename)
-		}
-
-		data, err := ioutil.ReadAll(rsc.Open())
-		if err != nil {
-			return nil, err
-		}
-
-		var tmpl *template.Template
-		name := filepath.Base(filename)
-		if t == nil {
-			t = template.New(name)
-		}
-		if name == t.Name() {
-			tmpl = t
-		} else {
-			tmpl = t.New(name)
-		}
-		_, err = tmpl.Parse(string(data))
-		if err != nil {
-			return nil, err
-		}
-	}
-	return t, nil
-}
-`
-/* nrsc - package resource files inside a go source file for single exectable deploy. */
-package main
+package nrsc
 
 import (
-	"flag"
+	"archive/zip"
 	"fmt"
+	"html/template"
 	"io"
 	"io/ioutil"
+	"mime"
+	"net/http"
 	"os"
 	"path/filepath"
 	"strings"
+	"time"
 )
 
 const (
-	version = "0.1.0"
-	outdir  = "nrsc"
+	Version = "0.2.0"
 )
 
-var ignoredDirs = map[string]bool{
-	".svn": true,
-	".hg":  true,
-	".git": true,
+var ResourceMap map[string]Resource
+
+func loadMap() (map[string]Resource, error) {
+	this := os.Args[0]
+	file, err := os.Open(this)
+	if err != nil {
+		return nil, err
+	}
+
+	info, err := file.Stat()
+	if err != nil {
+		return nil, err
+	}
+	rdr, err := zip.NewReader(file, info.Size())
+	if err != nil {
+		return nil, err
+	}
+
+	entries := make(map[string]Resource)
+	for _, file := range rdr.File {
+		if file.FileInfo().IsDir() {
+			continue
+		}
+		entries[file.Name] = &resource{file}
+	}
+
+	return entries, nil
 }
 
-type File struct {
-	path string
-	info os.FileInfo
+func Initialize() error {
+	var err error
+	ResourceMap, err = loadMap()
+	return err
 }
 
-var verbose bool
+type Resource interface {
+	Name() string
+	Open() (io.Reader, error)
+	Size() int64
+	ModTime() time.Time
+}
 
-// iterfiles iterats of directory tree, returns a channel with files to process
-func iterfiles(root string) chan *File {
-	out := make(chan *File)
+type resource struct {
+	entry *zip.File
+}
 
-	walkfn := func(path string, info os.FileInfo, err error) error {
-		if info.IsDir() {
-			if ignoredDirs[strings.ToLower(info.Name())] {
-				return filepath.SkipDir
-			}
-		} else {
-			if info.Name()[0] != '.' {
-				out <- &File{path, info}
-			}
-		}
-		return nil
+func (rsc *resource) Name() string {
+	return rsc.entry.Name
+}
+
+func (rsc *resource) Open() (io.Reader, error) {
+	return rsc.entry.Open()
+}
+
+func (rsc *resource) Size() int64 {
+	return rsc.entry.FileInfo().Size()
+}
+
+func (rsc *resource) ModTime() time.Time {
+	return rsc.entry.FileInfo().ModTime()
+}
+
+type handler int
+
+func (h handler) ServeHTTP(w http.ResponseWriter, req *http.Request) {
+	rsc := ResourceMap[req.URL.Path]
+	if rsc == nil {
+		http.NotFound(w, req)
+		return
 	}
 
-	go func() {
-		filepath.Walk(root, walkfn)
-		close(out)
-	}()
+	rdr, err := rsc.Open()
+	if err != nil {
+		message := fmt.Sprintf("can't open %s - %s", rsc.Name(), err)
+		http.Error(w, message, http.StatusInternalServerError)
+	}
 
-	return out
+	mtype := mime.TypeByExtension(filepath.Ext(req.URL.Path))
+	if len(mtype) != 0 {
+		w.Header().Set("Content-Type", mtype)
+	}
+	w.Header().Set("Content-Size", fmt.Sprintf("%d", rsc.Size()))
+	w.Header().Set("Last-Modified", rsc.ModTime().UTC().Format(http.TimeFormat))
+
+	io.Copy(w, rdr)
 }
 
-// writeResource write resource code to out
-func writeResource(prefix int, file *File, out io.Writer) error {
-	data, err := ioutil.ReadFile(file.path)
-	if err != nil {
-		return err
-	}
-	path, info := file.path, file.info
-
-	key := path[prefix:]
-	fmt.Fprintf(out, "\t\"%s\": &resource{\n", key)
-	fmt.Fprintf(out, "\t\tsize: %d,\n", info.Size())
-	fmt.Fprintf(out, "\t\tmtime: time.Unix(%d, 0),\n", info.ModTime().Unix())
-	fmt.Fprintf(out, "\t\tdata: []byte{")
-	for _, b := range data {
-		fmt.Fprintf(out, "%d, ", b)
-	}
-	fmt.Fprintf(out, "\t\t},\n\t},\n")
-
-	return nil
+// Get returns the named resource (nil if not found)
+func Get(path string) Resource {
+	return ResourceMap[path]
 }
 
-// die prints error and exists the program with exit status 1
-func die(format string, args ...interface{}) {
-	message := fmt.Sprintf(format, args...)
-	fmt.Fprintf(os.Stderr, "error: %s\n", message)
-	os.Exit(1)
+// Handle register HTTP handler under prefix
+func Handle(prefix string) {
+	if !strings.HasSuffix(prefix, "/") {
+		prefix += "/"
+	}
+	var h handler
+	http.Handle(prefix, http.StripPrefix(prefix, h))
 }
 
-// info prints some information to os.Stdout if global "verbose" is true
-func info(format string, args ...interface{}) {
-	if !verbose {
-		return
-	}
-	fmt.Printf(format, args...)
-}
-
-// dirExists return true if path exists and is a directory
-func dirExists(path string) bool {
-	info, err := os.Stat(path)
-	if err == nil {
-		return info.IsDir()
+// LoadTemplates loads named templates from resources.
+// If the argument "t" is nil, it is created from the first resource.
+func LoadTemplates(t *template.Template, filenames ...string) (*template.Template, error) {
+	if len(filenames) == 0 {
+		// Not really a problem, but be consistent.
+		return nil, fmt.Errorf("no files named in call to LoadTemplates")
 	}
 
-	return false
-}
+	for _, filename := range filenames {
+		rsc := Get(filename)
+		if rsc == nil {
+			return nil, fmt.Errorf("can't find %s", filename)
+		}
 
-// writeResources writes the go code for the resources file
-func writeResources(root string, out io.Writer) error {
-	prefix := len(root)
-	if root[len(root)-1] != '/' {
-		prefix += 1
-	}
+		rdr, err := rsc.Open()
+		if err != nil {
+			return nil, fmt.Errorf("can't open %s - %s", filename, err)
+		}
+		data, err := ioutil.ReadAll(rdr)
+		if err != nil {
+			return nil, err
+		}
 
-	fmt.Fprintf(out, "package nrsc\nimport \"time\"\n")
-	fmt.Fprintf(out, "var resources = map[string]Resource {\n")
-
-	for file := range iterfiles(root) {
-		info("adding %s\n", file.path)
-		if err := writeResource(prefix, file, out); err != nil {
-			return fmt.Errorf("can't write %s - %s", file.path, err)
+		var tmpl *template.Template
+		name := filepath.Base(filename)
+		if t == nil {
+			t = template.New(name)
+		}
+		if name == t.Name() {
+			tmpl = t
+		} else {
+			tmpl = t.New(name)
+		}
+		_, err = tmpl.Parse(string(data))
+		if err != nil {
+			return nil, err
 		}
 	}
-
-	fmt.Fprintf(out, "\n}")
-
-	return nil
+	return t, nil
 }
-
-func main() {
-	var showVersion bool
-
-	flag.BoolVar(&showVersion, "version", false, "show version and exit")
-	flag.BoolVar(&verbose, "v", false, "be verbose")
-	flag.Usage = func() {
-		fmt.Fprintf(os.Stderr, "usage: %s RESOURCE_DIR\n", os.Args[0])
-		flag.PrintDefaults()
-	}
-	flag.Parse()
-
-	if showVersion {
-		fmt.Printf("nrsc %s\n", version)
-		os.Exit(0)
-	}
-
-	if flag.NArg() != 1 {
-		die("wrong number of parameters")
-	}
-
-	root := flag.Arg(0)
-
-	if !dirExists(root) {
-		die("%s is not a directory", root)
-	}
-
-	if !dirExists(outdir) {
-		if err := os.Mkdir(outdir, 0700); err != nil {
-			die("can't create nrsc directory - %s", err)
-		}
-	}
-
-	ok := false
-
-	defer func() {
-		if !ok {
-			fmt.Printf("cleaning %s\n", outdir)
-			os.RemoveAll(outdir)
-		}
-	}()
-
-	outfile := fmt.Sprintf("%s/nrsc.go", outdir)
-	info("creating %s\n", outfile)
-	err := ioutil.WriteFile(outfile, []byte(iface), 0666)
-	if err != nil {
-		die("can't create %s - %s", outfile, err)
-	}
-
-	outfile = fmt.Sprintf("%s/data.go", outdir)
-	info("creating %s\n", outfile)
-	out, err := os.Create(outfile)
-	if err != nil {
-		die("can't create %s - %s", outfile, err)
-	}
-
-	if err := writeResources(root, out); err != nil {
-		die("%s", err)
-	}
-
-	ok = true
-}
-package main
+package nrsc
 
 import (
 	"fmt"
+#!/bin/bash
+# Pack assets as zip payload in go executable
+
+# Idea from Carlos Castillo (http://bit.ly/SmYXXm)
+
+case "$1" in
+    -h | --help ) echo "usage: $(basename $0) EXECTABLE RESOURCE_DIR"; exit;;
+esac
+
+if [ $# -ne 2 ]; then
+    $0 -h
+    exit 1
+fi
+
+exe=$1
+root=$2
+
+if [ ! -f "${exe}" ]; then
+    echo "error: can't find $exe"
+    exit 1
+fi
+
+if [ ! -d "${root}" ]; then
+    echo "error: ${root} is not a directory"
+    exit 1
+fi
+
+# Exit on 1'st error
+set -e
+
+tmp="/tmp/nrsc-$(date +%s).zip"
+trap "rm -f ${tmp}" EXIT
+
+(cd "${root}" && zip -r "${tmp}" .)
+
+cat "${tmp}" >> "${exe}"
+zip -q -A "${exe}"
Tip: Filter by directory path e.g. /media app.js to search for public/media/app.js.
Tip: Use camelCasing e.g. ProjME to search for ProjectModifiedEvent.java.
Tip: Filter by extension type e.g. /repo .js to search for all .js files in the /repo directory.
Tip: Separate your search with spaces e.g. /ssh pom.xml to search for src/ssh/pom.xml.
Tip: Use ↑ and ↓ arrow keys to navigate and return to view the file.
Tip: You can also navigate files with Ctrl+j (next) and Ctrl+k (previous) and view the file with Ctrl+o.
Tip: You can also navigate files with Alt+j (next) and Alt+k (previous) and view the file with Alt+o.