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  }