// InstallWithAptGetIfNeeded ... func InstallWithAptGetIfNeeded(tool string, isCIMode bool) error { if out, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("which", tool); err != nil { if err.Error() == "exit status 1" && out == "" { // Tool isn't installed -- install it... if !isCIMode { log.Infof("This step requires %s, which is not installed", tool) allow, err := goinp.AskForBool("Would you like to install (" + tool + ") with brew ? [yes/no]") if err != nil { return err } if !allow { return errors.New("(" + tool + ") is required for step") } } log.Infof("(%s) isn't installed, installing...", tool) if out, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("sudo", "apt-get", "-y", "install", tool); err != nil { log.Errorf("sudo apt-get -y install %s failed -- out: (%s) err: (%s)", tool, out, err) return err } log.Infof(" * "+colorstring.Green("[OK]")+" %s installed", tool) } else { // which failed log.Errorf("which (%s) failed -- out: (%s) err: (%s)", tool, out, err) return err } } else if out != "" { // already installed } else { log.Warnf("which (%s) -- out (%s)", tool, out) } return nil }
func checkIsAnsibleInstalled() error { progInstallPth, err := checkProgramInstalledPath("ansible") if err != nil { officialSiteURL := "http://www.ansible.com/home" officialGitHubURL := "https://github.com/ansible/ansible" log.Infoln("") log.Infoln("Ansible was not found.") log.Infoln("Ansible is used for system provisioning.") log.Infoln("You can find more information on Ansible's official website:", officialSiteURL) log.Infoln(" or on it's GitHub page:", officialGitHubURL) log.Infoln("You can install Ansible through brew:") log.Infoln("$ brew update && brew install ansible") isInstall, err := goinp.AskForBool("Would you like to install Ansible right now?") if err != nil { return err } if !isInstall { return errors.New("Ansible not found and install was not initiated.") } log.Infoln("$ brew update --verbose") if err := bitrise.RunCommand("brew", "update", "--verbose"); err != nil { return err } log.Infoln("$ brew install ansible") if err := bitrise.RunCommand("brew", "install", "ansible"); err != nil { return err } // just check again return checkIsAnsibleInstalled() } log.Infoln(" * [OK] Ansible :", progInstallPth) return nil }
func doInit(c *cli.Context) { log.Info("[BITRISE_CLI] - Init -- Work-in-progress!") bitriseConfigFileRelPath := "./bitrise.yml" if exists, err := pathutil.IsPathExists(bitriseConfigFileRelPath); err != nil { log.Fatalln("Error:", err) } else if exists { ask := fmt.Sprintf("A config file already exists at %s - do you want to overwrite it?", bitriseConfigFileRelPath) if val, err := goinp.AskForBool(ask); err != nil { log.Fatalln("Error:", err) } else if val == false { log.Infoln("Init canceled, existing file won't be overwritten.") os.Exit(0) } } projectSettingsEnvs := []models.EnvironmentItemModel{} if val, err := goinp.AskForString("What's the BITRISE_PROJECT_TITLE?"); err != nil { log.Fatalln(err) } else { projectSettingsEnvs = append(projectSettingsEnvs, models.EnvironmentItemModel{"BITRISE_PROJECT_TITLE": val, "is_expand": "no"}) } if val, err := goinp.AskForString("What's your primary development branch's name?"); err != nil { log.Fatalln(err) } else { projectSettingsEnvs = append(projectSettingsEnvs, models.EnvironmentItemModel{"BITRISE_DEV_BRANCH": val, "is_expand": "no"}) } // TODO: // generate a couple of base steps // * timestamp gen // * bash script bitriseConf := models.BitriseConfigModel{ FormatVersion: "1.0.0", // TODO: move this into a project config file! App: models.AppModel{ Environments: projectSettingsEnvs, }, Workflows: map[string]models.WorkflowModel{ "primary": models.WorkflowModel{}, }, } if err := SaveToFile(bitriseConfigFileRelPath, bitriseConf); err != nil { log.Fatalln("Failed to init:", err) } os.Exit(1) }
// InstallWithAptGetIfNeeded ... func InstallWithAptGetIfNeeded(aptGetDep stepmanModels.AptGetDepModel, isCIMode bool) error { isDepInstalled := false // First do a "which", to see if the binary is available. // Can be available from another source, not just from brew, // e.g. it's common to use NVM or similar to install and manage the Node.js version. if out, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("which", aptGetDep.GetBinaryName()); err != nil { if err.Error() == "exit status 1" && out == "" { isDepInstalled = false } else { // unexpected `which` error return fmt.Errorf("which (%s) failed -- out: (%s) err: (%s)", aptGetDep.Name, out, err) } } else if out != "" { isDepInstalled = true } else { // no error but which's output was empty return fmt.Errorf("which (%s) failed -- no error (exit code 0) but output was empty", aptGetDep.Name) } if !isDepInstalled { // which did not find the binary, also check in brew, // whether the package is installed isDepInstalled = checkIfAptPackageInstalled(aptGetDep.Name) } if !isDepInstalled { // Tool isn't installed -- install it... if !isCIMode { log.Infof("This step requires %s, which is not installed", aptGetDep.Name) allow, err := goinp.AskForBool("Would you like to install (" + aptGetDep.Name + ") with apt-get?") if err != nil { return err } if !allow { return errors.New("(" + aptGetDep.Name + ") is required for step") } } log.Infof("(%s) isn't installed, installing...", aptGetDep.Name) if out, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("sudo", "apt-get", "-y", "install", aptGetDep.Name); err != nil { log.Errorf("sudo apt-get -y install %s failed -- out: (%s) err: (%s)", aptGetDep.Name, out, err) return err } log.Infof(" * "+colorstring.Green("[OK]")+" %s installed", aptGetDep.Name) } return nil }
func saveSecretsToFile(pth, secretsStr string) (bool, error) { if exists, err := pathutil.IsPathExists(pth); err != nil { return false, err } else if exists { ask := fmt.Sprintf("A secrets file already exists at %s - do you want to overwrite it?", pth) if val, err := goinp.AskForBool(ask); err != nil { return false, err } else if !val { log.Infoln("Init canceled, existing file (" + pth + ") won't be overwritten.") return false, nil } } if err := fileutil.WriteStringToFile(pth, secretsStr); err != nil { return false, err } return true, nil }
func main() { retStr, err := goinp.AskForString("Please enter some text here") if err != nil { log.Fatalln("Error:", err) } fmt.Println("Entered text was:", retStr) retInt, err := goinp.AskForInt("Please enter a number") if err != nil { log.Fatalln("Error:", err) } fmt.Println("Entered:", retInt) retBool, err := goinp.AskForBool("Yes or no?") if err != nil { log.Fatalln("Error:", err) } fmt.Println("Entered:", retBool) }
// InstallWithBrewIfNeeded ... func InstallWithBrewIfNeeded(tool string, isCIMode bool) error { if err := checkWithBrewProgramInstalled(tool); err != nil { if !isCIMode { log.Infof("This step requires %s, which is not installed", tool) allow, err := goinp.AskForBool("Would you like to install (" + tool + ") with brew ? [yes/no]") if err != nil { return err } if !allow { return errors.New("(" + tool + ") is required for step") } } log.Infof("(%s) isn't installed, installing...", tool) if out, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("brew", "install", tool); err != nil { log.Errorf("brew install %s failed -- out: (%s) err: (%s)", tool, out, err) return err } log.Infof(" * "+colorstring.Green("[OK]")+" %s installed", tool) return nil } return nil }
func initConfig(c *cli.Context) error { PrintBitriseHeaderASCIIArt(c.App.Version) bitriseConfigFileRelPath := "./" + DefaultBitriseConfigFileName bitriseSecretsFileRelPath := "./" + DefaultSecretsFileName if exists, err := pathutil.IsPathExists(bitriseConfigFileRelPath); err != nil { log.Fatalf("Failed to init path (%s), error: %s", bitriseConfigFileRelPath, err) } else if exists { ask := fmt.Sprintf("A config file already exists at %s - do you want to overwrite it?", bitriseConfigFileRelPath) if val, err := goinp.AskForBool(ask); err != nil { log.Fatalf("Failed to ask for input, error: %s", err) } else if !val { log.Info("Init canceled, existing file won't be overwritten.") os.Exit(0) } } userInputProjectTitle := "" userInputDevBranch := "" if val, err := goinp.AskForString("What's the BITRISE_APP_TITLE?"); err != nil { log.Fatalf("Failed to ask for input, error: %s", err) } else { userInputProjectTitle = val } if val, err := goinp.AskForString("What's your development branch's name?"); err != nil { log.Fatalf("Failed to ask for input, error: %s", err) } else { userInputDevBranch = val } bitriseConfContent, warnings, err := generateBitriseYMLContent(userInputProjectTitle, userInputDevBranch) for _, warning := range warnings { log.Warnf("warning: %s", warning) } if err != nil { log.Fatalf("Invalid Bitrise YML, error: %s", err) } if err := fileutil.WriteStringToFile(bitriseConfigFileRelPath, bitriseConfContent); err != nil { log.Fatalf("Failed to init the bitrise config file, error: %s", err) } else { fmt.Println() fmt.Println("# NOTES about the " + DefaultBitriseConfigFileName + " config file:") fmt.Println() fmt.Println("We initialized a " + DefaultBitriseConfigFileName + " config file for you.") fmt.Println("If you're in this folder you can use this config file") fmt.Println(" with bitrise automatically, you don't have to") fmt.Println(" specify it's path.") fmt.Println() } if initialized, err := saveSecretsToFile(bitriseSecretsFileRelPath, defaultSecretsContent); err != nil { log.Fatalf("Failed to init the secrets file, error: %s", err) } else if initialized { fmt.Println() fmt.Println("# NOTES about the " + DefaultSecretsFileName + " secrets file:") fmt.Println() fmt.Println("We also created a " + DefaultSecretsFileName + " file") fmt.Println(" in this directory, to keep your passwords, absolute path configurations") fmt.Println(" and other secrets separate from your") fmt.Println(" main configuration file.") fmt.Println("This way you can safely commit and share your configuration file") fmt.Println(" and ignore this secrets file, so nobody else will") fmt.Println(" know about your secrets.") fmt.Println(colorstring.Yellow("You should NEVER commit this secrets file into your repository!!")) fmt.Println() } // add the general .bitrise* item // which will include both secret files like .bitrise.secrets.yml // and the .bitrise work temp dir if err := addToGitignore(".bitrise*"); err != nil { log.Fatalf("Failed to add .gitignore pattern, error: %s", err) } fmt.Println(colorstring.Green("For your convenience we added the pattern '.bitrise*' to your .gitignore file")) fmt.Println(" to make it sure that no secrets or temporary work directories will be") fmt.Println(" committed into your repository.") fmt.Println() fmt.Println("Hurray, you're good to go!") fmt.Println("You can simply run:") fmt.Println("-> bitrise run test") fmt.Println("to test the sample configuration (which contains") fmt.Println("an example workflow called 'test').") fmt.Println() fmt.Println("Once you tested this sample setup you can") fmt.Println(" open the " + DefaultBitriseConfigFileName + " config file,") fmt.Println(" modify it and then run a workflow with:") fmt.Println("-> bitrise run YOUR-WORKFLOW-NAME") fmt.Println(" or trigger a build with a pattern:") fmt.Println("-> bitrise trigger YOUR/PATTERN") return 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 start(c *cli.Context) error { // Input validation toolMode := c.Bool(ToolMode) collectionURI := c.String(CollectionKey) if collectionURI == "" { log.Fatalln("[STEPMAN] - No step collection specified") } if route, found := stepman.ReadRoute(collectionURI); found { collLocalPth := stepman.GetCollectionBaseDirPath(route) log.Warnf("StepLib found locally at: %s", collLocalPth) log.Info("For sharing it's required to work with a clean StepLib repository.") if val, err := goinp.AskForBool("Would you like to remove the local version (your forked StepLib repository) and re-clone it?"); err != nil { log.Fatalln(err) } else { if !val { log.Errorln("Unfortunately we can't continue with sharing without a clean StepLib repository.") log.Fatalln("Please finish your changes, run this command again and allow it to remove the local StepLib folder!") } if err := stepman.CleanupRoute(route); err != nil { log.Errorf("Failed to cleanup route for uri: %s", collectionURI) } } } // cleanup if err := DeleteShareSteplibFile(); err != nil { log.Fatal(err) } var route stepman.SteplibRoute isSuccess := false defer func() { if !isSuccess { if err := stepman.CleanupRoute(route); err != nil { log.Errorf("Failed to cleanup route for uri: %s", collectionURI) } if err := DeleteShareSteplibFile(); err != nil { log.Fatal(err) } } }() // Preparing steplib alias := stepman.GenerateFolderAlias() route = stepman.SteplibRoute{ SteplibURI: collectionURI, FolderAlias: alias, } pth := stepman.GetCollectionBaseDirPath(route) if err := cmdex.GitClone(collectionURI, pth); err != nil { log.Fatal("[STEPMAN] - Failed to setup step spec:", err) } specPth := pth + "/steplib.yml" collection, err := stepman.ParseStepCollection(specPth) if err != nil { log.Fatal("[STEPMAN] - Failed to read step spec:", err) } if err := stepman.WriteStepSpecToFile(collection, route); err != nil { log.Fatal("[STEPMAN] - Failed to save step spec:", err) } if err := stepman.AddRoute(route); err != nil { log.Fatal("[STEPMAN] - Failed to setup routing:", err) } share := ShareModel{ Collection: collectionURI, } if err := WriteShareSteplibToFile(share); err != nil { log.Fatal("[STEPMAN] - Failed to save share steplib to file:", err) } isSuccess = true printFinishStart(pth, toolMode) return nil }
func doInit(c *cli.Context) { PrintBitriseHeaderASCIIArt() bitriseConfigFileRelPath := "./" + DefaultBitriseConfigFileName bitriseSecretsFileRelPath := "./" + DefaultSecretsFileName if exists, err := pathutil.IsPathExists(bitriseConfigFileRelPath); err != nil { log.Fatalln("Error:", err) } else if exists { ask := fmt.Sprintf("A config file already exists at %s - do you want to overwrite it?", bitriseConfigFileRelPath) if val, err := goinp.AskForBool(ask); err != nil { log.Fatalln("Error:", err) } else if !val { log.Infoln("Init canceled, existing file won't be overwritten.") os.Exit(0) } } defaultExpand := true projectSettingsEnvs := []envmanModels.EnvironmentItemModel{} if val, err := goinp.AskForString("What's the BITRISE_PROJECT_TITLE?"); err != nil { log.Fatalln(err) } else { projectTitleEnv := envmanModels.EnvironmentItemModel{ "BITRISE_PROJECT_TITLE": val, "opts": envmanModels.EnvironmentItemOptionsModel{ IsExpand: &defaultExpand, }, } projectSettingsEnvs = append(projectSettingsEnvs, projectTitleEnv) } if val, err := goinp.AskForString("What's your primary development branch's name?"); err != nil { log.Fatalln(err) } else { devBranchEnv := envmanModels.EnvironmentItemModel{ "BITRISE_DEV_BRANCH": val, "opts": envmanModels.EnvironmentItemOptionsModel{ IsExpand: &defaultExpand, }, } projectSettingsEnvs = append(projectSettingsEnvs, devBranchEnv) } // TODO: // generate a couple of base steps // * timestamp gen // * bash script - hello world scriptStepTitle := "Hello Bitrise!" scriptStepContent := `#!/bin/bash echo "Welcome to Bitrise!"` bitriseConf := models.BitriseDataModel{ FormatVersion: c.App.Version, DefaultStepLibSource: defaultStepLibSource, App: models.AppModel{ Environments: projectSettingsEnvs, }, Workflows: map[string]models.WorkflowModel{ "primary": models.WorkflowModel{ Steps: []models.StepListItemModel{ models.StepListItemModel{ "script": stepmanModels.StepModel{ Title: &scriptStepTitle, Inputs: []envmanModels.EnvironmentItemModel{ envmanModels.EnvironmentItemModel{ "content": scriptStepContent, }, }, }, }, }, }, }, } if err := bitrise.SaveConfigToFile(bitriseConfigFileRelPath, bitriseConf); err != nil { log.Fatalln("Failed to init the bitrise config file:", err) } else { fmt.Println() fmt.Println("# NOTES about the " + DefaultBitriseConfigFileName + " config file:") fmt.Println() fmt.Println("We initialized a " + DefaultBitriseConfigFileName + " config file for you.") fmt.Println("If you're in this folder you can use this config file") fmt.Println(" with bitrise automatically, you don't have to") fmt.Println(" specify it's path.") fmt.Println() } if initialized, err := saveSecretsToFile(bitriseSecretsFileRelPath, defaultSecretsContent); err != nil { log.Fatalln("Failed to init the secrets file:", err) } else if initialized { fmt.Println() fmt.Println("# NOTES about the " + DefaultSecretsFileName + " secrets file:") fmt.Println() fmt.Println("We also created a " + DefaultSecretsFileName + " file") fmt.Println(" in this directory, to keep your passwords, absolute path configurations") fmt.Println(" and other secrets separate from your") fmt.Println(" main configuration file.") fmt.Println("This way you can safely commit and share your configuration file") fmt.Println(" and ignore this secrets file, so nobody else will") fmt.Println(" know about your secrets.") fmt.Println(colorstring.Yellow("You should NEVER commit this secrets file into your repository!!")) fmt.Println() } fmt.Println() fmt.Println("Hurray, you're good to go!") fmt.Println("You can simply run:") fmt.Println("-> bitrise run primary") fmt.Println("to test the sample configuration (which contains") fmt.Println("an example workflow called 'primary').") fmt.Println() fmt.Println("Once you tested this sample setup you can") fmt.Println(" open the " + DefaultBitriseConfigFileName + " config file,") fmt.Println(" modify it and then run a workflow with:") fmt.Println("-> bitrise run YOUR-WORKFLOW-NAME") }