github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/compute/hashsum.go (about) 1 package compute 2 3 import ( 4 "crypto/sha512" 5 "errors" 6 "fmt" 7 "io" 8 "os" 9 "path/filepath" 10 11 "github.com/kennygrant/sanitize" 12 13 "github.com/fastly/cli/pkg/argparser" 14 fsterr "github.com/fastly/cli/pkg/errors" 15 "github.com/fastly/cli/pkg/global" 16 "github.com/fastly/cli/pkg/manifest" 17 "github.com/fastly/cli/pkg/text" 18 ) 19 20 // HashsumCommand produces a deployable artifact from files on the local disk. 21 type HashsumCommand struct { 22 argparser.Base 23 24 // Build fields 25 dir argparser.OptionalString 26 env argparser.OptionalString 27 includeSrc argparser.OptionalBool 28 lang argparser.OptionalString 29 metadataDisable argparser.OptionalBool 30 metadataFilterEnvVars argparser.OptionalString 31 metadataShow argparser.OptionalBool 32 packageName argparser.OptionalString 33 timeout argparser.OptionalInt 34 35 buildCmd *BuildCommand 36 PackagePath string 37 SkipBuild bool 38 } 39 40 // NewHashsumCommand returns a usable command registered under the parent. 41 // Deprecated: Use NewHashFilesCommand instead. 42 func NewHashsumCommand(parent argparser.Registerer, g *global.Data, build *BuildCommand) *HashsumCommand { 43 var c HashsumCommand 44 c.buildCmd = build 45 c.Globals = g 46 c.CmdClause = parent.Command("hashsum", "Generate a SHA512 digest from a Compute package").Hidden() 47 c.CmdClause.Flag("dir", "Project directory to build (default: current directory)").Short('C').Action(c.dir.Set).StringVar(&c.dir.Value) 48 c.CmdClause.Flag("env", "The manifest environment config to use (e.g. 'stage' will attempt to read 'fastly.stage.toml')").Action(c.env.Set).StringVar(&c.env.Value) 49 c.CmdClause.Flag("include-source", "Include source code in built package").Action(c.includeSrc.Set).BoolVar(&c.includeSrc.Value) 50 c.CmdClause.Flag("language", "Language type").Action(c.lang.Set).StringVar(&c.lang.Value) 51 c.CmdClause.Flag("metadata-disable", "Disable Wasm binary metadata annotations").Action(c.metadataDisable.Set).BoolVar(&c.metadataDisable.Value) 52 c.CmdClause.Flag("metadata-filter-envvars", "Redact specified environment variables from [scripts.env_vars] using comma-separated list").Action(c.metadataFilterEnvVars.Set).StringVar(&c.metadataFilterEnvVars.Value) 53 c.CmdClause.Flag("metadata-show", "Inspect the Wasm binary metadata").Action(c.metadataShow.Set).BoolVar(&c.metadataShow.Value) 54 c.CmdClause.Flag("package", "Path to a package tar.gz").Short('p').StringVar(&c.PackagePath) 55 c.CmdClause.Flag("package-name", "Package name").Action(c.packageName.Set).StringVar(&c.packageName.Value) 56 c.CmdClause.Flag("skip-build", "Skip the build step").BoolVar(&c.SkipBuild) 57 c.CmdClause.Flag("timeout", "Timeout, in seconds, for the build compilation step").Action(c.timeout.Set).IntVar(&c.timeout.Value) 58 59 return &c 60 } 61 62 // Exec implements the command interface. 63 func (c *HashsumCommand) Exec(in io.Reader, out io.Writer) (err error) { 64 if !c.Globals.Flags.Quiet { 65 // FIXME: Remove `hashsum` subcommand before v11.0.0 is released. 66 text.Warning(out, "This command is deprecated. Use `fastly compute hash-files` instead.") 67 } 68 69 // No point in building a package if the user provides a package path. 70 if !c.SkipBuild && c.PackagePath == "" { 71 err = c.Build(in, out) 72 if err != nil { 73 return err 74 } 75 if !c.Globals.Flags.Quiet { 76 text.Break(out) 77 } 78 } 79 80 pkgPath := c.PackagePath 81 if pkgPath == "" { 82 manifestFilename := EnvironmentManifest(c.env.Value) 83 wd, err := os.Getwd() 84 if err != nil { 85 return fmt.Errorf("failed to get current working directory: %w", err) 86 } 87 defer func() { 88 _ = os.Chdir(wd) 89 }() 90 manifestPath := filepath.Join(wd, manifestFilename) 91 92 projectDir, err := ChangeProjectDirectory(c.dir.Value) 93 if err != nil { 94 return err 95 } 96 if projectDir != "" { 97 if c.Globals.Verbose() { 98 text.Info(out, ProjectDirMsg, projectDir) 99 } 100 manifestPath = filepath.Join(projectDir, manifestFilename) 101 } 102 103 if projectDir != "" || c.env.WasSet { 104 err = c.Globals.Manifest.File.Read(manifestPath) 105 } else { 106 err = c.Globals.Manifest.File.ReadError() 107 } 108 if err != nil { 109 if errors.Is(err, os.ErrNotExist) { 110 err = fsterr.ErrReadingManifest 111 } 112 c.Globals.ErrLog.Add(err) 113 return err 114 } 115 116 projectName, source := c.Globals.Manifest.Name() 117 if source == manifest.SourceUndefined { 118 return fsterr.ErrReadingManifest 119 } 120 pkgPath = filepath.Join(projectDir, "pkg", fmt.Sprintf("%s.tar.gz", sanitize.BaseName(projectName))) 121 } 122 123 err = validatePackage(pkgPath) 124 if err != nil { 125 var skipBuildMsg string 126 if c.SkipBuild { 127 skipBuildMsg = " avoid using --skip-build, or" 128 } 129 return fsterr.RemediationError{ 130 Inner: fmt.Errorf("failed to validate package: %w", err), 131 Remediation: fmt.Sprintf("Run `fastly compute build` to produce a Compute package, alternatively%s use the --package flag to reference a package outside of the current project.", skipBuildMsg), 132 } 133 } 134 135 hashSum, err := getHashSum(pkgPath) 136 if err != nil { 137 return err 138 } 139 140 text.Output(out, hashSum) 141 return nil 142 } 143 144 // Build constructs and executes the build logic. 145 func (c *HashsumCommand) Build(in io.Reader, out io.Writer) error { 146 output := out 147 if !c.Globals.Verbose() && !c.metadataShow.WasSet { 148 output = io.Discard 149 } else { 150 text.Break(out) 151 } 152 if c.dir.WasSet { 153 c.buildCmd.Flags.Dir = c.dir.Value 154 } 155 if c.env.WasSet { 156 c.buildCmd.Flags.Env = c.env.Value 157 } 158 if c.includeSrc.WasSet { 159 c.buildCmd.Flags.IncludeSrc = c.includeSrc.Value 160 } 161 if c.lang.WasSet { 162 c.buildCmd.Flags.Lang = c.lang.Value 163 } 164 if c.packageName.WasSet { 165 c.buildCmd.Flags.PackageName = c.packageName.Value 166 } 167 if c.timeout.WasSet { 168 c.buildCmd.Flags.Timeout = c.timeout.Value 169 } 170 if c.metadataDisable.WasSet { 171 c.buildCmd.MetadataDisable = c.metadataDisable.Value 172 } 173 if c.metadataFilterEnvVars.WasSet { 174 c.buildCmd.MetadataFilterEnvVars = c.metadataFilterEnvVars.Value 175 } 176 if c.metadataShow.WasSet { 177 c.buildCmd.MetadataShow = c.metadataShow.Value 178 } 179 return c.buildCmd.Exec(in, output) 180 } 181 182 // getHashSum returns a hash of the package. 183 func getHashSum(pkg string) (string, error) { 184 // gosec flagged this: 185 // G304 (CWE-22): Potential file inclusion via variable 186 // Disabling as we trust the source of the filepath variable. 187 /* #nosec */ 188 f, err := os.Open(pkg) 189 if err != nil { 190 return "", err 191 } 192 193 h := sha512.New() 194 if _, err := io.Copy(h, f); err != nil { 195 _ = f.Close() 196 return "", err 197 } 198 199 if err = f.Close(); err != nil { 200 return "", err 201 } 202 203 return fmt.Sprintf("%x", h.Sum(nil)), nil 204 }