diff --git a/cmd/bundle.go b/cmd/bundle.go index 8bdee49f..0c03888d 100644 --- a/cmd/bundle.go +++ b/cmd/bundle.go @@ -40,6 +40,7 @@ type bundleNew struct { description string templateName string connections []string + artifacts []string outputDir string paramsDir string } @@ -88,6 +89,7 @@ func NewCmdBundle() *cobra.Command { bundleNewCmd.Flags().StringVarP(&bundleNewInput.name, "name", "n", "", "Name of the new bundle. Setting this along with --template-name will disable the interactive prompt.") bundleNewCmd.Flags().StringVarP(&bundleNewInput.description, "description", "d", "", "Description of the new bundle") bundleNewCmd.Flags().StringVarP(&bundleNewInput.templateName, "template-name", "t", "", "Name of the bundle template to use. Setting this along with --name will disable the interactive prompt.") + bundleNewCmd.Flags().StringSliceVarP(&bundleNewInput.artifacts, "artifacts", "a", []string{}, "Artifacts and names to add to the bundle - example: network=massdriver/vpc") bundleNewCmd.Flags().StringSliceVarP(&bundleNewInput.connections, "connections", "c", []string{}, "Connections and names to add to the bundle - example: network=massdriver/vpc") bundleNewCmd.Flags().StringVarP(&bundleNewInput.outputDir, "output-directory", "o", ".", "Directory to output the new bundle") bundleNewCmd.Flags().StringVarP(&bundleNewInput.paramsDir, "params-directory", "p", "", "Path with existing params to use - opentofu module directory or helm chart values.yaml") @@ -209,6 +211,18 @@ func runBundleNewFlags(input *bundleNew) (*templatecache.TemplateData, error) { } } + artifactData := make([]templatecache.Artifact, len(input.artifacts)) + for i, art := range input.artifacts { + parts := strings.Split(art, "=") + if len(parts) != 2 { + return nil, fmt.Errorf("invalid connection argument: %s", art) + } + artifactData[i] = templatecache.Artifact{ + ArtifactDefinition: parts[1], + Name: parts[0], + } + } + templateData := &templatecache.TemplateData{ TemplateRepo: "/massdriver-cloud/application-templates", OutputDir: input.outputDir, @@ -216,6 +230,7 @@ func runBundleNewFlags(input *bundleNew) (*templatecache.TemplateData, error) { Description: input.description, TemplateName: input.templateName, Connections: connectionData, + Artifacts: artifactData, ExistingParamsPath: input.paramsDir, } diff --git a/docs/generated/mass_bundle_new.md b/docs/generated/mass_bundle_new.md index b73449e5..edee1609 100644 --- a/docs/generated/mass_bundle_new.md +++ b/docs/generated/mass_bundle_new.md @@ -41,6 +41,7 @@ mass bundle new [flags] ### Options ``` + -a, --artifacts strings Artifacts and names to add to the bundle - example: network=massdriver/vpc -c, --connections strings Connections and names to add to the bundle - example: network=massdriver/vpc -d, --description string Description of the new bundle -h, --help help for new diff --git a/pkg/bundle/prompt.go b/pkg/bundle/prompt.go index cea39ab5..6534c86d 100644 --- a/pkg/bundle/prompt.go +++ b/pkg/bundle/prompt.go @@ -18,13 +18,13 @@ import ( var ( // These look eerily similar, the difference being - vs _ - baseRegex = "^[a-z]+[a-z0-9%s]*[a-z0-9]+$" - bundleNameFormat = regexp.MustCompile(fmt.Sprintf(baseRegex, "-")) - connectionNameFormat = regexp.MustCompile(fmt.Sprintf(baseRegex, "_")) + baseRegex = "^[a-z]+[a-z0-9%s]*[a-z0-9]+$" + bundleNameFormat = regexp.MustCompile(fmt.Sprintf(baseRegex, "-")) + linkNameFormat = regexp.MustCompile(fmt.Sprintf(baseRegex, "_")) baseNameError = "name must be 2 to 53 characters, can only include lowercase letters, numbers and %s, must start with a letter and end with an alphanumeric character [abc%s123, my%scool%sthing]" bundleNameError = fmt.Sprintf(baseNameError, "dashes", "-", "-", "-") - connNameError = fmt.Sprintf(baseNameError, "underscores", "_", "_", "_") + linkNameError = fmt.Sprintf(baseNameError, "underscores", "_", "_", "_") ) var massdriverArtifactDefinitions map[string]map[string]any @@ -33,7 +33,8 @@ var promptsNew = []func(t *templatecache.TemplateData) error{ getName, getDescription, getTemplate, - GetConnections, + getConnections, + getArtifacts, getOutputDir, } @@ -138,17 +139,17 @@ func getTemplate(t *templatecache.TemplateData) error { return nil } -func connNameValidate(name string) error { +func linkNameValidate(name string) error { if len(name) < 2 || len(name) > 53 { - return errors.New(connNameError) + return errors.New(linkNameError) } - if !connectionNameFormat.MatchString(name) { - return errors.New(connNameError) + if !linkNameFormat.MatchString(name) { + return errors.New(linkNameError) } return nil } -func GetConnections(t *templatecache.TemplateData) error { +func getConnections(t *templatecache.TemplateData) error { none := "(None)" artifactDefinitionsTypes := []string{} @@ -175,7 +176,7 @@ func GetConnections(t *templatecache.TemplateData) error { for _, currentDep := range selectedDeps { if currentDep == none { if len(selectedDeps) > 1 { - return fmt.Errorf("if selecting %v, you cannot select other dependecies. selected %#v", none, selectedDeps) + return fmt.Errorf("if selecting %v, you cannot select other connections. selected %#v", none, selectedDeps) } return nil } @@ -184,7 +185,7 @@ func GetConnections(t *templatecache.TemplateData) error { prompt := promptui.Prompt{ Label: `Name`, - Validate: connNameValidate, + Validate: linkNameValidate, } result, errName := prompt.Run() @@ -202,6 +203,56 @@ func GetConnections(t *templatecache.TemplateData) error { return nil } +func getArtifacts(t *templatecache.TemplateData) error { + none := "(None)" + + artifactDefinitionsTypes := []string{} + // in 1.23 we can use maps.Keys(), but until then we'll extract the keys manually + for adt := range massdriverArtifactDefinitions { + artifactDefinitionsTypes = append(artifactDefinitionsTypes, adt) + } + sort.StringSlice(artifactDefinitionsTypes).Sort() + + var selectedArts []string + multiselect := &survey.MultiSelect{ + Message: "What artifacts do you need?\n If you don't need any, just hit enter or select (None)\n", + Options: artifactDefinitionsTypes, + } + + err := survey.AskOne(multiselect, &selectedArts) + if err != nil { + return err + } + + var artMap []templatecache.Artifact + + for _, currentArt := range selectedArts { + if currentArt == none { + if len(selectedArts) > 1 { + return fmt.Errorf("if selecting %v, you cannot select other artifacts. selected %#v", none, selectedArts) + } + return nil + } + + fmt.Printf("Please enter a name for the artifact: \"%v\"\nThis will be the variable name used to reference it in your app|bundle IaC\n", currentArt) + + prompt := promptui.Prompt{ + Label: `Name`, + Validate: linkNameValidate, + } + + result, errName := prompt.Run() + if errName != nil { + return errName + } + + artMap = append(artMap, templatecache.Artifact{Name: result, ArtifactDefinition: currentArt}) + } + + t.Artifacts = artMap + return nil +} + func removeIgnoredTemplateDirectories(templates []templatecache.TemplateList) []string { filteredTemplates := []string{} for _, repo := range templates { diff --git a/pkg/bundle/prompt_test.go b/pkg/bundle/prompt_test.go index 7e10628e..1216d65f 100644 --- a/pkg/bundle/prompt_test.go +++ b/pkg/bundle/prompt_test.go @@ -32,13 +32,13 @@ func TestNameValidate(t *testing.T) { } } -func TestConnNameValidate(t *testing.T) { +func TestLinkNameValidate(t *testing.T) { goodValues := []string{ "ab", "abc", "a1", "a_1", "a__1__2__b", strings.Repeat("a", 53), } for _, val := range goodValues { - if err := connNameValidate(val); err != nil { + if err := linkNameValidate(val); err != nil { t.Errorf("expected no error for '%s': %v", val, err) } } @@ -52,7 +52,7 @@ func TestConnNameValidate(t *testing.T) { "1111", "1-1-1", "----", strings.Repeat("a", 54), } for _, val := range badValues { - if err := connNameValidate(val); err == nil { + if err := linkNameValidate(val); err == nil { t.Errorf("expected error for '%s'", val) } } diff --git a/pkg/bundle/publish_test.go b/pkg/bundle/publish_test.go index 28048fba..6853b9b5 100644 --- a/pkg/bundle/publish_test.go +++ b/pkg/bundle/publish_test.go @@ -77,7 +77,7 @@ func TestPackageBundle(t *testing.T) { } if len(manifest.Layers) != len(tc.expectedLayers) { - for title, _ := range tc.expectedLayers { + for title := range tc.expectedLayers { found := false for _, layer := range manifest.Layers { if layer.Annotations[ocispec.AnnotationTitle] == title { diff --git a/pkg/templatecache/template_cache.go b/pkg/templatecache/template_cache.go index b4e2cd89..a469de96 100644 --- a/pkg/templatecache/template_cache.go +++ b/pkg/templatecache/template_cache.go @@ -16,6 +16,7 @@ type TemplateData struct { OutputDir string `json:"outputDir"` Type string `json:"type"` Connections []Connection `json:"connections"` + Artifacts []Artifact `json:"artifacts"` Envs map[string]string `json:"envs"` // ParamsSchema is a YAML formatted string @@ -29,6 +30,11 @@ type TemplateData struct { RepoNameEncoded string `json:"repoNameEncoded"` } +type Artifact struct { + Name string `json:"name"` + ArtifactDefinition string `json:"artifact_definition"` +} + type Connection struct { Name string `json:"name"` ArtifactDefinition string `json:"artifact_definition"`