package main import ( "errors" "fmt" "io/ioutil" "os" "path/filepath" "github.com/spf13/cobra" yaml "gopkg.in/yaml.v2" "src.lwithers.me.uk/go/htpack/cmd/htpacker/packer" "src.lwithers.me.uk/go/htpack/packed" ) var packCmd = &cobra.Command{ Use: "pack", Short: "creates a packfile from a YAML spec or set of files/dirs", Long: `When given a YAML spec file (a template for which can be generated with the "yaml" command), files will be packed exactly as per the spec. The --content-type flag cannot be used and no extra files can be specified. When given a list of files and directories to pack, the content type for each file will be automatically detected. It is possible to override the content type by specifying one or more --content-type flags. These take an argument in the form "pattern:content/type". The pattern is matched using common glob (* = wildcard), very similar to .gitignore. If the pattern contains any directory names, these must match the final components of the file to pack's path. If the pattern starts with a "/", then the full path must be matched exactly. `, RunE: func(c *cobra.Command, args []string) error { // 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 { 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 } } // parse content-type globs ctGlobList, err := c.Flags().GetStringArray("content-type") if err != nil { return err } ctGlobs, err := parseGlobs(ctGlobList) if 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 len(args) == 0 { return errors.New("need --yaml, " + "or one or more filenames") } err = PackFiles2(c, args, ctGlobs, out) } else { if len(args) != 0 { return errors.New("cannot specify files " + "when using --yaml") } if ctGlobs != nil { return errors.New("cannot specify --content-type " + "when using --yaml") } err = PackSpec(c, spec, out) } if err != nil { fmt.Fprintln(os.Stderr, err) os.Exit(1) } return nil }, } func init() { packCmd.Flags().StringP("out", "O", "", "Output filename") packCmd.MarkFlagRequired("out") packCmd.Flags().StringP("spec", "y", "", "YAML specification file (if not present, just pack files)") packCmd.Flags().StringP("chdir", "C", "", "Change to directory before searching for input files") packCmd.Flags().StringArrayP("content-type", "", nil, "Override content type for pattern, e.g. \"*.foo=bar/baz\" (like .gitignore)") } func PackFiles(c *cobra.Command, args []string, out string) error { return PackFiles2(c, args, nil, out) } func PackFiles2(c *cobra.Command, args []string, ctGlobs ctGlobList, out string) error { ftp, err := filesFromList(args) if err != nil { return err } ctGlobs.ApplyContentTypes(ftp) return doPack(ftp, out) } func PackSpec(c *cobra.Command, spec, out string) error { raw, err := ioutil.ReadFile(spec) if err != nil { return err } var ftp packer.FilesToPack if err := yaml.UnmarshalStrict(raw, &ftp); err != nil { return fmt.Errorf("parsing YAML spec %s: %v", spec, err) } return doPack(ftp, out) } func doPack(ftp packer.FilesToPack, out string) error { prog := mpbProgress(ftp) err := packer.Pack2(ftp, out, prog) prog.Complete() if err == nil { fin, err := os.Open(out) if err != nil { return err } defer fin.Close() _, dir, err := packed.Load(fin) if err != nil { return err } inspectSummary(dir) } return err }