diff --git a/cmd/connect-client/commands/init.go b/cmd/connect-client/commands/init.go index 33de82d49..8d9127386 100644 --- a/cmd/connect-client/commands/init.go +++ b/cmd/connect-client/commands/init.go @@ -5,7 +5,6 @@ package commands import ( "fmt" - "github.com/rstudio/connect-client/internal/apptypes" "github.com/rstudio/connect-client/internal/bundles" "github.com/rstudio/connect-client/internal/cli_types" "github.com/rstudio/connect-client/internal/config" @@ -22,7 +21,7 @@ func (cmd *InitCommand) inspectProjectType(log logging.Logger) (*inspect.Content if err != nil { return nil, fmt.Errorf("error detecting content type: %w", err) } - log.Info("Deployment type", "Entrypoint", contentType.Entrypoint, "AppMode", contentType.AppMode) + log.Info("Deployment type", "Entrypoint", contentType.Entrypoint, "AppMode", contentType.Type) return contentType, nil } @@ -33,8 +32,8 @@ type InitCommand struct { config *config.Config } -func (cmd *InitCommand) requiresPython(appMode apptypes.AppMode) (bool, error) { - if appMode.IsPythonContent() { +func (cmd *InitCommand) requiresPython(contentType config.ContentType) (bool, error) { + if contentType.IsPythonContent() { return true, nil } if cmd.Python.Path() != "" { @@ -79,10 +78,10 @@ func (cmd *InitCommand) Run(args *cli_types.CommonArgs, ctx *cli_types.CLIContex if err != nil { return err } - cmd.config.Type = contentType.AppMode + cmd.config.Type = contentType.Type cmd.config.Entrypoint = contentType.Entrypoint - requiresPython, err := cmd.requiresPython(contentType.AppMode) + requiresPython, err := cmd.requiresPython(contentType.Type) if err != nil { return err } diff --git a/internal/bundles/manifest.go b/internal/bundles/manifest.go index d27d7cab7..6a2dea8ca 100644 --- a/internal/bundles/manifest.go +++ b/internal/bundles/manifest.go @@ -154,11 +154,33 @@ func NewManifest() *Manifest { } } +var connectContentTypeMap = map[config.ContentType]apptypes.AppMode{ + config.ContentTypeHTML: apptypes.StaticMode, + config.ContentTypeJupyterNotebook: apptypes.StaticJupyterMode, + config.ContentTypeJupyterVoila: apptypes.JupyterVoilaMode, + config.ContentTypePythonBokeh: apptypes.PythonBokehMode, + config.ContentTypePythonDash: apptypes.PythonDashMode, + config.ContentTypePythonFastAPI: apptypes.PythonFastAPIMode, + config.ContentTypePythonFlask: apptypes.PythonAPIMode, + config.ContentTypePythonShiny: apptypes.PythonShinyMode, + config.ContentTypePythonStreamlit: apptypes.PythonStreamlitMode, + config.ContentTypeQuartoShiny: apptypes.ShinyQuartoMode, + config.ContentTypeQuarto: apptypes.StaticQuartoMode, + config.ContentTypeRPlumber: apptypes.PlumberAPIMode, + config.ContentTypeRShiny: apptypes.ShinyMode, + config.ContentTypeRMarkdownShiny: apptypes.ShinyRmdMode, + config.ContentTypeRMarkdown: apptypes.StaticRmdMode, +} + func NewManifestFromConfig(cfg *config.Config) *Manifest { + contentType, ok := connectContentTypeMap[cfg.Type] + if !ok { + contentType = apptypes.UnknownMode + } m := &Manifest{ Version: 1, Metadata: Metadata{ - AppMode: cfg.Type, + AppMode: contentType, Entrypoint: cfg.Entrypoint, }, Jupyter: nil, @@ -185,10 +207,10 @@ func NewManifestFromConfig(cfg *config.Config) *Manifest { } } switch cfg.Type { - case apptypes.StaticRmdMode: - case apptypes.ShinyRmdMode: + case config.ContentTypeRMarkdown: + case config.ContentTypeRMarkdownShiny: m.Metadata.PrimaryRmd = cfg.Entrypoint - case apptypes.StaticMode: + case config.ContentTypeHTML: m.Metadata.PrimaryHtml = cfg.Entrypoint } return m diff --git a/internal/config/config_test.go b/internal/config/config_test.go index 1e7e70680..6846c1778 100644 --- a/internal/config/config_test.go +++ b/internal/config/config_test.go @@ -6,7 +6,6 @@ import ( "io/fs" "testing" - "github.com/rstudio/connect-client/internal/apptypes" "github.com/rstudio/connect-client/internal/util" "github.com/rstudio/connect-client/internal/util/utiltest" "github.com/spf13/afero" @@ -60,7 +59,7 @@ func (s *ConfigSuite) TestFromFile() { cfg, err := FromFile(path) s.NoError(err) s.NotNil(cfg) - s.Equal(apptypes.AppMode("python-dash"), cfg.Type) + s.Equal(ContentTypePythonDash, cfg.Type) } func (s *ConfigSuite) TestFromFileErr() { diff --git a/internal/config/types.go b/internal/config/types.go index 9e75413b5..1b45c678a 100644 --- a/internal/config/types.go +++ b/internal/config/types.go @@ -2,26 +2,57 @@ package config // Copyright (C) 2023 by Posit Software, PBC. -import ( - "github.com/rstudio/connect-client/internal/apptypes" +type ContentType string + +const ( + ContentTypeHTML ContentType = "html" + ContentTypeJupyterNotebook ContentType = "jupyter-notebook" + ContentTypeJupyterVoila ContentType = "jupyter-voila" + ContentTypePythonBokeh ContentType = "python-bokeh" + ContentTypePythonDash ContentType = "python-dash" + ContentTypePythonFastAPI ContentType = "python-fastapi" + ContentTypePythonFlask ContentType = "python-flask" + ContentTypePythonShiny ContentType = "python-shiny" + ContentTypePythonStreamlit ContentType = "python-streamlit" + ContentTypeQuartoShiny ContentType = "quarto-shiny" + ContentTypeQuarto ContentType = "quarto" + ContentTypeRPlumber ContentType = "r-plumber" + ContentTypeRShiny ContentType = "r-shiny" + ContentTypeRMarkdownShiny ContentType = "rmd-shiny" + ContentTypeRMarkdown ContentType = "rmd" ) +func (t ContentType) IsPythonContent() bool { + switch t { + case ContentTypeJupyterNotebook: + case ContentTypeJupyterVoila: + case ContentTypePythonBokeh: + case ContentTypePythonDash: + case ContentTypePythonFastAPI: + case ContentTypePythonFlask: + case ContentTypePythonShiny: + case ContentTypePythonStreamlit: + return true + } + return false +} + type Config struct { - Schema SchemaURL `toml:"$schema" json:"$schema"` - Type apptypes.AppMode `toml:"type" json:"type"` - Entrypoint string `toml:"entrypoint,omitempty" json:"entrypoint,omitempty"` - Title string `toml:"title,omitempty" json:"title,omitempty"` - Description string `toml:"description,multiline,omitempty" json:"description,omitempty"` - ThumbnailFile string `toml:"thumbnail,omitempty" json:"thumbnail,omitempty"` - Tags []string `toml:"tags,omitempty" json:"tags,omitempty"` - Python *Python `toml:"python,omitempty" json:"python,omitempty"` - R *R `toml:"r,omitempty" json:"r,omitempty"` - Quarto *Quarto `toml:"quarto,omitempty" json:"quarto,omitempty"` - Environment Environment `toml:"environment,omitempty" json:"environment,omitempty"` - Secrets []string `toml:"secrets,omitempty" json:"secrets,omitempty"` - Schedules []Schedule `toml:"schedules,omitempty" json:"schedules,omitempty"` - Access *Access `toml:"access,omitempty" json:"access,omitempty"` - Connect *Connect `toml:"connect,omitempty" json:"connect,omitempty"` + Schema SchemaURL `toml:"$schema" json:"$schema"` + Type ContentType `toml:"type" json:"type"` + Entrypoint string `toml:"entrypoint,omitempty" json:"entrypoint,omitempty"` + Title string `toml:"title,omitempty" json:"title,omitempty"` + Description string `toml:"description,multiline,omitempty" json:"description,omitempty"` + ThumbnailFile string `toml:"thumbnail,omitempty" json:"thumbnail,omitempty"` + Tags []string `toml:"tags,omitempty" json:"tags,omitempty"` + Python *Python `toml:"python,omitempty" json:"python,omitempty"` + R *R `toml:"r,omitempty" json:"r,omitempty"` + Quarto *Quarto `toml:"quarto,omitempty" json:"quarto,omitempty"` + Environment Environment `toml:"environment,omitempty" json:"environment,omitempty"` + Secrets []string `toml:"secrets,omitempty" json:"secrets,omitempty"` + Schedules []Schedule `toml:"schedules,omitempty" json:"schedules,omitempty"` + Access *Access `toml:"access,omitempty" json:"access,omitempty"` + Connect *Connect `toml:"connect,omitempty" json:"connect,omitempty"` } type SchemaURL string diff --git a/internal/inspect/all_test.go b/internal/inspect/all_test.go index 6e2f7ca75..44de407b1 100644 --- a/internal/inspect/all_test.go +++ b/internal/inspect/all_test.go @@ -6,7 +6,7 @@ import ( "os" "testing" - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" "github.com/rstudio/connect-client/internal/util/utiltest" "github.com/spf13/afero" @@ -35,7 +35,7 @@ func (s *AllSuite) TestInferTypeDirectory() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.PythonDashMode, + Type: config.ContentTypePythonDash, Entrypoint: appFilename, RequiresPython: true, }, t) @@ -57,7 +57,7 @@ func (s *AllSuite) TestInferTypeFileLowerPriority() { t, err := detector.InferType(htmlPath) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.StaticMode, + Type: config.ContentTypeHTML, Entrypoint: htmlFilename, }, t) } @@ -78,7 +78,7 @@ func (s *AllSuite) TestInferTypeFileHigherPriority() { t, err := detector.InferType(appPath) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.PythonDashMode, + Type: config.ContentTypePythonDash, Entrypoint: appFilename, RequiresPython: true, }, t) @@ -99,7 +99,7 @@ func (s *AllSuite) TestInferTypeDirectoryPriority() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.PythonDashMode, + Type: config.ContentTypePythonDash, Entrypoint: appFilename, RequiresPython: true, }, t) diff --git a/internal/inspect/html.go b/internal/inspect/html.go index 7f36e0ad3..a7e3bfdc9 100644 --- a/internal/inspect/html.go +++ b/internal/inspect/html.go @@ -3,7 +3,7 @@ package inspect // Copyright (C) 2023 by Posit Software, PBC. import ( - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" ) @@ -30,7 +30,7 @@ func (d *StaticHTMLDetector) InferType(path util.Path) (*ContentType, error) { } if entrypoint != "" { return &ContentType{ - AppMode: apptypes.StaticMode, + Type: config.ContentTypeHTML, Entrypoint: entrypoint, }, nil } diff --git a/internal/inspect/html_test.go b/internal/inspect/html_test.go index ce57b1e1c..44af8af93 100644 --- a/internal/inspect/html_test.go +++ b/internal/inspect/html_test.go @@ -6,7 +6,7 @@ import ( "errors" "testing" - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" "github.com/rstudio/connect-client/internal/util/utiltest" "github.com/spf13/afero" @@ -32,7 +32,7 @@ func (s *StaticHTMLDetectorSuite) TestInferTypeSpecifiedFile() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.StaticMode, + Type: config.ContentTypeHTML, Entrypoint: filename, }, t) } @@ -47,7 +47,7 @@ func (s *StaticHTMLDetectorSuite) TestInferTypePreferredFilename() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.StaticMode, + Type: config.ContentTypeHTML, Entrypoint: filename, }, t) } @@ -62,7 +62,7 @@ func (s *StaticHTMLDetectorSuite) TestInferTypeOnlyHTMLFile() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.StaticMode, + Type: config.ContentTypeHTML, Entrypoint: filename, }, t) } diff --git a/internal/inspect/infer.go b/internal/inspect/infer.go index ffbc30b98..4635b703f 100644 --- a/internal/inspect/infer.go +++ b/internal/inspect/infer.go @@ -5,12 +5,12 @@ package inspect import ( "io" - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" ) type ContentType struct { - AppMode apptypes.AppMode + Type config.ContentType Entrypoint string RequiresR bool RequiresPython bool diff --git a/internal/inspect/notebook.go b/internal/inspect/notebook.go index c46fcdd51..2c2769466 100644 --- a/internal/inspect/notebook.go +++ b/internal/inspect/notebook.go @@ -8,7 +8,7 @@ import ( "io" "strings" - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" ) @@ -53,9 +53,9 @@ func (d *NotebookDetector) InferType(path util.Path) (*ContentType, error) { RequiresPython: true, } if isVoila { - t.AppMode = apptypes.JupyterVoilaMode + t.Type = config.ContentTypeJupyterVoila } else { - t.AppMode = apptypes.StaticJupyterMode + t.Type = config.ContentTypeJupyterNotebook } return t, nil } diff --git a/internal/inspect/notebook_test.go b/internal/inspect/notebook_test.go index 4d4556660..c9b1a450d 100644 --- a/internal/inspect/notebook_test.go +++ b/internal/inspect/notebook_test.go @@ -10,7 +10,7 @@ import ( "strings" "testing" - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" "github.com/rstudio/connect-client/internal/util/utiltest" "github.com/spf13/afero" @@ -100,7 +100,7 @@ func (s *NotebookDetectorSuite) TestInferTypePlainNotebook() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.StaticJupyterMode, + Type: config.ContentTypeJupyterNotebook, Entrypoint: filename, RequiresPython: true, }, t) @@ -116,7 +116,7 @@ func (s *NotebookDetectorSuite) TestInferTypeVoilaNotebook() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.JupyterVoilaMode, + Type: config.ContentTypeJupyterVoila, Entrypoint: filename, RequiresPython: true, }, t) diff --git a/internal/inspect/python.go b/internal/inspect/python.go index 586253ddb..95f456650 100644 --- a/internal/inspect/python.go +++ b/internal/inspect/python.go @@ -3,26 +3,26 @@ package inspect // Copyright (C) 2023 by Posit Software, PBC. import ( - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" ) type PythonAppDetector struct { inferenceHelper - appMode apptypes.AppMode - imports []string + contentType config.ContentType + imports []string } -func NewPythonAppDetector(appMode apptypes.AppMode, imports []string) *PythonAppDetector { +func NewPythonAppDetector(contentType config.ContentType, imports []string) *PythonAppDetector { return &PythonAppDetector{ inferenceHelper: defaultInferenceHelper{}, - appMode: appMode, + contentType: contentType, imports: imports, } } func NewFlaskDetector() *PythonAppDetector { - return NewPythonAppDetector(apptypes.PythonAPIMode, []string{ + return NewPythonAppDetector(config.ContentTypePythonFlask, []string{ "flask", // also matches flask_api, flask_openapi3, etc. "flasgger", "falcon", // must check for this after falcon.asgi (FastAPI) @@ -30,7 +30,7 @@ func NewFlaskDetector() *PythonAppDetector { } func NewFastAPIDetector() *PythonAppDetector { - return NewPythonAppDetector(apptypes.PythonFastAPIMode, []string{ + return NewPythonAppDetector(config.ContentTypePythonFastAPI, []string{ "fastapi", "falcon.asgi", "quart", @@ -41,25 +41,25 @@ func NewFastAPIDetector() *PythonAppDetector { } func NewDashDetector() *PythonAppDetector { - return NewPythonAppDetector(apptypes.PythonDashMode, []string{ + return NewPythonAppDetector(config.ContentTypePythonDash, []string{ "dash", // also matches dash_core_components, dash_bio, etc. }) } func NewStreamlitDetector() *PythonAppDetector { - return NewPythonAppDetector(apptypes.PythonStreamlitMode, []string{ + return NewPythonAppDetector(config.ContentTypePythonStreamlit, []string{ "streamlit", }) } func NewBokehDetector() *PythonAppDetector { - return NewPythonAppDetector(apptypes.PythonBokehMode, []string{ + return NewPythonAppDetector(config.ContentTypePythonBokeh, []string{ "bokeh", }) } func NewPyShinyDetector() *PythonAppDetector { - return NewPythonAppDetector(apptypes.PythonShinyMode, []string{ + return NewPythonAppDetector(config.ContentTypePythonShiny, []string{ "shiny", }) } @@ -77,7 +77,7 @@ func (d *PythonAppDetector) InferType(path util.Path) (*ContentType, error) { if matches { return &ContentType{ Entrypoint: entrypoint, - AppMode: d.appMode, + Type: d.contentType, RequiresPython: true, }, nil } diff --git a/internal/inspect/python_test.go b/internal/inspect/python_test.go index bff3443d6..c6bc49d4b 100644 --- a/internal/inspect/python_test.go +++ b/internal/inspect/python_test.go @@ -6,7 +6,7 @@ import ( "errors" "testing" - "github.com/rstudio/connect-client/internal/apptypes" + "github.com/rstudio/connect-client/internal/config" "github.com/rstudio/connect-client/internal/util" "github.com/rstudio/connect-client/internal/util/utiltest" "github.com/spf13/afero" @@ -32,7 +32,7 @@ func (s *PythonSuite) TestInferTypeSpecifiedFile() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.PythonAPIMode, + Type: config.ContentTypePythonFlask, Entrypoint: filename, RequiresPython: true, }, t) @@ -48,7 +48,7 @@ func (s *PythonSuite) TestInferTypePreferredFilename() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.PythonAPIMode, + Type: config.ContentTypePythonFlask, Entrypoint: filename, RequiresPython: true, }, t) @@ -64,7 +64,7 @@ func (s *PythonSuite) TestInferTypeOnlyPythonFile() { t, err := detector.InferType(path) s.Nil(err) s.Equal(&ContentType{ - AppMode: apptypes.PythonAPIMode, + Type: config.ContentTypePythonFlask, Entrypoint: filename, RequiresPython: true, }, t)