func TestValidate(t *testing.T) { step := StepModel{ Title: pointers.NewStringPtr("title"), Summary: pointers.NewStringPtr("summary"), Website: pointers.NewStringPtr("website"), PublishedAt: pointers.NewTimePtr(time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC)), Source: &StepSourceModel{ Git: "https://github.com/bitrise-io/bitrise.git", Commit: "1e1482141079fc12def64d88cb7825b8f1cb1dc3", }, } require.Equal(t, nil, step.Audit()) step.Title = nil require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'title' property") step.Title = new(string) *step.Title = "" require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'title' property") *step.Title = "title" step.PublishedAt = nil require.NotEqual(t, nil, step.Audit()) require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'PublishedAt' property") step.PublishedAt = new(time.Time) *step.PublishedAt = time.Time{} require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'PublishedAt' property") step.PublishedAt = pointers.NewTimePtr(time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC)) step.Website = nil require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'website' property") step.Website = new(string) *step.Website = "" require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'website' property") *step.Website = "website" step.Source.Git = "" require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'source.git' property") step.Source.Git = "git" step.Source.Git = "[email protected]:bitrise-io/bitrise.git" require.EqualError(t, step.Audit(), "Invalid step: step source should start with http:// or https://") step.Source.Git = "https://github.com/bitrise-io/bitrise" require.EqualError(t, step.Audit(), "Invalid step: step source should end with .git") step.Source.Git = "https://github.com/bitrise-io/bitrise.git" step.Source.Commit = "" require.EqualError(t, step.Audit(), "Invalid step: missing or empty required 'source.commit' property") step.Source.Commit = "1e1482141079fc12def64d88cb7825b8f1cb1dc3" step.Timeout = new(int) *step.Timeout = -1 require.EqualError(t, step.Audit(), "Invalid step: timeout less then 0") }
// Test - Stepman audit step // Checks if step Source.Commit meets the git commit hash of realese version // 'auditStep(...)' calls 'cmdex.GitCloneTagOrBranchAndValidateCommitHash(...)', which method validates the commit hash func TestValidateStepCommitHash(t *testing.T) { // Slack step - valid hash stepSlack := models.StepModel{ Title: pointers.NewStringPtr("hash_test"), Summary: pointers.NewStringPtr("summary"), Website: pointers.NewStringPtr("website"), PublishedAt: pointers.NewTimePtr(time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC)), Source: &models.StepSourceModel{ Git: "https://github.com/bitrise-io/steps-slack-message.git", Commit: "756f39f76f94d525aaea2fc2d0c5a23799f8ec97", }, } if err := auditStepModelBeforeSharePullRequest(stepSlack, "slack", "2.1.0"); err != nil { t.Fatal("Step audit failed:", err) } // Slack step - invalid hash stepSlack.Source.Commit = "should fail commit" if err := auditStepModelBeforeSharePullRequest(stepSlack, "slack", "2.1.0"); err == nil { t.Fatal("Step audit should fail") } // Slack step - empty hash stepSlack.Source.Commit = "" if err := auditStepModelBeforeSharePullRequest(stepSlack, "slack", "2.1.0"); err == nil { t.Fatal("Step audit should fail") } }
func TestValidate(t *testing.T) { step := StepModel{ Title: pointers.NewStringPtr("title"), Summary: pointers.NewStringPtr("summary"), Website: pointers.NewStringPtr("website"), PublishedAt: pointers.NewTimePtr(time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC)), Source: StepSourceModel{ Git: "https://github.com/bitrise-io/bitrise.git", Commit: "1e1482141079fc12def64d88cb7825b8f1cb1dc3", }, } require.Equal(t, nil, step.Audit()) step.Title = nil require.NotEqual(t, nil, step.Audit()) step.Title = new(string) *step.Title = "" require.NotEqual(t, nil, step.Audit()) step.PublishedAt = nil require.NotEqual(t, nil, step.Audit()) step.PublishedAt = new(time.Time) *step.PublishedAt = time.Time{} require.NotEqual(t, nil, step.Audit()) step.Description = nil require.NotEqual(t, nil, step.Audit()) step.Description = new(string) *step.Description = "" require.NotEqual(t, nil, step.Audit()) step.Website = nil require.NotEqual(t, nil, step.Audit()) step.Website = new(string) *step.Website = "" require.NotEqual(t, nil, step.Audit()) step.Source.Git = "" require.NotEqual(t, nil, step.Audit()) step.Source.Git = "[email protected]:bitrise-io/bitrise.git" require.NotEqual(t, nil, step.Audit()) step.Source.Git = "https://github.com/bitrise-io/bitrise" require.NotEqual(t, nil, step.Audit()) step.Source.Commit = "" require.NotEqual(t, nil, step.Audit()) }
func TestMergeStepWith(t *testing.T) { desc := "desc 1" summ := "sum 1" website := "web/1" fork := "fork/1" published := time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC) stepData := stepmanModels.StepModel{ Description: pointers.NewStringPtr(desc), Summary: pointers.NewStringPtr(summ), Website: pointers.NewStringPtr(website), SourceCodeURL: pointers.NewStringPtr(fork), PublishedAt: pointers.NewTimePtr(published), HostOsTags: []string{"osx"}, ProjectTypeTags: []string{"ios"}, TypeTags: []string{"test"}, IsRequiresAdminUser: pointers.NewBoolPtr(true), Inputs: []envmanModels.EnvironmentItemModel{ envmanModels.EnvironmentItemModel{ "KEY_1": "Value 1", }, envmanModels.EnvironmentItemModel{ "KEY_2": "Value 2", }, }, Outputs: []envmanModels.EnvironmentItemModel{}, } diffTitle := "name 2" newSuppURL := "supp" runIfStr := "" stepDiffToMerge := stepmanModels.StepModel{ Title: pointers.NewStringPtr(diffTitle), HostOsTags: []string{"linux"}, Source: &stepmanModels.StepSourceModel{ Git: "https://git.url", }, Dependencies: []stepmanModels.DependencyModel{ stepmanModels.DependencyModel{ Manager: "brew", Name: "test", }, }, SupportURL: pointers.NewStringPtr(newSuppURL), RunIf: pointers.NewStringPtr(runIfStr), Inputs: []envmanModels.EnvironmentItemModel{ envmanModels.EnvironmentItemModel{ "KEY_2": "Value 2 CHANGED", }, }, Timeout: pointers.NewIntPtr(1), Toolkit: &stepmanModels.StepToolkitModel{ Go: &stepmanModels.GoStepToolkitModel{ PackageName: "test", }, }, } mergedStepData, err := MergeStepWith(stepData, stepDiffToMerge) require.NoError(t, err) require.Equal(t, "name 2", *mergedStepData.Title) require.Equal(t, "desc 1", *mergedStepData.Description) require.Equal(t, "sum 1", *mergedStepData.Summary) require.Equal(t, "web/1", *mergedStepData.Website) require.Equal(t, "fork/1", *mergedStepData.SourceCodeURL) require.Equal(t, true, (*mergedStepData.PublishedAt).Equal(time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC))) require.Equal(t, "linux", mergedStepData.HostOsTags[0]) require.Equal(t, "", *mergedStepData.RunIf) require.Equal(t, 1, len(mergedStepData.Dependencies)) require.Equal(t, "test", mergedStepData.Toolkit.Go.PackageName) require.Equal(t, 1, *mergedStepData.Timeout) dep := mergedStepData.Dependencies[0] require.Equal(t, "brew", dep.Manager) require.Equal(t, "test", dep.Name) // inputs input0 := mergedStepData.Inputs[0] key0, value0, err := input0.GetKeyValuePair() require.NoError(t, err) require.Equal(t, "KEY_1", key0) require.Equal(t, "Value 1", value0) input1 := mergedStepData.Inputs[1] key1, value1, err := input1.GetKeyValuePair() require.NoError(t, err) require.Equal(t, "KEY_2", key1) require.Equal(t, "Value 2 CHANGED", value1) }
// MergeStepWith ... func MergeStepWith(step, otherStep stepmanModels.StepModel) (stepmanModels.StepModel, error) { if otherStep.Title != nil { step.Title = pointers.NewStringPtr(*otherStep.Title) } if otherStep.Description != nil { step.Description = pointers.NewStringPtr(*otherStep.Description) } if otherStep.Summary != nil { step.Summary = pointers.NewStringPtr(*otherStep.Summary) } if otherStep.Website != nil { step.Website = pointers.NewStringPtr(*otherStep.Website) } if otherStep.SourceCodeURL != nil { step.SourceCodeURL = pointers.NewStringPtr(*otherStep.SourceCodeURL) } if otherStep.SupportURL != nil { step.SupportURL = pointers.NewStringPtr(*otherStep.SupportURL) } if otherStep.PublishedAt != nil { step.PublishedAt = pointers.NewTimePtr(*otherStep.PublishedAt) } if otherStep.Source.Git != "" { step.Source.Git = otherStep.Source.Git } if otherStep.Source.Commit != "" { step.Source.Commit = otherStep.Source.Commit } if len(otherStep.Dependencies) > 0 { step.Dependencies = otherStep.Dependencies } if len(otherStep.Deps.Brew) > 0 || len(otherStep.Deps.AptGet) > 0 || len(otherStep.Deps.CheckOnly) > 0 { step.Deps = otherStep.Deps } if len(otherStep.HostOsTags) > 0 { step.HostOsTags = otherStep.HostOsTags } if len(otherStep.ProjectTypeTags) > 0 { step.ProjectTypeTags = otherStep.ProjectTypeTags } if len(otherStep.TypeTags) > 0 { step.TypeTags = otherStep.TypeTags } if otherStep.IsRequiresAdminUser != nil { step.IsRequiresAdminUser = pointers.NewBoolPtr(*otherStep.IsRequiresAdminUser) } if otherStep.IsAlwaysRun != nil { step.IsAlwaysRun = pointers.NewBoolPtr(*otherStep.IsAlwaysRun) } if otherStep.IsSkippable != nil { step.IsSkippable = pointers.NewBoolPtr(*otherStep.IsSkippable) } if otherStep.RunIf != nil { step.RunIf = pointers.NewStringPtr(*otherStep.RunIf) } for _, input := range step.Inputs { key, _, err := input.GetKeyValuePair() if err != nil { return stepmanModels.StepModel{}, err } otherInput, found := getInputByKey(otherStep, key) if found { err := MergeEnvironmentWith(&input, otherInput) if err != nil { return stepmanModels.StepModel{}, err } } } for _, output := range step.Outputs { key, _, err := output.GetKeyValuePair() if err != nil { return stepmanModels.StepModel{}, err } otherOutput, found := getOutputByKey(otherStep, key) if found { err := MergeEnvironmentWith(&output, otherOutput) if err != nil { return stepmanModels.StepModel{}, err } } } return step, nil }
func create(c *cli.Context) error { toolMode := c.Bool(ToolMode) share, err := ReadShareSteplibFromFile() if err != nil { log.Error(err) log.Fatalln("You have to start sharing with `stepman share start`, or you can read instructions with `stepman share`") } // Input validation tag := c.String(TagKey) if tag == "" { log.Fatalln("No Step tag specified") } gitURI := c.String(GitKey) if gitURI == "" { log.Fatalln("No Step url specified") } stepID := c.String(StepIDKEy) if stepID == "" { stepID = getStepIDFromGit(gitURI) } if stepID == "" { log.Fatalln("No Step id specified") } r := regexp.MustCompile(`[a-z0-9-]+`) if find := r.FindString(stepID); find != stepID { log.Fatalln("StepID doesn't conforms to: [a-z0-9-]") } route, found := stepman.ReadRoute(share.Collection) if !found { log.Fatalf("No route found for collectionURI (%s)", share.Collection) } stepDirInSteplib := stepman.GetStepCollectionDirPath(route, stepID, tag) stepYMLPathInSteplib := path.Join(stepDirInSteplib, "step.yml") if exist, err := pathutil.IsPathExists(stepYMLPathInSteplib); err != nil { log.Fatalf("Failed to check step.yml path in steplib, err: %s", err) } else if exist { log.Warnf("Step already exist in path: %s.", stepDirInSteplib) if val, err := goinp.AskForBool("Would you like to overwrite local version of Step?"); err != nil { log.Fatalf("Failed to get bool, err: %s", err) } else { if !val { log.Errorln("Unfortunately we can't continue with sharing without an overwrite exist step.yml.") log.Fatalln("Please finish your changes, run this command again and allow it to overwrite the exist step.yml!") } } } // Clone Step to tmp dir tmp, err := pathutil.NormalizedOSTempDirPath("") if err != nil { log.Fatalf("Failed to get temp directory, err: %s", err) } log.Infof("Cloning Step from (%s) with tag (%s) to temporary path (%s)", gitURI, tag, tmp) if err := cmdex.GitCloneTag(gitURI, tmp, tag); err != nil { log.Fatalf("Git clone failed, err: %s", err) } // Update step.yml tmpStepYMLPath := path.Join(tmp, "step.yml") bytes, err := fileutil.ReadBytesFromFile(tmpStepYMLPath) if err != nil { log.Fatalf("Failed to read Step from file, err: %s", err) } var stepModel models.StepModel if err := yaml.Unmarshal(bytes, &stepModel); err != nil { log.Fatalf("Failed to unmarchal Step, err: %s", err) } commit, err := cmdex.GitGetCommitHashOfHEAD(tmp) if err != nil { log.Fatalf("Failed to get commit hash, err: %s", err) } stepModel.Source = &models.StepSourceModel{ Git: gitURI, Commit: commit, } stepModel.PublishedAt = pointers.NewTimePtr(time.Now()) // Validate step-yml if err := stepModel.Audit(); err != nil { log.Fatalf("Failed to validate Step, err: %s", err) } for _, input := range stepModel.Inputs { key, value, err := input.GetKeyValuePair() if err != nil { log.Fatalf("Failed to get Step input key-value pair, err: %s", err) } options, err := input.GetOptions() if err != nil { log.Fatalf("Failed to get Step input (%s) options, err: %s", key, err) } if len(options.ValueOptions) > 0 && value == "" { log.Warn("Step input with 'value_options', should contain default value!") log.Fatalf("Missing default value for Step input (%s).", key) } } if strings.Contains(*stepModel.Summary, "\n") { log.Warningln("Step summary should be one line!") } if utf8.RuneCountInString(*stepModel.Summary) > maxSummaryLength { log.Warningf("Step summary should contains maximum (%d) characters, actual: (%d)!", maxSummaryLength, utf8.RuneCountInString(*stepModel.Summary)) } // Copy step.yml to steplib share.StepID = stepID share.StepTag = tag if err := WriteShareSteplibToFile(share); err != nil { log.Fatalf("Failed to save share steplib to file, err: %s", err) } log.Info("Step dir in collection:", stepDirInSteplib) if exist, err := pathutil.IsPathExists(stepDirInSteplib); err != nil { log.Fatalf("Failed to check path (%s), err: %s", stepDirInSteplib, err) } else if !exist { if err := os.MkdirAll(stepDirInSteplib, 0777); err != nil { log.Fatalf("Failed to create path (%s), err: %s", stepDirInSteplib, err) } } log.Infof("Checkout branch: %s", share.ShareBranchName()) collectionDir := stepman.GetCollectionBaseDirPath(route) if err := cmdex.GitCheckout(collectionDir, share.ShareBranchName()); err != nil { if err := cmdex.GitCreateAndCheckoutBranch(collectionDir, share.ShareBranchName()); err != nil { log.Fatalf("Git failed to create and checkout branch, err: %s", err) } } stepBytes, err := yaml.Marshal(stepModel) if err != nil { log.Fatalf("Failed to marcshal Step model, err: %s", err) } if err := fileutil.WriteBytesToFile(stepYMLPathInSteplib, stepBytes); err != nil { log.Fatalf("Failed to write Step to file, err: %s", err) } // Update spec.json if err := stepman.ReGenerateStepSpec(route); err != nil { log.Fatalf("Failed to re-create steplib, err: %s", err) } printFinishCreate(share, stepDirInSteplib, toolMode) return nil }
func TestRemoveStepRedundantFields(t *testing.T) { step := stepmanModels.StepModel{ Title: pointers.NewStringPtr(""), Description: pointers.NewStringPtr(""), Summary: pointers.NewStringPtr(""), Website: pointers.NewStringPtr(""), SourceCodeURL: pointers.NewStringPtr(""), SupportURL: pointers.NewStringPtr(""), PublishedAt: pointers.NewTimePtr(time.Time{}), Source: stepmanModels.StepSourceModel{ Git: "", Commit: "", }, HostOsTags: []string{}, ProjectTypeTags: []string{}, TypeTags: []string{}, Dependencies: []stepmanModels.DependencyModel{ stepmanModels.DependencyModel{ Manager: "", Name: "", }, }, IsRequiresAdminUser: pointers.NewBoolPtr(stepmanModels.DefaultIsRequiresAdminUser), IsAlwaysRun: pointers.NewBoolPtr(stepmanModels.DefaultIsAlwaysRun), IsSkippable: pointers.NewBoolPtr(stepmanModels.DefaultIsSkippable), RunIf: pointers.NewStringPtr(""), Inputs: []envmanModels.EnvironmentItemModel{ envmanModels.EnvironmentItemModel{ "IN": "in", envmanModels.OptionsKey: envmanModels.EnvironmentItemOptionsModel{ Title: pointers.NewStringPtr(""), }, }, }, Outputs: []envmanModels.EnvironmentItemModel{ envmanModels.EnvironmentItemModel{ "OUT": "", envmanModels.OptionsKey: envmanModels.EnvironmentItemOptionsModel{ Description: pointers.NewStringPtr(""), }, }, }, } if err := removeStepRedundantFields(&step); err != nil { t.Fatal("Failed to remove redundant fields:", err) } if step.Title != nil { t.Fatal("step.Title should be nil") } if step.Description != nil { t.Fatal("step.Description should be nil") } if step.Summary != nil { t.Fatal("step.Summary should be nil") } if step.Website != nil { t.Fatal("step.Website should be nil") } if step.SourceCodeURL != nil { t.Fatal("step.SourceCodeURL should be nil") } if step.PublishedAt != nil { t.Fatal("step.PublishedAt should be nil") } if step.SupportURL != nil { t.Fatal("step.SupportURL should be nil") } if step.PublishedAt != nil { t.Fatal("step.PublishedAt should be nil") } if step.Source.Git != "" || step.Source.Commit != "" { t.Fatal("step.Source.Git && step.Source.Commit should be empty") } if len(step.HostOsTags) != 0 { t.Fatal("len(step.HostOsTags) should be 0") } if len(step.ProjectTypeTags) != 0 { t.Fatal("len(step.ProjectTypeTags) should be 0") } if len(step.TypeTags) != 0 { t.Fatal("len(step.TypeTags) should be 0") } if step.IsRequiresAdminUser != nil { t.Fatal("step.IsRequiresAdminUser should be nil") } if step.IsAlwaysRun != nil { t.Fatal("step.IsAlwaysRun should be nil") } if step.IsSkippable != nil { t.Fatal("step.IsSkippable should be nil") } if step.RunIf != nil { t.Fatal("step.RunIf should be nil") } for _, input := range step.Inputs { options, err := input.GetOptions() if err != nil { t.Fatal("Failed to get env options:", err) } if options.Title != nil { t.Fatal("options.Title should be nil") } if options.Description != nil { t.Fatal("options.Description should be nil") } if options.Summary != nil { t.Fatal("options.Summary should be nil") } if len(options.ValueOptions) != 0 { t.Fatal("options.ValueOptions should be empty") } if options.IsRequired != nil { t.Fatal("options.IsRequired should be nil") } if options.IsExpand != nil { t.Fatal("options.IsExpand should be nil") } if options.IsDontChangeValue != nil { t.Fatal("options.IsDontChangeValue should be nil") } } for _, output := range step.Outputs { options, err := output.GetOptions() if err != nil { t.Fatal("Failed to get env options:", err) } if options.Title != nil { t.Fatal("options.Title should be nil") } if options.Description != nil { t.Fatal("options.Description should be nil") } if options.Summary != nil { t.Fatal("options.Summary should be nil") } if len(options.ValueOptions) != 0 { t.Fatal("options.ValueOptions should be empty") } if options.IsRequired != nil { t.Fatal("options.IsRequired should be nil") } if options.IsExpand != nil { t.Fatal("options.IsExpand should be nil") } if options.IsDontChangeValue != nil { t.Fatal("options.IsDontChangeValue should be nil") } } }
func TestValidate(t *testing.T) { step := StepModel{ Title: pointers.NewStringPtr("title"), Summary: pointers.NewStringPtr("summary"), Website: pointers.NewStringPtr("website"), PublishedAt: pointers.NewTimePtr(time.Date(2012, time.January, 1, 0, 0, 0, 0, time.UTC)), Source: StepSourceModel{ Git: "https://github.com/bitrise-io/bitrise.git", Commit: "1e1482141079fc12def64d88cb7825b8f1cb1dc3", }, } if err := step.Validate(true); err != nil { t.Fatal(err) } step.Title = nil if err := step.Validate(true); err == nil { t.Fatal("Invalid step: no Title defined") } step.Title = new(string) *step.Title = "" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: empty Title") } step.PublishedAt = nil if err := step.Validate(true); err == nil { t.Fatal("Invalid step: no Title defined") } step.PublishedAt = new(time.Time) *step.PublishedAt = time.Time{} if err := step.Validate(true); err == nil { t.Fatal("Invalid step: empty Title") } step.Description = nil if err := step.Validate(true); err == nil { t.Fatal("Invalid step: no Description defined") } step.Description = new(string) *step.Description = "" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: empty Description") } step.Website = nil if err := step.Validate(true); err == nil { t.Fatal("Invalid step: no Website defined") } step.Website = new(string) *step.Website = "" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: empty Website") } step.Source.Git = "" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: empty Source.Git") } step.Source.Git = "[email protected]:bitrise-io/bitrise.git" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: Source.Git has invalid prefix") } step.Source.Git = "https://github.com/bitrise-io/bitrise" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: Source.Git has invalid suffix") } step.Source.Commit = "" if err := step.Validate(true); err == nil { t.Fatal("Invalid step: empty Source.Commit") } }