diff --git a/commands/local_server_start.go b/commands/local_server_start.go index 1ddb5016..dfa61a9c 100644 --- a/commands/local_server_start.go +++ b/commands/local_server_start.go @@ -60,32 +60,11 @@ var localServerStartCmd = &console.Command{ Aliases: []*console.Alias{{Name: "server:start"}, {Name: "serve"}}, Usage: "Run a local web server", Description: localWebServerProdWarningMsg, - Flags: []console.Flag{ + Flags: append( + project.ConfigurationFlags, dirFlag, - &console.BoolFlag{Name: "allow-http", Usage: "Prevent auto-redirection from HTTP to HTTPS"}, - &console.StringFlag{Name: "document-root", Usage: "Project document root (auto-configured by default)"}, - &console.StringFlag{Name: "passthru", Usage: "Project passthru index (auto-configured by default)"}, - &console.IntFlag{Name: "port", DefaultValue: 8000, Usage: "Preferred HTTP port"}, - &console.StringFlag{Name: "listen-ip", DefaultValue: "127.0.0.1", Usage: "The IP on which the CLI should listen"}, - &console.BoolFlag{Name: "allow-all-ip", Usage: "Listen on all the available interfaces"}, - &console.BoolFlag{Name: "daemon", Aliases: []string{"d"}, Usage: "Run the server in the background"}, &console.BoolFlag{Name: "no-humanize", Usage: "Do not format JSON logs"}, - &console.StringFlag{Name: "p12", Usage: "Name of the file containing the TLS certificate to use in p12 format"}, - &console.BoolFlag{Name: "no-tls", Usage: "Use HTTP instead of HTTPS"}, - &console.BoolFlag{Name: "use-gzip", Usage: "Use GZIP"}, - &console.StringFlag{ - Name: "tls-key-log-file", - Usage: "Destination for TLS master secrets in NSS key log format", - // If 'SSLKEYLOGFILE' environment variable is set, uses this as a - // destination of TLS key log. In this context, the name - // 'SSLKEYLOGFILE' is common, so using 'SSL' instead of 'TLS' name. - // This environment variable is preferred than the key log file - // from the console argument. - EnvVars: []string{"SSLKEYLOGFILE"}, - }, - &console.BoolFlag{Name: "no-workers", Usage: "Do not start workers"}, - &console.BoolFlag{Name: "allow-cors", Usage: "Allow Cross-origin resource sharing (CORS) requests"}, - }, + ), Action: func(c *console.Context) error { ui := terminal.SymfonyStyle(terminal.Stdout, terminal.Stdin) projectDir, err := getProjectDir(c.String("dir")) @@ -109,12 +88,21 @@ var localServerStartCmd = &console.Command{ return console.Exit("", 1) } + lw, err := pidFile.LogWriter() + if err != nil { + return err + } + reexec.NotifyForeground("config") - config, fileConfig, err := project.NewConfigFromContext(c, projectDir) + config, err := project.NewConfigFromContext( + c, + zerolog.New(lw).With().Str("source", "server").Timestamp().Logger(), + homeDir, + projectDir, + ) if err != nil { return errors.WithStack(err) } - config.HomeDir = homeDir if config.Daemon && !reexec.IsChild() { varDir := filepath.Join(homeDir, "var") @@ -148,20 +136,20 @@ var localServerStartCmd = &console.Command{ if err != nil { return errors.WithStack(err) } - if fileConfig != nil && fileConfig.Proxy != nil { - if err := proxyConfig.ReplaceDirDomains(projectDir, fileConfig.Proxy.Domains); err != nil { + if len(config.Proxy.Domains) > 0 { + if err := proxyConfig.ReplaceDirDomains(projectDir, config.Proxy.Domains); err != nil { return errors.WithStack(err) } } reexec.NotifyForeground("tls") - if !config.NoTLS && config.PKCS12 == "" { + if !config.HTTP.NoTLS && config.HTTP.PKCS12 == "" { ca, err := cert.NewCA(filepath.Join(homeDir, "certs")) if err != nil { return errors.WithStack(err) } else if !ca.HasCA() { ui.Warning(fmt.Sprintf(`run "%s server:ca:install" first if you want to run the web server with TLS support, or use "--p12" or "--no-tls" to avoid this warning`, c.App.HelpName)) - config.NoTLS = true + config.HTTP.NoTLS = true } else { p12 := filepath.Join(homeDir, "certs", "default.p12") if _, err := os.Stat(p12); os.IsNotExist(err) { @@ -182,24 +170,19 @@ var localServerStartCmd = &console.Command{ ui.Warning(fmt.Sprintf(`Your local CA must be regenerated, run "%s %s --renew" first to renew it`, c.App.HelpName, localServerCAInstallCmd.FullName())) } } - config.PKCS12 = p12 + config.HTTP.PKCS12 = p12 } } - if config.TlsKeyLogFile != "" { + if config.HTTP.TlsKeyLogFile != "" { ui.Warning(localWebServerTlsKeyLogWarningMsg) } - if config.AllowCORS { + if config.HTTP.AllowCORS { ui.Warning(localWebServerAllowsCORSLogWarningMsg) } - lw, err := pidFile.LogWriter() - if err != nil { - return err - } - config.Logger = zerolog.New(lw).With().Str("source", "server").Timestamp().Logger() - p, err := project.New(config) + p, err := project.New(config, c.App.Version) if err != nil { return err } @@ -283,7 +266,7 @@ var localServerStartCmd = &console.Command{ } scheme := "https" - if config.NoTLS { + if config.HTTP.NoTLS { scheme = "http" } @@ -310,7 +293,7 @@ var localServerStartCmd = &console.Command{ reexec.NotifyForeground("listening") ui.Warning(localWebServerProdWarningMsg) - if config.ListenIp == "127.0.0.1" { + if config.HTTP.ListenIp == "127.0.0.1" { ui.Warning(`Please note that the Symfony CLI only listens on 127.0.0.1 by default since version 5.10.3. You can use the --allow-all-ip or --listen-ip flags to change this behavior.`) } @@ -321,16 +304,16 @@ var localServerStartCmd = &console.Command{ go tailer.Tail(terminal.Stderr) } - if fileConfig != nil && !config.NoWorkers { + if !config.NoWorkers { reexec.NotifyForeground("workers") - _, isDockerComposeWorkerConfigured := fileConfig.Workers[project.DockerComposeWorkerKey] + _, isDockerComposeWorkerConfigured := config.Workers[project.DockerComposeWorkerKey] var dockerWg sync.WaitGroup if isDockerComposeWorkerConfigured { dockerWg.Add(1) } - for name, worker := range fileConfig.Workers { + for name, worker := range config.Workers { pidFile := pid.New(projectDir, worker.Cmd) if pidFile.IsRunning() { terminal.Eprintfln("WARNING Unable to start worker \"%s\": it is already running for this project as PID %d", name, pidFile.Pid) diff --git a/local/project/config.go b/local/project/config.go index 82049342..b2cc73bd 100644 --- a/local/project/config.go +++ b/local/project/config.go @@ -29,111 +29,251 @@ import ( "gopkg.in/yaml.v2" ) -const DockerComposeWorkerKey = "docker_compose" - -// Config is the struct taken by New (should not be used for anything else) -type Config struct { - HomeDir string - ProjectDir string - ListenIp string - DocumentRoot string `yaml:"document_root"` - Passthru string `yaml:"passthru"` - Port int `yaml:"port"` - PreferredPort int `yaml:"preferred_port"` - PKCS12 string `yaml:"p12"` - Logger zerolog.Logger - AppVersion string - AllowHTTP bool `yaml:"allow_http"` - NoTLS bool `yaml:"no_tls"` - Daemon bool `yaml:"daemon"` - UseGzip bool `yaml:"use_gzip"` - TlsKeyLogFile string `yaml:"tls_key_log_file"` - NoWorkers bool `yaml:"no_workers"` - AllowCORS bool `yaml:"allow_cors"` -} +const ( + ConfigFilePrefix = ".symfony.local" -type FileConfig struct { - Proxy *struct { - Domains []string `yaml:"domains"` - } `yaml:"proxy"` - HTTP *Config `yaml:"http"` - Workers map[string]*Worker `yaml:"workers"` -} + DockerComposeWorkerKey = "docker_compose" +) -type Worker struct { - Cmd []string `yaml:"cmd"` - Watch []string `yaml:"watch"` +type config struct { + Logger zerolog.Logger + HomeDir string + ProjectDir string + + NoWorkers bool + Daemon bool + + HTTP struct { + DocumentRoot string + Passthru string + Port int + PreferredPort int + ListenIp string + AllowHTTP bool + NoTLS bool + PKCS12 string + TlsKeyLogFile string + UseGzip bool + AllowCORS bool + } + Workers map[string]struct { + Cmd []string + Watch []string + } + Proxy struct { + Domains []string + } } -func NewConfigFromContext(c *console.Context, projectDir string) (*Config, *FileConfig, error) { - config := &Config{} - var fileConfig *FileConfig - var err error - fileConfig, err = newConfigFromFile(filepath.Join(projectDir, ".symfony.local.yaml")) - if err != nil { - return nil, nil, err +func NewConfigFromDirectory(logger zerolog.Logger, homeDir, projectDir string) (*config, error) { + config := &config{ + Logger: logger, + HomeDir: homeDir, + ProjectDir: projectDir, + Workers: make(map[string]struct { + Cmd []string + Watch []string + }), } - if fileConfig != nil { - if fileConfig.HTTP == nil { - fileConfig.HTTP = &Config{} - } else { - config = fileConfig.HTTP + + // first consider project configuration files in this specific order + for _, suffix := range []string{".dist.yaml", ".yaml", ".override.yaml"} { + fileConfig, err := newConfigFromFile(filepath.Join(projectDir, ConfigFilePrefix+suffix)) + if errors.Is(err, os.ErrNotExist) { + continue + } else if err != nil { + return nil, err + } else if fileConfig == nil { + continue } - if fileConfig.Workers == nil { - fileConfig.Workers = make(map[string]*Worker) + + config.mergeWithFileConfig(*fileConfig) + } + + for k, v := range config.Workers { + if len(v.Cmd) == 0 { + return nil, errors.Errorf(`The command for the "%s" worker entry cannot be empty.`, k) } } - config.AppVersion = c.App.Version - config.ProjectDir = projectDir + + return config, nil +} + +func NewConfigFromContext(c *console.Context, logger zerolog.Logger, homeDir, projectDir string) (*config, error) { + config, err := NewConfigFromDirectory(logger, homeDir, projectDir) + if err != nil { + return nil, err + } + + // then each option that can be overridden by command line flags + config.mergeWithContext(c) + + return config, nil +} + +func (config *config) mergeWithContext(c *console.Context) { if c.IsSet("allow-all-ip") { - config.ListenIp = "" + config.HTTP.ListenIp = "" } else { - config.ListenIp = c.String("listen-ip") + config.HTTP.ListenIp = c.String("listen-ip") } if c.IsSet("document-root") { - config.DocumentRoot = c.String("document-root") + config.HTTP.DocumentRoot = c.String("document-root") } if c.IsSet("passthru") { - config.Passthru = c.String("passthru") + config.HTTP.Passthru = c.String("passthru") } if c.IsSet("port") { - config.Port = c.Int("port") + config.HTTP.Port = c.Int("port") } - if config.Port == 0 { - config.PreferredPort = 8000 + if config.HTTP.Port == 0 { // ? + config.HTTP.PreferredPort = 8000 + } + if c.IsSet("allow-cors") { + config.HTTP.AllowCORS = c.Bool("allow-cors") } if c.IsSet("allow-http") { - config.AllowHTTP = c.Bool("allow-http") + config.HTTP.AllowHTTP = c.Bool("allow-http") } if c.IsSet("p12") { - config.PKCS12 = c.String("p12") + config.HTTP.PKCS12 = c.String("p12") } if c.IsSet("no-tls") { - config.NoTLS = c.Bool("no-tls") + config.HTTP.NoTLS = c.Bool("no-tls") } - if c.IsSet("daemon") { - config.Daemon = c.Bool("daemon") + if c.IsSet("tls-key-log-suffix") { + config.HTTP.TlsKeyLogFile = c.String("tls-key-log-suffix") } if c.IsSet("use-gzip") { - config.UseGzip = c.Bool("use-gzip") + config.HTTP.UseGzip = c.Bool("use-gzip") } - if c.IsSet("tls-key-log-file") { - config.TlsKeyLogFile = c.String("tls-key-log-file") + if c.IsSet("daemon") { + config.Daemon = c.Bool("daemon") } if c.IsSet("no-workers") { config.NoWorkers = c.Bool("no-workers") } - if c.IsSet("allow-cors") { - config.AllowCORS = c.Bool("allow-cors") +} + +func (config *config) mergeWithFileConfig(fileConfig fileConfig) { + config.Logger.Debug().Msgf("Loading configuration from %s", fileConfig.filename) + + if fileConfig.Daemon != nil { + config.Daemon = *fileConfig.Daemon + } + if fileConfig.NoWorkers != nil { + config.NoWorkers = *fileConfig.NoWorkers } - return config, fileConfig, nil + if fileConfig.Proxy != nil { + config.Proxy.Domains = fileConfig.Proxy.Domains + } + + if fileConfig.Workers != nil { + for workerName, fileWorker := range fileConfig.Workers { + worker, hasWorkerDefined := config.Workers[workerName] + + if fileWorker == nil { + if !hasWorkerDefined { + continue + } + + delete(config.Workers, workerName) + continue + } + + if fileWorker.Cmd != nil { + worker.Cmd = fileWorker.Cmd + } + + if fileWorker.Watch != nil { + worker.Watch = fileWorker.Watch + } + + config.Workers[workerName] = worker + } + } + + if fileConfig.HTTP != nil { + if fileConfig.HTTP.DocumentRoot != nil { + config.HTTP.DocumentRoot = *fileConfig.HTTP.DocumentRoot + } + if fileConfig.HTTP.Passthru != nil { + config.HTTP.Passthru = *fileConfig.HTTP.Passthru + } + if fileConfig.HTTP.Port != nil { + config.HTTP.Port = *fileConfig.HTTP.Port + } + if fileConfig.HTTP.PreferredPort != nil { + config.HTTP.PreferredPort = *fileConfig.HTTP.PreferredPort + } + if fileConfig.HTTP.AllowCORS != nil { + config.HTTP.AllowCORS = *fileConfig.HTTP.AllowCORS + } + if fileConfig.HTTP.AllowHTTP != nil { + config.HTTP.AllowHTTP = *fileConfig.HTTP.AllowHTTP + } + if fileConfig.HTTP.NoTLS != nil { + config.HTTP.NoTLS = *fileConfig.HTTP.NoTLS + } + if fileConfig.HTTP.PKCS12 != nil { + config.HTTP.PKCS12 = *fileConfig.HTTP.PKCS12 + } + if fileConfig.HTTP.TlsKeyLogFile != nil { + config.HTTP.TlsKeyLogFile = *fileConfig.HTTP.TlsKeyLogFile + } + if fileConfig.HTTP.UseGzip != nil { + config.HTTP.UseGzip = *fileConfig.HTTP.UseGzip + } + + if fileConfig.HTTP.Daemon != nil { + config.Daemon = *fileConfig.HTTP.Daemon + config.Logger.Warn().Msgf(`"http.daemon" setting has been deprecated since v5.12.0, use the "daemon" (at root level) setting instead.`) + } + if fileConfig.HTTP.NoWorkers != nil { + config.NoWorkers = *fileConfig.HTTP.NoWorkers + config.Logger.Warn().Msgf(`"http.no_workers" setting has been deprecated since v5.12.0, use the "no_workers" (at root level) setting instead.`) + } + } +} + +type fileConfig struct { + filename string + + NoWorkers *bool `yaml:"no_workers"` + Daemon *bool `yaml:"daemon"` + + Proxy *struct { + Domains []string `yaml:"domains"` + } `yaml:"proxy"` + HTTP *struct { + DocumentRoot *string `yaml:"document_root"` + Passthru *string `yaml:"passthru"` + Port *int `yaml:"port"` + PreferredPort *int `yaml:"preferred_port"` + AllowHTTP *bool `yaml:"allow_http"` + NoTLS *bool `yaml:"no_tls"` + PKCS12 *string `yaml:"p12"` + TlsKeyLogFile *string `yaml:"tls_key_log_file"` + UseGzip *bool `yaml:"use_gzip"` + AllowCORS *bool `yaml:"allow_cors"` + + // BC-layer + Daemon *bool `yaml:"daemon"` + NoWorkers *bool `yaml:"no_workers"` + } `yaml:"http"` + Workers map[string]*workerFileConfig `yaml:"workers"` +} + +type workerFileConfig struct { + Cmd []string `yaml:"cmd"` + Watch []string `yaml:"watch"` } // Should only be used when for customers -func newConfigFromFile(configFile string) (*FileConfig, error) { +func newConfigFromFile(configFile string) (*fileConfig, error) { if _, err := os.Stat(configFile); err != nil { - return nil, nil + return nil, errors.Wrapf(err, "config file %s does not exist", configFile) } contents, err := os.ReadFile(configFile) @@ -141,7 +281,9 @@ func newConfigFromFile(configFile string) (*FileConfig, error) { return nil, err } - var fileConfig FileConfig + fileConfig := fileConfig{ + filename: filepath.Base(configFile), + } if err := yaml.Unmarshal(contents, &fileConfig); err != nil { return nil, err } @@ -153,14 +295,13 @@ func newConfigFromFile(configFile string) (*FileConfig, error) { return &fileConfig, nil } -func (c *FileConfig) parseWorkers() error { +func (c *fileConfig) parseWorkers() error { if c.Workers == nil { - c.Workers = make(map[string]*Worker) return nil } if v, ok := c.Workers[DockerComposeWorkerKey]; ok && v == nil { - c.Workers[DockerComposeWorkerKey] = &Worker{ + c.Workers[DockerComposeWorkerKey] = &workerFileConfig{ Cmd: []string{"docker", "compose", "up"}, Watch: []string{ "compose.yaml", "compose.override.yaml", @@ -171,22 +312,16 @@ func (c *FileConfig) parseWorkers() error { } } if v, ok := c.Workers["yarn_encore_watch"]; ok && v == nil { - c.Workers["yarn_encore_watch"] = &Worker{ + c.Workers["yarn_encore_watch"] = &workerFileConfig{ Cmd: []string{"yarn", "encore", "dev", "--watch"}, } } if v, ok := c.Workers["messenger_consume_async"]; ok && v == nil { - c.Workers["messenger_consume_async"] = &Worker{ + c.Workers["messenger_consume_async"] = &workerFileConfig{ Cmd: []string{"symfony", "console", "messenger:consume", "async"}, Watch: []string{"config", "src", "templates", "vendor"}, } } - for k, v := range c.Workers { - if v == nil { - return errors.Errorf("The \"%s\" worker entry in \".symfony.local.yaml\" cannot be empty.", k) - } - } - return nil } diff --git a/local/project/config_test.go b/local/project/config_test.go new file mode 100644 index 00000000..1bf3d9d8 --- /dev/null +++ b/local/project/config_test.go @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2021-present Fabien Potencier + * + * This file is part of Symfony CLI project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package project + +import ( + "github.com/rs/zerolog" + "github.com/symfony-cli/console" + . "gopkg.in/check.v1" +) + +type ConfigSuite struct{} + +var _ = Suite(&ConfigSuite{}) + +func (s *ConfigSuite) TestDefaultConfig(c *C) { + config, err := NewConfigFromDirectory( + zerolog.Nop(), + "", + "", + ) + c.Assert(err, IsNil) + c.Assert(config, NotNil) +} + +func (s *ConfigSuite) TestConfigFromDirectory(c *C) { + config, err := NewConfigFromDirectory( + zerolog.Nop(), + "testdata", + "testdata", + ) + c.Assert(err, IsNil) + c.Assert(config, NotNil) + + c.Assert(config.NoWorkers, Equals, true) + c.Assert(config.Daemon, Equals, false) + + c.Assert(config.Proxy.Domains, DeepEquals, []string{"foo"}) + c.Assert(config.Proxy.Domains, DeepEquals, []string{"foo"}) + + c.Assert(config.HTTP.PreferredPort, Equals, 8181) + + c.Assert(config.Workers, HasLen, 3) + c.Assert(config.Workers["docker_compose"].Cmd, NotNil) + c.Assert(config.Workers["docker_compose"].Cmd, Not(Equals), []string{}) + + c.Assert(config.Workers["messenger_consume_async"].Cmd, NotNil) + c.Assert(config.Workers["messenger_consume_async"].Cmd, Not(Equals), []string{}) + + c.Assert(config.Workers["my_node_process"].Cmd, DeepEquals, []string{"npx", "foo"}) + c.Assert(config.Workers["my_node_process"].Watch, DeepEquals, []string{".node_version"}) +} + +func (s *ConfigSuite) TestConfigFromContext(c *C) { + app := console.Application{ + Flags: ConfigurationFlags, + Action: func(context *console.Context) error { + config, err := NewConfigFromContext( + context, + zerolog.Nop(), + "testdata", + "testdata", + ) + c.Assert(err, IsNil) + c.Assert(config, NotNil) + + //c.Assert(config.HTTP.PreferredPort, Equals, 8282) + c.Assert(config.HTTP.AllowHTTP, Equals, true) + + c.Assert(config.NoWorkers, Equals, true) + c.Assert(config.Daemon, Equals, false) + + c.Assert(config.Proxy.Domains, DeepEquals, []string{"foo"}) + c.Assert(config.Proxy.Domains, DeepEquals, []string{"foo"}) + + c.Assert(config.Workers, HasLen, 3) + c.Assert(config.Workers["docker_compose"].Cmd, NotNil) + c.Assert(config.Workers["docker_compose"].Cmd, Not(Equals), []string{}) + + c.Assert(config.Workers["messenger_consume_async"].Cmd, NotNil) + c.Assert(config.Workers["messenger_consume_async"].Cmd, Not(Equals), []string{}) + + c.Assert(config.Workers["my_node_process"].Cmd, DeepEquals, []string{"npx", "foo"}) + c.Assert(config.Workers["my_node_process"].Watch, DeepEquals, []string{".node_version"}) + + return nil + }, + } + c.Check(app.Run([]string{"--port=8282", "--allow-http=true"}), IsNil) +} diff --git a/local/project/flags.go b/local/project/flags.go new file mode 100644 index 00000000..8915ab52 --- /dev/null +++ b/local/project/flags.go @@ -0,0 +1,47 @@ +/* + * Copyright (c) 2021-present Fabien Potencier + * + * This file is part of Symfony CLI project + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU Affero General Public License as + * published by the Free Software Foundation, either version 3 of the + * License, or (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU Affero General Public License for more details. + * + * You should have received a copy of the GNU Affero General Public License + * along with this program. If not, see . + */ + +package project + +import "github.com/symfony-cli/console" + +var ConfigurationFlags = []console.Flag{ + &console.BoolFlag{Name: "allow-http", Usage: "Prevent auto-redirection from HTTP to HTTPS"}, + &console.StringFlag{Name: "document-root", Usage: "Project document root (auto-configured by default)"}, + &console.StringFlag{Name: "passthru", Usage: "Project passthru index (auto-configured by default)"}, + &console.IntFlag{Name: "port", DefaultValue: 8000, Usage: "Preferred HTTP port"}, + &console.StringFlag{Name: "listen-ip", DefaultValue: "127.0.0.1", Usage: "The IP on which the CLI should listen"}, + &console.BoolFlag{Name: "allow-all-ip", Usage: "Listen on all the available interfaces"}, + &console.BoolFlag{Name: "daemon", Aliases: []string{"d"}, Usage: "Run the server in the background"}, + &console.StringFlag{Name: "p12", Usage: "Name of the file containing the TLS certificate to use in p12 format"}, + &console.BoolFlag{Name: "no-tls", Usage: "Use HTTP instead of HTTPS"}, + &console.BoolFlag{Name: "use-gzip", Usage: "Use GZIP"}, + &console.StringFlag{ + Name: "tls-key-log-file", + Usage: "Destination for TLS master secrets in NSS key log format", + // If 'SSLKEYLOGFILE' environment variable is set, uses this as a + // destination of TLS key log. In this context, the name + // 'SSLKEYLOGFILE' is common, so using 'SSL' instead of 'TLS' name. + // This environment variable is preferred than the key log file + // from the console argument. + EnvVars: []string{"SSLKEYLOGFILE"}, + }, + &console.BoolFlag{Name: "no-workers", Usage: "Do not start workers"}, + &console.BoolFlag{Name: "allow-cors", Usage: "Allow Cross-origin resource sharing (CORS) requests"}, +} diff --git a/local/project/project.go b/local/project/project.go index ac179b12..38d77c83 100644 --- a/local/project/project.go +++ b/local/project/project.go @@ -40,26 +40,26 @@ type Project struct { } // New creates a new PHP project -func New(c *Config) (*Project, error) { - documentRoot, err := realDocumentRoot(c.ProjectDir, c.DocumentRoot) +func New(c *config, appVersion string) (*Project, error) { + documentRoot, err := realDocumentRoot(c.ProjectDir, c.HTTP.DocumentRoot) if err != nil { return nil, errors.WithStack(err) } - passthru, err := realPassthru(documentRoot, c.Passthru) + passthru, err := realPassthru(documentRoot, c.HTTP.Passthru) p := &Project{ Logger: c.Logger.With().Str("source", "HTTP").Logger(), HTTP: &lhttp.Server{ - DocumentRoot: documentRoot, - Port: c.Port, - PreferredPort: c.PreferredPort, - ListenIp: c.ListenIp, + Appversion: appVersion, Logger: c.Logger, - PKCS12: c.PKCS12, - AllowHTTP: c.AllowHTTP, - UseGzip: c.UseGzip, - Appversion: c.AppVersion, - TlsKeyLogFile: c.TlsKeyLogFile, - AllowCORS: c.AllowCORS, + DocumentRoot: documentRoot, + Port: c.HTTP.Port, + PreferredPort: c.HTTP.PreferredPort, + ListenIp: c.HTTP.ListenIp, + PKCS12: c.HTTP.PKCS12, + AllowHTTP: c.HTTP.AllowHTTP, + UseGzip: c.HTTP.UseGzip, + TlsKeyLogFile: c.HTTP.TlsKeyLogFile, + AllowCORS: c.HTTP.AllowCORS, }, } if err != nil { @@ -68,13 +68,13 @@ func New(c *Config) (*Project, error) { msg += ", disabling the PHP server" } p.Logger.Warn().Err(err).Msg(msg) - } else if c.Passthru == "index.html" { + } else if c.HTTP.Passthru == "index.html" { p.HTTP.Callback = func(w http.ResponseWriter, r *http.Request, env map[string]string) error { http.ServeFile(w, r, "/index.html") return nil } } else { - p.PHPServer, err = php.NewServer(c.HomeDir, c.ProjectDir, documentRoot, passthru, c.AppVersion, c.Logger) + p.PHPServer, err = php.NewServer(c.HomeDir, c.ProjectDir, documentRoot, passthru, appVersion, c.Logger) if err != nil { return nil, err } diff --git a/local/project/testdata/.symfony.local.dist.yaml b/local/project/testdata/.symfony.local.dist.yaml new file mode 100644 index 00000000..9e5fc37a --- /dev/null +++ b/local/project/testdata/.symfony.local.dist.yaml @@ -0,0 +1,12 @@ +daemon: true + +proxy: + domains: + - bar + +workers: + docker_compose: ~ + hello_world: + cmd: [ "echo", "hello", "world" ] + my_node_process: + cmd: ["npx", "foo"] diff --git a/local/project/testdata/.symfony.local.override.yaml b/local/project/testdata/.symfony.local.override.yaml new file mode 100644 index 00000000..a20529a3 --- /dev/null +++ b/local/project/testdata/.symfony.local.override.yaml @@ -0,0 +1,13 @@ +daemon: false + +proxy: + domains: + - foo + +http: + preferred_port: 8181 + +workers: + hello_world: null + my_node_process: + watch: [".node_version",] diff --git a/local/project/testdata/.symfony.local.yaml b/local/project/testdata/.symfony.local.yaml new file mode 100644 index 00000000..3566a4c8 --- /dev/null +++ b/local/project/testdata/.symfony.local.yaml @@ -0,0 +1,5 @@ +http: + no_workers: true + +workers: + messenger_consume_async: ~