cmd/htpacker: lots more WIP

This commit is contained in:
Laurence Withers 2019-03-29 09:18:07 +00:00
parent feb25bf276
commit 84ea7c3673
4 changed files with 228 additions and 18 deletions

View File

@ -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

View File

@ -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 {

View File

@ -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)
} }

172
cmd/htpacker/yaml.go Normal file
View File

@ -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())
}
}