github.com/fastly/cli@v1.7.2-0.20240304164155-9d0f1d77c3bf/pkg/commands/compute/publish.go (about) 1 package compute 2 3 import ( 4 "fmt" 5 "io" 6 "os" 7 8 "github.com/fastly/cli/pkg/argparser" 9 "github.com/fastly/cli/pkg/global" 10 "github.com/fastly/cli/pkg/text" 11 ) 12 13 // PublishCommand produces and deploys an artifact from files on the local disk. 14 type PublishCommand struct { 15 argparser.Base 16 build *BuildCommand 17 deploy *DeployCommand 18 19 // Build fields 20 dir argparser.OptionalString 21 includeSrc argparser.OptionalBool 22 lang argparser.OptionalString 23 metadataDisable argparser.OptionalBool 24 metadataFilterEnvVars argparser.OptionalString 25 metadataShow argparser.OptionalBool 26 packageName argparser.OptionalString 27 timeout argparser.OptionalInt 28 29 // Deploy fields 30 comment argparser.OptionalString 31 domain argparser.OptionalString 32 env argparser.OptionalString 33 pkg argparser.OptionalString 34 serviceName argparser.OptionalServiceNameID 35 serviceVersion argparser.OptionalServiceVersion 36 statusCheckCode int 37 statusCheckOff bool 38 statusCheckPath string 39 statusCheckTimeout int 40 41 // Publish private fields 42 projectDir string 43 } 44 45 // NewPublishCommand returns a usable command registered under the parent. 46 func NewPublishCommand(parent argparser.Registerer, g *global.Data, build *BuildCommand, deploy *DeployCommand) *PublishCommand { 47 var c PublishCommand 48 c.Globals = g 49 c.build = build 50 c.deploy = deploy 51 c.CmdClause = parent.Command("publish", "Build and deploy a Compute package to a Fastly service") 52 53 c.CmdClause.Flag("comment", "Human-readable comment").Action(c.comment.Set).StringVar(&c.comment.Value) 54 c.CmdClause.Flag("dir", "Project directory to build (default: current directory)").Short('C').Action(c.dir.Set).StringVar(&c.dir.Value) 55 c.CmdClause.Flag("domain", "The name of the domain associated to the package").Action(c.domain.Set).StringVar(&c.domain.Value) 56 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) 57 c.CmdClause.Flag("include-source", "Include source code in built package").Action(c.includeSrc.Set).BoolVar(&c.includeSrc.Value) 58 c.CmdClause.Flag("language", "Language type").Action(c.lang.Set).StringVar(&c.lang.Value) 59 c.CmdClause.Flag("metadata-disable", "Disable Wasm binary metadata annotations").Action(c.metadataDisable.Set).BoolVar(&c.metadataDisable.Value) 60 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) 61 c.CmdClause.Flag("metadata-show", "Inspect the Wasm binary metadata").Action(c.metadataShow.Set).BoolVar(&c.metadataShow.Value) 62 c.CmdClause.Flag("package", "Path to a package tar.gz").Short('p').Action(c.pkg.Set).StringVar(&c.pkg.Value) 63 c.CmdClause.Flag("package-name", "Package name").Action(c.packageName.Set).StringVar(&c.packageName.Value) 64 c.RegisterFlag(argparser.StringFlagOpts{ 65 Name: argparser.FlagServiceIDName, 66 Description: argparser.FlagServiceIDDesc, 67 Dst: &c.Globals.Manifest.Flag.ServiceID, 68 Short: 's', 69 }) 70 c.RegisterFlag(argparser.StringFlagOpts{ 71 Action: c.serviceName.Set, 72 Name: argparser.FlagServiceName, 73 Description: argparser.FlagServiceDesc, 74 Dst: &c.serviceName.Value, 75 }) 76 c.CmdClause.Flag("status-check-code", "Set the expected status response for the service availability check to the root path").IntVar(&c.statusCheckCode) 77 c.CmdClause.Flag("status-check-off", "Disable the service availability check").BoolVar(&c.statusCheckOff) 78 c.CmdClause.Flag("status-check-path", "Specify the URL path for the service availability check").Default("/").StringVar(&c.statusCheckPath) 79 c.CmdClause.Flag("status-check-timeout", "Set a timeout (in seconds) for the service availability check").Default("120").IntVar(&c.statusCheckTimeout) 80 c.RegisterFlag(argparser.StringFlagOpts{ 81 Name: argparser.FlagVersionName, 82 Description: argparser.FlagVersionDesc, 83 Dst: &c.serviceVersion.Value, 84 Action: c.serviceVersion.Set, 85 }) 86 c.CmdClause.Flag("timeout", "Timeout, in seconds, for the build compilation step").Action(c.timeout.Set).IntVar(&c.timeout.Value) 87 88 return &c 89 } 90 91 // Exec implements the command interface. 92 // 93 // NOTE: unlike other non-aggregate commands that initialize a new 94 // text.Progress type for displaying progress information to the user, we don't 95 // use that in this command because the nested commands overlap the output in 96 // non-deterministic ways. It's best to leave those nested commands to handle 97 // the progress indicator. 98 func (c *PublishCommand) Exec(in io.Reader, out io.Writer) (err error) { 99 wd, err := os.Getwd() 100 if err != nil { 101 return fmt.Errorf("failed to get current working directory: %w", err) 102 } 103 defer func() { 104 _ = os.Chdir(wd) 105 }() 106 107 c.projectDir, err = ChangeProjectDirectory(c.dir.Value) 108 if err != nil { 109 return err 110 } 111 if c.projectDir != "" { 112 if c.Globals.Verbose() { 113 text.Info(out, ProjectDirMsg, c.projectDir) 114 } 115 } 116 117 err = c.Build(in, out) 118 if err != nil { 119 c.Globals.ErrLog.Add(err) 120 return err 121 } 122 123 text.Break(out) 124 125 err = c.Deploy(in, out) 126 if err != nil { 127 c.Globals.ErrLog.Add(err) 128 return err 129 } 130 return nil 131 } 132 133 // Build constructs and executes the build logic. 134 func (c *PublishCommand) Build(in io.Reader, out io.Writer) error { 135 // Reset the fields on the BuildCommand based on PublishCommand values. 136 if c.dir.WasSet { 137 c.build.Flags.Dir = c.dir.Value 138 } 139 if c.env.WasSet { 140 c.build.Flags.Env = c.env.Value 141 } 142 if c.includeSrc.WasSet { 143 c.build.Flags.IncludeSrc = c.includeSrc.Value 144 } 145 if c.lang.WasSet { 146 c.build.Flags.Lang = c.lang.Value 147 } 148 if c.packageName.WasSet { 149 c.build.Flags.PackageName = c.packageName.Value 150 } 151 if c.timeout.WasSet { 152 c.build.Flags.Timeout = c.timeout.Value 153 } 154 if c.metadataDisable.WasSet { 155 c.build.MetadataDisable = c.metadataDisable.Value 156 } 157 if c.metadataFilterEnvVars.WasSet { 158 c.build.MetadataFilterEnvVars = c.metadataFilterEnvVars.Value 159 } 160 if c.metadataShow.WasSet { 161 c.build.MetadataShow = c.metadataShow.Value 162 } 163 if c.projectDir != "" { 164 c.build.SkipChangeDir = true // we've already changed directory 165 } 166 return c.build.Exec(in, out) 167 } 168 169 // Deploy constructs and executes the deploy logic. 170 func (c *PublishCommand) Deploy(in io.Reader, out io.Writer) error { 171 // Reset the fields on the DeployCommand based on PublishCommand values. 172 if c.dir.WasSet { 173 c.deploy.Dir = c.dir.Value 174 } 175 if c.pkg.WasSet { 176 c.deploy.PackagePath = c.pkg.Value 177 } 178 if c.serviceName.WasSet { 179 c.deploy.ServiceName = c.serviceName // deploy's field is a argparser.OptionalServiceNameID 180 } 181 if c.serviceVersion.WasSet { 182 c.deploy.ServiceVersion = c.serviceVersion // deploy's field is a argparser.OptionalServiceVersion 183 } 184 if c.domain.WasSet { 185 c.deploy.Domain = c.domain.Value 186 } 187 if c.env.WasSet { 188 c.deploy.Env = c.env.Value 189 } 190 if c.comment.WasSet { 191 c.deploy.Comment = c.comment 192 } 193 if c.statusCheckCode > 0 { 194 c.deploy.StatusCheckCode = c.statusCheckCode 195 } 196 if c.statusCheckOff { 197 c.deploy.StatusCheckOff = c.statusCheckOff 198 } 199 if c.statusCheckTimeout > 0 { 200 c.deploy.StatusCheckTimeout = c.statusCheckTimeout 201 } 202 c.deploy.StatusCheckPath = c.statusCheckPath 203 return c.deploy.Exec(in, out) 204 }