cmd/htpacker: lots more WIP
This commit is contained in:
parent
feb25bf276
commit
84ea7c3673
|
@ -50,14 +50,20 @@ func Inspect(filename string) error {
|
||||||
fmt.Printf(" • %s\n"+
|
fmt.Printf(" • %s\n"+
|
||||||
" · Etag: %s\n"+
|
" · Etag: %s\n"+
|
||||||
" · Content type: %s\n"+
|
" · Content type: %s\n"+
|
||||||
" · Uncompressed: %s (offset %d)\n"+
|
" · Uncompressed: %s (offset %d)\n",
|
||||||
" · Gzipped: %s (offset %d)\n"+
|
|
||||||
" · Brotli: %s (offset %d)\n",
|
|
||||||
path, info.Etag, info.ContentType,
|
path, info.Etag, info.ContentType,
|
||||||
printSize(info.Uncompressed.Length), info.Uncompressed.Offset,
|
printSize(info.Uncompressed.Length),
|
||||||
printSize(info.Gzip.Length), info.Gzip.Offset,
|
info.Uncompressed.Offset)
|
||||||
printSize(info.Brotli.Length), info.Brotli.Offset,
|
|
||||||
)
|
if info.Gzip != nil {
|
||||||
|
fmt.Printf(" · Gzipped: %s (offset %d)\n",
|
||||||
|
printSize(info.Gzip.Length), info.Gzip.Offset)
|
||||||
|
}
|
||||||
|
|
||||||
|
if info.Brotli != nil {
|
||||||
|
fmt.Printf(" · Brotli: %s (offset %d)\n",
|
||||||
|
printSize(info.Brotli.Length), info.Brotli.Offset)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return err
|
return err
|
||||||
|
|
|
@ -18,7 +18,7 @@ arguments.`,
|
||||||
|
|
||||||
func main() {
|
func main() {
|
||||||
rootCmd.AddCommand(packCmd)
|
rootCmd.AddCommand(packCmd)
|
||||||
//rootCmd.AddCommand(yamlCmd)
|
rootCmd.AddCommand(yamlCmd)
|
||||||
rootCmd.AddCommand(inspectCmd)
|
rootCmd.AddCommand(inspectCmd)
|
||||||
|
|
||||||
if err := rootCmd.Execute(); err != nil {
|
if err := rootCmd.Execute(); err != nil {
|
||||||
|
|
|
@ -5,6 +5,7 @@ import (
|
||||||
"fmt"
|
"fmt"
|
||||||
"io/ioutil"
|
"io/ioutil"
|
||||||
"os"
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
|
||||||
"github.com/lwithers/htpack/packer"
|
"github.com/lwithers/htpack/packer"
|
||||||
"github.com/spf13/cobra"
|
"github.com/spf13/cobra"
|
||||||
|
@ -15,23 +16,54 @@ var packCmd = &cobra.Command{
|
||||||
Use: "pack",
|
Use: "pack",
|
||||||
Short: "creates a packfile from a YAML spec or set of files/dirs",
|
Short: "creates a packfile from a YAML spec or set of files/dirs",
|
||||||
RunE: func(c *cobra.Command, args []string) error {
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
spec, err := c.Flags().GetString("spec")
|
// convert "out" to an absolute path, so that it will still
|
||||||
|
// work after chdir
|
||||||
|
out, err := c.Flags().GetString("out")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out, err = filepath.Abs(out)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// if "spec" is present, convert to an absolute path
|
||||||
|
spec, err := c.Flags().GetString("spec")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if spec != "" {
|
||||||
|
spec, err = filepath.Abs(spec)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// chdir if required
|
||||||
|
chdir, err := c.Flags().GetString("chdir")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if chdir != "" {
|
||||||
|
if err = os.Chdir(chdir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// if "spec" is not present, then we expect a list of input
|
||||||
|
// files, and we'll build a spec from them
|
||||||
if spec == "" {
|
if spec == "" {
|
||||||
if len(args) == 0 {
|
if len(args) == 0 {
|
||||||
return errors.New("need --yaml, " +
|
return errors.New("need --yaml, " +
|
||||||
"or one or more filenames")
|
"or one or more filenames")
|
||||||
}
|
}
|
||||||
err = PackFiles(c, args)
|
err = PackFiles(c, args, out)
|
||||||
} else {
|
} else {
|
||||||
if len(args) != 0 {
|
if len(args) != 0 {
|
||||||
return errors.New("cannot specify files " +
|
return errors.New("cannot specify files " +
|
||||||
"when using --yaml")
|
"when using --yaml")
|
||||||
}
|
}
|
||||||
err = PackSpec(c, spec)
|
err = PackSpec(c, spec, out)
|
||||||
}
|
}
|
||||||
if err != nil {
|
if err != nil {
|
||||||
fmt.Fprintln(os.Stderr, err)
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
@ -52,12 +84,15 @@ func init() {
|
||||||
"Change to directory before searching for input files")
|
"Change to directory before searching for input files")
|
||||||
}
|
}
|
||||||
|
|
||||||
func PackFiles(c *cobra.Command, args []string) error {
|
func PackFiles(c *cobra.Command, args []string, out string) error {
|
||||||
// TODO
|
ftp, err := filesFromList(args)
|
||||||
return errors.New("not implemented yet")
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
return packer.Pack(ftp, out)
|
||||||
}
|
}
|
||||||
|
|
||||||
func PackSpec(c *cobra.Command, spec string) error {
|
func PackSpec(c *cobra.Command, spec, out string) error {
|
||||||
raw, err := ioutil.ReadFile(spec)
|
raw, err := ioutil.ReadFile(spec)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
|
@ -68,8 +103,5 @@ func PackSpec(c *cobra.Command, spec string) error {
|
||||||
return fmt.Errorf("parsing YAML spec %s: %v", spec, err)
|
return fmt.Errorf("parsing YAML spec %s: %v", spec, err)
|
||||||
}
|
}
|
||||||
|
|
||||||
// TODO: chdir
|
|
||||||
|
|
||||||
out, _ := c.Flags().GetString("out")
|
|
||||||
return packer.Pack(ftp, out)
|
return packer.Pack(ftp, out)
|
||||||
}
|
}
|
||||||
|
|
|
@ -0,0 +1,172 @@
|
||||||
|
package main
|
||||||
|
|
||||||
|
import (
|
||||||
|
"errors"
|
||||||
|
"fmt"
|
||||||
|
"io/ioutil"
|
||||||
|
"net/http"
|
||||||
|
"os"
|
||||||
|
"path/filepath"
|
||||||
|
"strings"
|
||||||
|
|
||||||
|
"github.com/lwithers/htpack/packer"
|
||||||
|
"github.com/spf13/cobra"
|
||||||
|
yaml "gopkg.in/yaml.v2"
|
||||||
|
)
|
||||||
|
|
||||||
|
var yamlCmd = &cobra.Command{
|
||||||
|
Use: "yaml",
|
||||||
|
Short: "Build YAML spec from list of files/dirs",
|
||||||
|
Long: `Generates a YAML specification from a list of files and directories.
|
||||||
|
The specification is suitable for passing to pack.
|
||||||
|
|
||||||
|
File names will be mapped as follows:
|
||||||
|
• if you specify a file, it will appear be served as "/filename";
|
||||||
|
• if you specify a directory, its contents will be merged into "/", such that a
|
||||||
|
directory with contents "a", "b", and "c/d" will cause entries "/a", "/b" and
|
||||||
|
"/c/d" to be served.
|
||||||
|
`,
|
||||||
|
RunE: func(c *cobra.Command, args []string) error {
|
||||||
|
if len(args) == 0 {
|
||||||
|
return errors.New("must specify one or more files/directories")
|
||||||
|
}
|
||||||
|
|
||||||
|
// convert "out" to absolute path, in case we need to chdir
|
||||||
|
out, err := c.Flags().GetString("out")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
out, err = filepath.Abs(out)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
// chdir if required
|
||||||
|
chdir, err := c.Flags().GetString("chdir")
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
if chdir != "" {
|
||||||
|
if err = os.Chdir(chdir); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if err := MakeYaml(args, out); err != nil {
|
||||||
|
fmt.Fprintln(os.Stderr, err)
|
||||||
|
os.Exit(1)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
},
|
||||||
|
}
|
||||||
|
|
||||||
|
func init() {
|
||||||
|
yamlCmd.Flags().StringP("out", "O", "",
|
||||||
|
"Output filename")
|
||||||
|
yamlCmd.MarkFlagRequired("out")
|
||||||
|
yamlCmd.Flags().StringP("chdir", "C", "",
|
||||||
|
"Change to directory before searching for input files")
|
||||||
|
}
|
||||||
|
|
||||||
|
func MakeYaml(args []string, out string) error {
|
||||||
|
ftp, err := filesFromList(args)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
raw, err := yaml.Marshal(ftp)
|
||||||
|
if err != nil {
|
||||||
|
return fmt.Errorf("failed to marshal %T to YAML: %v", ftp, err)
|
||||||
|
}
|
||||||
|
|
||||||
|
return ioutil.WriteFile(out, raw, 0666)
|
||||||
|
}
|
||||||
|
|
||||||
|
func filesFromList(args []string) (packer.FilesToPack, error) {
|
||||||
|
ftp := make(packer.FilesToPack)
|
||||||
|
|
||||||
|
// NB: we don't use filepath.Walk since:
|
||||||
|
// (a) we don't care about lexical order; just do it quick
|
||||||
|
// (b) we want to dereference symlinks
|
||||||
|
for _, arg := range args {
|
||||||
|
if err := filesFromListR(arg, arg, ftp); err != nil {
|
||||||
|
return nil, err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return ftp, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func filesFromListR(prefix, arg string, ftp packer.FilesToPack) error {
|
||||||
|
f, err := os.Open(arg)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
defer f.Close()
|
||||||
|
|
||||||
|
fi, err := f.Stat()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
|
||||||
|
switch {
|
||||||
|
case fi.Mode().IsDir():
|
||||||
|
// readdir
|
||||||
|
fnames, err := f.Readdirnames(0) // 0 ⇒ everything
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
for _, fname := range fnames {
|
||||||
|
fullname := filepath.Join(arg, fname)
|
||||||
|
if err = filesFromListR(prefix, fullname, ftp); err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
case fi.Mode().IsRegular():
|
||||||
|
// sniff content type
|
||||||
|
buf := make([]byte, 512)
|
||||||
|
n, err := f.Read(buf)
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
buf = buf[:n]
|
||||||
|
ctype := http.DetectContentType(buf)
|
||||||
|
|
||||||
|
// augmented rules for JS / CSS / etc.
|
||||||
|
switch {
|
||||||
|
case strings.HasPrefix(ctype, "text/plain"):
|
||||||
|
switch filepath.Ext(arg) {
|
||||||
|
case ".css":
|
||||||
|
ctype = "text/css"
|
||||||
|
case ".js":
|
||||||
|
ctype = "application/javascript"
|
||||||
|
case ".json":
|
||||||
|
ctype = "application/json"
|
||||||
|
}
|
||||||
|
|
||||||
|
case strings.HasPrefix(ctype, "text/xml"):
|
||||||
|
switch filepath.Ext(arg) {
|
||||||
|
case ".svg":
|
||||||
|
ctype = "image/svg+xml"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// pack
|
||||||
|
srvName := strings.TrimPrefix(arg, prefix)
|
||||||
|
if srvName == "" {
|
||||||
|
srvName = filepath.Base(arg)
|
||||||
|
}
|
||||||
|
if !strings.HasPrefix(srvName, "/") {
|
||||||
|
srvName = "/" + srvName
|
||||||
|
}
|
||||||
|
ftp[srvName] = packer.FileToPack{
|
||||||
|
Filename: arg,
|
||||||
|
ContentType: ctype,
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
|
||||||
|
default:
|
||||||
|
return fmt.Errorf("%s: not file/dir (mode %x)", arg, fi.Mode())
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue