github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/compute/pack.go (about) 1 package compute 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 "path/filepath" 8 9 "github.com/mholt/archiver/v3" 10 11 "github.com/fastly/cli/pkg/argparser" 12 fsterr "github.com/fastly/cli/pkg/errors" 13 "github.com/fastly/cli/pkg/filesystem" 14 "github.com/fastly/cli/pkg/global" 15 "github.com/fastly/cli/pkg/manifest" 16 "github.com/fastly/cli/pkg/text" 17 ) 18 19 // PackCommand takes a .wasm and builds the required tar/gzip package ready to be uploaded. 20 type PackCommand struct { 21 argparser.Base 22 wasmBinary string 23 } 24 25 // NewPackCommand returns a usable command registered under the parent. 26 func NewPackCommand(parent argparser.Registerer, g *global.Data) *PackCommand { 27 var c PackCommand 28 c.Globals = g 29 c.CmdClause = parent.Command("pack", "Package a pre-compiled Wasm binary for a Fastly Compute service") 30 c.CmdClause.Flag("wasm-binary", "Path to a pre-compiled Wasm binary").Short('w').Required().StringVar(&c.wasmBinary) 31 32 return &c 33 } 34 35 // Exec implements the command interface. 36 // 37 // NOTE: The bin/manifest is placed in a 'package' folder within the tar.gz. 38 func (c *PackCommand) Exec(_ io.Reader, out io.Writer) (err error) { 39 spinner, err := text.NewSpinner(out) 40 if err != nil { 41 return err 42 } 43 44 defer func(errLog fsterr.LogInterface) { 45 _ = os.RemoveAll("pkg/package") 46 if err != nil { 47 errLog.Add(err) 48 } 49 }(c.Globals.ErrLog) 50 51 if err = c.Globals.Manifest.File.ReadError(); err != nil { 52 return err 53 } 54 55 bin := "pkg/package/bin/main.wasm" 56 bindir := filepath.Dir(bin) 57 58 err = filesystem.MakeDirectoryIfNotExists(bindir) 59 if err != nil { 60 c.Globals.ErrLog.AddWithContext(err, map[string]any{ 61 "Wasm directory (relative)": bindir, 62 }) 63 return err 64 } 65 66 src, err := filepath.Abs(c.wasmBinary) 67 if err != nil { 68 c.Globals.ErrLog.AddWithContext(err, map[string]any{ 69 "Path (absolute)": src, 70 }) 71 return err 72 } 73 74 dst, err := filepath.Abs(bin) 75 if err != nil { 76 c.Globals.ErrLog.AddWithContext(err, map[string]any{ 77 "Wasm destination (relative)": bin, 78 }) 79 return err 80 } 81 82 err = spinner.Process("Copying wasm binary", func(_ *text.SpinnerWrapper) error { 83 if err := filesystem.CopyFile(src, dst); err != nil { 84 c.Globals.ErrLog.AddWithContext(err, map[string]any{ 85 "Path (absolute)": src, 86 "Wasm destination (absolute)": dst, 87 }) 88 return fmt.Errorf("error copying wasm binary to '%s': %w", dst, err) 89 } 90 91 if !filesystem.FileExists(bin) { 92 return fsterr.RemediationError{ 93 Inner: fmt.Errorf("no wasm binary found"), 94 Remediation: "Run `fastly compute pack --path </path/to/wasm/binary>` to copy your wasm binary to the required location", 95 } 96 } 97 return nil 98 }) 99 if err != nil { 100 return err 101 } 102 103 err = spinner.Process("Copying manifest", func(_ *text.SpinnerWrapper) error { 104 src = manifest.Filename 105 dst = fmt.Sprintf("pkg/package/%s", manifest.Filename) 106 if err := filesystem.CopyFile(src, dst); err != nil { 107 c.Globals.ErrLog.AddWithContext(err, map[string]any{ 108 "Manifest (destination)": dst, 109 "Manifest (source)": src, 110 }) 111 return fmt.Errorf("error copying manifest to '%s': %w", dst, err) 112 } 113 return nil 114 }) 115 if err != nil { 116 return err 117 } 118 119 return spinner.Process("Creating package.tar.gz file", func(_ *text.SpinnerWrapper) error { 120 tar := archiver.NewTarGz() 121 tar.OverwriteExisting = true 122 { 123 dir := "pkg/package" 124 src := []string{dir} 125 dst := fmt.Sprintf("%s.tar.gz", dir) 126 if err = tar.Archive(src, dst); err != nil { 127 c.Globals.ErrLog.AddWithContext(err, map[string]any{ 128 "Tar source": dir, 129 "Tar destination": dst, 130 }) 131 return err 132 } 133 } 134 return nil 135 }) 136 }