func printFinishShare() { fmt.Println() log.Info(" * " + colorstring.Green("[OK] ") + "Yeah!! You rock!!") fmt.Println() fmt.Println(" " + GuideTextForFinish()) fmt.Println() msg := ` You can create a pull request in your forked StepLib repository, if you used the main StepLib repository then your repository's url looks like: ` + ` ` + colorstring.Green("https://github.com/[your-username]/bitrise-steplib") + ` On GitHub you can find a ` + colorstring.Green("'Compare & pull request'") + ` button, in the ` + colorstring.Green("'Your recently pushed branches:'") + ` section, which will bring you to the 'Open a pull request' page, where you can review and create your Pull Request. ` fmt.Println(msg) }
// 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 pluginList(c *cli.Context) { pluginMap, err := plugins.ListPlugins() if err != nil { log.Fatalf("Failed to list plugins, err: %s", err) } pluginNames := []string{} for _, plugins := range pluginMap { for _, plugin := range plugins { pluginNames = append(pluginNames, plugin.PrintableName()) } } sort.Strings(pluginNames) if len(pluginNames) > 0 { fmt.Println("") for _, name := range pluginNames { fmt.Printf(" ⚡️ %s\n", colorstring.Green(name)) } fmt.Println("") } else { fmt.Println("") fmt.Println("No installed plugin found") fmt.Println("") } }
func doSetupToolkits() error { log.Infoln("Checking Bitrise Toolkits...") coreToolkits := toolkits.AllSupportedToolkits() for _, aCoreTK := range coreToolkits { toolkitName := aCoreTK.ToolkitName() isInstallRequired, checkResult, err := aCoreTK.Check() if err != nil { return fmt.Errorf("Failed to perform toolkit check (%s), error: %s", toolkitName, err) } if isInstallRequired { log.Infoln("No installed/suitable '" + toolkitName + "' found, installing toolkit ...") if err := aCoreTK.Install(); err != nil { return fmt.Errorf("Failed to install toolkit (%s), error: %s", toolkitName, err) } isInstallRequired, checkResult, err = aCoreTK.Check() if err != nil { return fmt.Errorf("Failed to perform toolkit check (%s), error: %s", toolkitName, err) } } if isInstallRequired { return fmt.Errorf("Toolkit (%s) still reports that it isn't (properly) installed", toolkitName) } log.Infoln(" * "+colorstring.Green("[OK]")+" "+toolkitName+" :", checkResult.Path) log.Infoln(" version :", checkResult.Version) } return nil }
func printFinishCreate(share ShareModel, stepDirInSteplib string, toolMode bool) { fmt.Println() log.Infof(" * "+colorstring.Green("[OK]")+" Your Step (%s) (%s) added to local StepLib (%s).", share.StepID, share.StepTag, stepDirInSteplib) log.Infoln(" * You can find your Step's step.yml at: " + colorstring.Greenf("%s/step.yml", stepDirInSteplib)) fmt.Println() fmt.Println(" " + GuideTextForShareFinish(toolMode)) }
func checkIsBitriseToolInstalled(toolname, minVersion string, isInstall bool) error { doInstall := func() error { installCmdLines := []string{ "curl -fL https://github.com/bitrise-io/" + toolname + "/releases/download/" + minVersion + "/" + toolname + "-$(uname -s)-$(uname -m) > /usr/local/bin/" + toolname, "chmod +x /usr/local/bin/" + toolname, } officialGithub := "https://github.com/bitrise-io/" + toolname fmt.Println() log.Warnln("No supported " + toolname + " version found.") log.Infoln("You can find more information about "+toolname+" on it's official GitHub page:", officialGithub) fmt.Println() // Install log.Infoln("Installing...") fmt.Println(strings.Join(installCmdLines, "\n")) if err := cmdex.RunBashCommandLines(installCmdLines); err != nil { return err } // check again return checkIsBitriseToolInstalled(toolname, minVersion, false) } // check whether installed progInstallPth, err := CheckProgramInstalledPath(toolname) if err != nil { if !isInstall { return err } return doInstall() } verStr, err := cmdex.RunCommandAndReturnStdout(toolname, "-version") if err != nil { log.Infoln("") return errors.New("Failed to get version") } // version check isVersionOk, err := versions.IsVersionGreaterOrEqual(verStr, minVersion) if err != nil { log.Error("Failed to validate installed version") return err } if !isVersionOk { log.Warn("Installed "+toolname+" found, but not a supported version: ", verStr) if !isInstall { return errors.New("Failed to install required version.") } log.Warn("Updating...") return doInstall() } log.Infoln(" * "+colorstring.Green("[OK]")+" "+toolname+" :", progInstallPth) log.Infoln(" version :", verStr) return nil }
// GuideTextForStart ... func GuideTextForStart() string { guide := colorstring.Blue("Fork the StepLib repository") + " you want to share your Step in.\n" + ` You can find the main ("official") StepLib repository at: ` + colorstring.Green("https://github.com/bitrise-io/bitrise-steplib") + ` ` + colorstring.Yellow("Note") + `: You can use any StepLib repository you like, the StepLib system is decentralized, you don't have to work with the main StepLib repository if you don't want to. Feel free to maintain and use your own (or your team's) Step Library. ` return guide }
// 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) } } // then do a package manager specific lookup { 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" to be available, but it is not installed.`, aptGetDep.GetBinaryName()) allow, err := goinp.AskForBoolWithDefault(`Would you like to install the "`+aptGetDep.Name+`" package with apt-get?`, true) 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 cmdOut, 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, cmdOut, err) return err } log.Infof(" * "+colorstring.Green("[OK]")+" %s installed", aptGetDep.Name) } return nil }
func share(c *cli.Context) error { toolMode := c.Bool(ToolMode) guide := ` Do you want to ` + colorstring.Green("share ") + colorstring.Yellow("your ") + colorstring.Magenta("own ") + colorstring.Blue("Step") + ` with the world? Awesome!! To get started you can find a template Step repository at: ` + colorstring.Green("https://github.com/bitrise-steplib/step-template") + ` Once you have your Step in a ` + colorstring.Yellow("public git repository") + ` you can share it with others. To share your Step just follow these steps (pun intended ;) : 1. ` + GuideTextForStart() + ` 2. ` + GuideTextForShareStart(toolMode) + ` 3. ` + GuideTextForShareCreate(toolMode) + ` 4. ` + GuideTextForAudit(toolMode) + ` 5. ` + GuideTextForShareFinish(toolMode) + ` 6. ` + GuideTextForFinish() fmt.Println(guide) return nil }
func printRawEnvInfo(env models.EnvInfoModel) { if env.DefaultValue != "" { fmt.Printf("- %s: %s\n", colorstring.Green(env.Key), env.DefaultValue) } else { fmt.Printf("- %s\n", colorstring.Green(env.Key)) } fmt.Printf(" %s: %v\n", colorstring.Green("is expand"), env.IsExpand) if len(env.ValueOptions) > 0 { fmt.Printf(" %s:\n", colorstring.Green("value options")) for _, option := range env.ValueOptions { fmt.Printf(" - %s\n", option) } } if env.Description != "" { fmt.Printf(" %s:\n", colorstring.Green("description")) fmt.Printf(" %s\n", env.Description) } }
// GuideTextForShareStart ... func GuideTextForShareStart(toolMode bool) string { name := "stepman" if toolMode { name = "bitrise" } guide := "Call " + colorstring.Blue("'"+name+" share start -c STEPLIB_REPO_FORK_GIT_URL'") + ", with the " + colorstring.Yellow("git clone url") + " of " + colorstring.Yellow("your forked StepLib repository") + ".\n" + ` This will prepare your forked StepLib locally for sharing. For example, if you want to share your Step in the main StepLib repository you should call: ` + colorstring.Green(""+name+" share start -c https://github.com/[your-username]/bitrise-steplib.git") + ` ` return guide }
// InstallWithBrewIfNeeded ... func InstallWithBrewIfNeeded(brewDep stepmanModels.BrewDepModel, 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", brewDep.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)", brewDep.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", brewDep.Name) } if !isDepInstalled { // which did not find the binary, also check in brew, // whether the package is installed isDepInstalled = checkIfBrewPackageInstalled(brewDep.Name) } if !isDepInstalled { // Tool isn't installed -- install it... if !isCIMode { log.Infof("This step requires %s, which is not installed", brewDep.Name) allow, err := goinp.AskForBool("Would you like to install (" + brewDep.Name + ") with brew?") if err != nil { return err } if !allow { return errors.New("(" + brewDep.Name + ") is required for step") } } log.Infof("(%s) isn't installed, installing...", brewDep.Name) if out, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("brew", "install", brewDep.Name); err != nil { log.Errorf("brew install %s failed -- out: (%s) err: (%s)", brewDep.Name, out, err) return err } log.Infof(" * "+colorstring.Green("[OK]")+" %s installed", brewDep.Name) } return nil }
// GuideTextForShareCreate ... func GuideTextForShareCreate(toolMode bool) string { name := "stepman" if toolMode { name = "bitrise" } guide := "Next, call " + colorstring.Blue("'"+name+" share create --tag STEP_VERSION_TAG --git STEP_GIT_URI --stepid STEP_ID'") + `, to add your Step to your forked StepLib repository (locally). This will copy the required step.yml file from your Step's repository. This is all what's required to add your step (or a new version) to a StepLib. ` + colorstring.Yellow("Important") + `: You have to add the (version) tag to your Step's repository before you would call this! You can do that at: https://github.com/[your-username]/[step-repository]/tags An example call: ` + colorstring.Green(""+name+" share create --tag 1.0.0 --git https://github.com/[your-username]/[step-repository].git --stepid my-awesome-step") + ` ` + colorstring.Yellow("Note") + `: You'll still be able to modify the step.yml in the StepLib after this. ` return guide }
// PrintInstalledXcodeInfos ... func PrintInstalledXcodeInfos() error { xcodeSelectPth, err := cmdex.RunCommandAndReturnStdout("xcode-select", "--print-path") if err != nil { xcodeSelectPth = "xcode-select --print-path failed to detect the location of activate Xcode Command Line Tools path" } progInstallPth, err := utils.CheckProgramInstalledPath("xcodebuild") if err != nil { return errors.New("xcodebuild is not installed") } isFullXcodeAvailable := false verStr, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("xcodebuild", "-version") if err != nil { // No full Xcode available, only the Command Line Tools // verStr is something like "xcode-select: error: tool 'xcodebuild' requires Xcode, but active developer directory '/Library/Developer/CommandLineTools' is a command line tools instance" isFullXcodeAvailable = false } else { // version OK - full Xcode available // we'll just format it a bit to fit into one line isFullXcodeAvailable = true verStr = strings.Join(strings.Split(verStr, "\n"), " | ") } log.Infoln(" * "+colorstring.Green("[OK]")+" xcodebuild path :", progInstallPth) if !isFullXcodeAvailable { log.Infoln(" version (xcodebuild) :", colorstring.Yellowf("%s", verStr)) } else { log.Infoln(" version (xcodebuild) :", verStr) } log.Infoln(" active Xcode (Command Line Tools) path (xcode-select --print-path) :", xcodeSelectPth) if !isFullXcodeAvailable { log.Warn(colorstring.Yellowf("%s", "No Xcode found, only the Xcode Command Line Tools are available!")) log.Warn(colorstring.Yellowf("%s", "Full Xcode is required to build, test and archive iOS apps!")) } return nil }
// 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 }
// CheckIsHomebrewInstalled ... func CheckIsHomebrewInstalled(isFullSetupMode bool) error { brewRubyInstallCmdString := `$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"` officialSiteURL := "http://brew.sh/" progInstallPth, err := utils.CheckProgramInstalledPath("brew") if err != nil { fmt.Println() log.Warn("It seems that Homebrew is not installed on your system.") log.Infoln("Homebrew (short: brew) is required in order to be able to auto-install all the bitrise dependencies.") log.Infoln("You should be able to install brew by copying this command and running it in your Terminal:") log.Infoln(brewRubyInstallCmdString) log.Infoln("You can find more information about Homebrew on its official site at:", officialSiteURL) log.Warn("Once the installation of brew is finished you should call the bitrise setup again.") return err } verStr, err := cmdex.RunCommandAndReturnStdout("brew", "--version") if err != nil { log.Infoln("") return errors.New("Failed to get version") } if isFullSetupMode { // brew doctor doctorOutput, err := cmdex.RunCommandAndReturnCombinedStdoutAndStderr("brew", "doctor") if err != nil { fmt.Println("") log.Warn("brew doctor returned an error:") log.Warnf("%s", doctorOutput) return errors.New("Failed to: brew doctor") } } log.Infoln(" * "+colorstring.Green("[OK]")+" Homebrew :", progInstallPth) log.Infoln(" version :", verStr) return nil }
func exportProvisioningProfiles(provProfileFileInfos []provprofile.ProvisioningProfileFileInfoModel, exportTargetDirPath string) error { for idx, aProvProfileFileInfo := range provProfileFileInfos { if idx != 0 { fmt.Println() } provProfileInfo := aProvProfileFileInfo.ProvisioningProfileInfo log.Infoln(" "+colorstring.Green("Exporting Provisioning Profile:"), provProfileInfo.Name) log.Infoln(" App ID Name:", provProfileInfo.AppIDName) log.Infoln(" App ID:", provProfileInfo.Entitlements.AppID) log.Infoln(" Expiration Date:", provProfileInfo.ExpirationDate) log.Infoln(" UUID:", provProfileInfo.UUID) log.Infoln(" TeamName:", provProfileInfo.TeamName) log.Infoln(" Team ID:", provProfileInfo.Entitlements.TeamID) exportFileName := provProfileExportFileName(aProvProfileFileInfo) exportPth := filepath.Join(exportTargetDirPath, exportFileName) if err := cmdex.RunCommand("cp", aProvProfileFileInfo.Path, exportPth); err != nil { return fmt.Errorf("Failed to copy Provisioning Profile (from: %s) (to: %s), error: %s", aProvProfileFileInfo.Path, exportPth, err) } } return nil }
// Done ... func Done(format string, v ...interface{}) { message := fmt.Sprintf(format, v...) fmt.Println(colorstring.Green(message)) }
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 printFinishStart(specPth string, toolMode bool) { fmt.Println() log.Info(" * "+colorstring.Green("[OK]")+" You can find your StepLib repo at: ", specPth) fmt.Println() fmt.Println(" " + GuideTextForShareCreate(toolMode)) }
func scanXamarinProject(cmd *cobra.Command, args []string) error { absExportOutputDirPath, err := initExportOutputDir() if err != nil { return printXamarinScanFinishedWithError("Failed to prepare Export directory: %s", err) } logOutput := "" xamarinCmd := xamarin.CommandModel{} if paramXamarinOutputLogFilePath != "" { xamLog, err := fileutil.ReadStringFromFile(paramXamarinOutputLogFilePath) if err != nil { return printXamarinScanFinishedWithError("Failed to read log from the specified log file, error: %s", err) } logOutput = xamLog } else { // --- Inputs --- // Xamarin Solution Path xamarinCmd.SolutionFilePath = paramXamarinSolutionFilePath if xamarinCmd.SolutionFilePath == "" { askText := `Please drag-and-drop your Xamarin Solution (` + colorstring.Green(".sln") + `) file here, and then hit Enter` fmt.Println() projpth, err := goinp.AskForPath(askText) if err != nil { return printXamarinScanFinishedWithError("Failed to read input: %s", err) } xamarinCmd.SolutionFilePath = projpth } log.Debugf("xamSolutionPth: %s", xamarinCmd.SolutionFilePath) xamSln, err := solution.New(xamarinCmd.SolutionFilePath, true) if err != nil { return printXamarinScanFinishedWithError("Failed to analyze Xamarin solution: %s", err) } log.Debugf("xamSln: %#v", xamSln) // filter only the iOS "app"" projects xamarinProjectsToChooseFrom := []project.Model{} for _, aXamarinProject := range xamSln.ProjectMap { switch aXamarinProject.ProjectType { case constants.ProjectTypeIOS, constants.ProjectTypeTvOS, constants.ProjectTypeMacOS: if aXamarinProject.OutputType == "exe" { // possible project xamarinProjectsToChooseFrom = append(xamarinProjectsToChooseFrom, aXamarinProject) } default: continue } } log.Debugf("len(xamarinProjectsToChooseFrom): %#v", len(xamarinProjectsToChooseFrom)) log.Debugf("xamarinProjectsToChooseFrom: %#v", xamarinProjectsToChooseFrom) // Xamarin Project selectedXamarinProject := project.Model{} { if len(xamarinProjectsToChooseFrom) < 1 { return printXamarinScanFinishedWithError( "No acceptable Project found in the provided Solution, or none can be used for iOS Archive.", ) } if paramXamarinProjectName != "" { // project specified via flag/param for _, aProj := range xamarinProjectsToChooseFrom { if paramXamarinProjectName == aProj.Name { selectedXamarinProject = aProj break } } if selectedXamarinProject.Name == "" { return printXamarinScanFinishedWithError( `Invalid Project specified (%s), either not found in the provided Solution or it can't be used for iOS "Archive for Publishing".`, paramXamarinProjectName) } } else { // no project CLI param specified if len(xamarinProjectsToChooseFrom) == 1 { selectedXamarinProject = xamarinProjectsToChooseFrom[0] } else { projectNames := []string{} for _, aProj := range xamarinProjectsToChooseFrom { projectNames = append(projectNames, aProj.Name) } fmt.Println() answerValue, err := goinp.SelectFromStrings( `Select the Project Name you use for "Archive for Publishing" (usually ends with ".iOS", e.g.: MyProject.iOS)?`, projectNames, ) if err != nil { return printXamarinScanFinishedWithError("Failed to select Project: %s", err) } log.Debugf("selected project: %v", answerValue) for _, aProj := range xamarinProjectsToChooseFrom { if answerValue == aProj.Name { selectedXamarinProject = aProj break } } } } } xamarinCmd.ProjectName = selectedXamarinProject.Name log.Debugf("xamarinCmd.ProjectName: %s", xamarinCmd.ProjectName) log.Debugf("selectedXamarinProject.Configs: %#v", selectedXamarinProject.Configs) // Xamarin Configuration Name selectedXamarinConfigurationName := "" { acceptableConfigs := []string{} for configName, aConfig := range selectedXamarinProject.Configs { if aConfig.Platform == "iPhone" { if aConfig.Configuration == "Release" { // ios & tvOS app acceptableConfigs = append(acceptableConfigs, configName) } } else if aConfig.Platform == "x86" { if aConfig.Configuration == "Release" || aConfig.Configuration == "Debug" { // MacOS app acceptableConfigs = append(acceptableConfigs, configName) } } } if len(acceptableConfigs) < 1 { return printXamarinScanFinishedWithError( `No acceptable Configuration found in the provided Solution and Project, or none can be used for iOS "Archive for Publishing".`, ) } if paramXamarinConfigurationName != "" { // configuration specified via flag/param for _, aConfigName := range acceptableConfigs { if paramXamarinConfigurationName == aConfigName { selectedXamarinConfigurationName = aConfigName break } } if selectedXamarinConfigurationName == "" { return printXamarinScanFinishedWithError( "Invalid Configuration specified (%s), either not found in the provided Solution and Project or it can't be used for iOS Archive.", paramXamarinConfigurationName) } } else { // no configuration CLI param specified if len(acceptableConfigs) == 1 { selectedXamarinConfigurationName = acceptableConfigs[0] } else { fmt.Println() answerValue, err := goinp.SelectFromStrings( `Select the Configuration Name you use for "Archive for Publishing" (usually Release|iPhone)?`, acceptableConfigs, ) if err != nil { return printXamarinScanFinishedWithError("Failed to select Configuration: %s", err) } log.Debugf("selected configuration: %v", answerValue) selectedXamarinConfigurationName = answerValue } } } if selectedXamarinConfigurationName == "" { return printXamarinScanFinishedWithError( `No acceptable Configuration found (it was empty) in the provided Solution and Project, or none can be used for iOS "Archive for Publishing".`, ) } xamarinCmd.ConfigurationName = selectedXamarinConfigurationName fmt.Println() fmt.Println() log.Println(`🔦 Running a Build, to get all the required code signing settings...`) xamLogOut, err := xamarinCmd.GenerateLog() logOutput = xamLogOut // save the xamarin output into a debug log file logOutputFilePath := filepath.Join(absExportOutputDirPath, "xamarin-build-output.log") { log.Infof(" 💡 "+colorstring.Yellow("Saving xamarin output into file")+": %s", logOutputFilePath) if logWriteErr := fileutil.WriteStringToFile(logOutputFilePath, logOutput); logWriteErr != nil { log.Errorf("Failed to save xamarin build output into file (%s), error: %s", logOutputFilePath, logWriteErr) } else if err != nil { log.Infoln(colorstring.Yellow("Please check the logfile (" + logOutputFilePath + ") to see what caused the error")) log.Infoln(colorstring.Red(`and make sure that you can "Archive for Publishing" this project from Xamarin!`)) fmt.Println() log.Infoln("Open the project: ", xamarinCmd.SolutionFilePath) log.Infoln(`And do "Archive for Publishing", after selecting the Configuration: `, xamarinCmd.ConfigurationName) fmt.Println() } } if err != nil { return printXamarinScanFinishedWithError("Failed to run xamarin build command: %s", err) } } codeSigningSettings, err := xamarinCmd.ScanCodeSigningSettings(logOutput) if err != nil { return printXamarinScanFinishedWithError("Failed to detect code signing settings: %s", err) } log.Debugf("codeSigningSettings: %#v", codeSigningSettings) return exportCodeSigningFiles("Xamarin Studio", absExportOutputDirPath, codeSigningSettings) }
func printWorkflList(workflowList map[string]map[string]string, format string, minimal bool) error { printRawWorkflowMap := func(name string, workflow map[string]string) { fmt.Printf("⚡️ %s\n", colorstring.Green(name)) fmt.Printf(" %s: %s\n", colorstring.Yellow("Summary"), workflow["summary"]) if !minimal { fmt.Printf(" %s: %s\n", colorstring.Yellow("Description"), workflow["description"]) } fmt.Println() } switch format { case output.FormatRaw: workflowNames := []string{} utilityWorkflowNames := []string{} for wfName := range workflowList { if strings.HasPrefix(wfName, "_") { utilityWorkflowNames = append(utilityWorkflowNames, wfName) } else { workflowNames = append(workflowNames, wfName) } } sort.Strings(workflowNames) sort.Strings(utilityWorkflowNames) fmt.Println() if len(workflowNames) > 0 { fmt.Printf("%s\n", "Workflows") fmt.Printf("%s\n", "---------") for _, name := range workflowNames { workflow := workflowList[name] printRawWorkflowMap(name, workflow) } fmt.Println() } if len(utilityWorkflowNames) > 0 { fmt.Printf("%s\n", "Util Workflows") fmt.Printf("%s\n", "--------------") for _, name := range utilityWorkflowNames { workflow := workflowList[name] printRawWorkflowMap(name, workflow) } fmt.Println() } if len(workflowNames) == 0 && len(utilityWorkflowNames) == 0 { fmt.Printf("Config doesn't contain any workflow") } case output.FormatJSON: bytes, err := json.Marshal(workflowList) if err != nil { return err } fmt.Println(string(bytes)) default: return fmt.Errorf("Invalid output format: %s", format) } return nil }
func runStep(step stepmanModels.StepModel, stepIDData models.StepIDData, stepDir string, environments []envmanModels.EnvironmentItemModel, buildRunResults models.BuildRunResultsModel) (int, []envmanModels.EnvironmentItemModel, error) { log.Debugf("[BITRISE_CLI] - Try running step: %s (%s)", stepIDData.IDorURI, stepIDData.Version) // Check & Install Step Dependencies if len(step.Dependencies) > 0 { log.Warnf("step.dependencies is deprecated... Use step.deps instead.") } if len(step.Deps.Brew) > 0 || len(step.Deps.AptGet) > 0 || len(step.Deps.CheckOnly) > 0 { // // New dependency handling for _, checkOnlyDep := range step.Deps.CheckOnly { if err := bitrise.DependencyTryCheckTool(checkOnlyDep.Name); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", checkOnlyDep.Name) } switch runtime.GOOS { case "darwin": for _, brewDep := range step.Deps.Brew { if err := bitrise.InstallWithBrewIfNeeded(brewDep.Name, configs.IsCIMode); err != nil { log.Infof("Failed to install (%s) with brew", brewDep.Name) return 1, []envmanModels.EnvironmentItemModel{}, err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", brewDep.Name) } case "linux": for _, aptGetDep := range step.Deps.AptGet { log.Infof("Start installing (%s) with apt-get", aptGetDep.Name) if err := bitrise.InstallWithAptGetIfNeeded(aptGetDep.Name, configs.IsCIMode); err != nil { log.Infof("Failed to install (%s) with apt-get", aptGetDep.Name) return 1, []envmanModels.EnvironmentItemModel{}, err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", aptGetDep.Name) } default: return 1, []envmanModels.EnvironmentItemModel{}, errors.New("Unsupported os") } } else if len(step.Dependencies) > 0 { log.Info("Deprecated dependencies found") // // Deprecated dependency handling for _, dep := range step.Dependencies { isSkippedBecauseOfPlatform := false switch dep.Manager { case depManagerBrew: if runtime.GOOS == "darwin" { err := bitrise.InstallWithBrewIfNeeded(dep.Name, configs.IsCIMode) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } } else { isSkippedBecauseOfPlatform = true } break case depManagerTryCheck: err := bitrise.DependencyTryCheckTool(dep.Name) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } break default: return 1, []envmanModels.EnvironmentItemModel{}, errors.New("Not supported dependency (" + dep.Manager + ") (" + dep.Name + ")") } if isSkippedBecauseOfPlatform { log.Debugf(" * Dependency (%s) skipped, manager (%s) not supported on this platform (%s)", dep.Name, dep.Manager, runtime.GOOS) } else { log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", dep.Name) } } } // Collect step inputs if err := tools.EnvmanInitAtPath(configs.InputEnvstorePath); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if err := bitrise.ExportEnvironmentsList(environments); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } evaluatedInputs := []envmanModels.EnvironmentItemModel{} for _, input := range step.Inputs { key, value, err := input.GetKeyValuePair() if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } options, err := input.GetOptions() if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if options.IsTemplate != nil && *options.IsTemplate { outStr, err := tools.EnvmanJSONPrint(configs.InputEnvstorePath) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, fmt.Errorf("EnvmanJSONPrint failed, err: %s", err) } envList, err := envmanModels.NewEnvJSONList(outStr) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, fmt.Errorf("CreateFromJSON failed, err: %s", err) } evaluatedValue, err := bitrise.EvaluateTemplateToString(value, configs.IsCIMode, configs.IsPullRequestMode, buildRunResults, envList) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } input[key] = evaluatedValue } evaluatedInputs = append(evaluatedInputs, input) } environments = append(environments, evaluatedInputs...) if err := tools.EnvmanInitAtPath(configs.InputEnvstorePath); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if err := bitrise.ExportEnvironmentsList(environments); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } // Run step stepCmd := filepath.Join(stepDir, "step.sh") cmd := []string{"bash", stepCmd} bitriseSourceDir, err := getCurrentBitriseSourceDir(environments) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if bitriseSourceDir == "" { bitriseSourceDir = configs.CurrentDir } if exit, err := tools.EnvmanRun(configs.InputEnvstorePath, bitriseSourceDir, cmd); err != nil { stepOutputs, envErr := bitrise.CollectEnvironmentsFromFile(configs.OutputEnvstorePath) if envErr != nil { return 1, []envmanModels.EnvironmentItemModel{}, envErr } return exit, stepOutputs, err } stepOutputs, err := bitrise.CollectEnvironmentsFromFile(configs.OutputEnvstorePath) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } log.Debugf("[BITRISE_CLI] - Step executed: %s (%s)", stepIDData.IDorURI, stepIDData.Version) return 0, stepOutputs, nil }
func run(c *cli.Context) error { PrintBitriseHeaderASCIIArt(version.VERSION) // // Expand cli.Context var prGlobalFlagPtr *bool if c.GlobalIsSet(PRKey) { prGlobalFlagPtr = pointers.NewBoolPtr(c.GlobalBool(PRKey)) } var ciGlobalFlagPtr *bool if c.GlobalIsSet(CIKey) { ciGlobalFlagPtr = pointers.NewBoolPtr(c.GlobalBool(CIKey)) } workflowToRunID := c.String(WorkflowKey) if workflowToRunID == "" && len(c.Args()) > 0 { workflowToRunID = c.Args()[0] } bitriseConfigBase64Data := c.String(ConfigBase64Key) bitriseConfigPath := c.String(ConfigKey) deprecatedBitriseConfigPath := c.String(PathKey) if bitriseConfigPath == "" && deprecatedBitriseConfigPath != "" { log.Warn("'path' key is deprecated, use 'config' instead!") bitriseConfigPath = deprecatedBitriseConfigPath } inventoryBase64Data := c.String(InventoryBase64Key) inventoryPath := c.String(InventoryKey) jsonParams := c.String(JSONParamsKey) jsonParamsBase64 := c.String(JSONParamsBase64Key) runParams, err := parseRunParams( workflowToRunID, bitriseConfigPath, bitriseConfigBase64Data, inventoryPath, inventoryBase64Data, jsonParams, jsonParamsBase64) if err != nil { return fmt.Errorf("Failed to parse command params, error: %s", err) } // // Inventory validation inventoryEnvironments, err := CreateInventoryFromCLIParams(runParams.InventoryBase64Data, runParams.InventoryPath) if err != nil { log.Fatalf("Failed to create inventory, error: %s", err) } // Config validation bitriseConfig, warnings, err := CreateBitriseConfigFromCLIParams(runParams.BitriseConfigBase64Data, runParams.BitriseConfigPath) for _, warning := range warnings { log.Warnf("warning: %s", warning) } if err != nil { log.Fatalf("Failed to create bitrise config, error: %s", err) } // Workflow id validation if runParams.WorkflowToRunID == "" { // no workflow specified // list all the available ones and then exit log.Error("No workfow specified!") fmt.Println() printAvailableWorkflows(bitriseConfig) os.Exit(1) } if strings.HasPrefix(runParams.WorkflowToRunID, "_") { // util workflow specified // print about util workflows and then exit log.Error("Utility workflows can't be triggered directly") fmt.Println() printAboutUtilityWorkflowsText() os.Exit(1) } // // // Main isPRMode, err := isPRMode(prGlobalFlagPtr, inventoryEnvironments) if err != nil { log.Fatalf("Failed to check PR mode, error: %s", err) } if err := registerPrMode(isPRMode); err != nil { log.Fatalf("Failed to register PR mode, error: %s", err) } isCIMode, err := isCIMode(ciGlobalFlagPtr, inventoryEnvironments) if err != nil { log.Fatalf("Failed to check CI mode, error: %s", err) } if err := registerCIMode(isCIMode); err != nil { log.Fatalf("Failed to register CI mode, error: %s", err) } log.Infoln(colorstring.Green("Running workflow:"), runParams.WorkflowToRunID) runAndExit(bitriseConfig, inventoryEnvironments, runParams.WorkflowToRunID) // return nil }
func runStep(step stepmanModels.StepModel, stepIDData models.StepIDData, stepDir string, environments []envmanModels.EnvironmentItemModel) (int, []envmanModels.EnvironmentItemModel, error) { log.Debugf("[BITRISE_CLI] - Try running step: %s (%s)", stepIDData.IDorURI, stepIDData.Version) // Check dependencies for _, dep := range step.Dependencies { isSkippedBecauseOfPlatform := false switch dep.Manager { case depManagerBrew: if runtime.GOOS == "darwin" { err := bitrise.InstallWithBrewIfNeeded(dep.Name, IsCIMode) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } } else { isSkippedBecauseOfPlatform = true } break case depManagerTryCheck: err := bitrise.DependencyTryCheckTool(dep.Name) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } break default: return 1, []envmanModels.EnvironmentItemModel{}, errors.New("Not supported dependency (" + dep.Manager + ") (" + dep.Name + ")") } if isSkippedBecauseOfPlatform { log.Debugf(" * Dependency (%s) skipped, manager (%s) not supported on this platform (%s)", dep.Name, dep.Manager, runtime.GOOS) } else { log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", dep.Name) } } // Collect step inputs environments = append(environments, step.Inputs...) // Cleanup envstore if err := bitrise.EnvmanInitAtPath(bitrise.InputEnvstorePath); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if err := bitrise.ExportEnvironmentsList(environments); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } // Run step stepCmd := path.Join(stepDir, "step.sh") log.Debug("OUTPUT:") cmd := []string{"bash", stepCmd} if exit, err := bitrise.EnvmanRun(bitrise.InputEnvstorePath, bitrise.CurrentDir, cmd, "panic"); err != nil { return exit, []envmanModels.EnvironmentItemModel{}, err } stepOutputs, err := bitrise.CollectEnvironmentsFromFile(bitrise.OutputEnvstorePath) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } log.Debugf("[BITRISE_CLI] - Step executed: %s (%s)", stepIDData.IDorURI, stepIDData.Version) return 0, stepOutputs, nil }
func checkAndInstallStepDependencies(step stepmanModels.StepModel) error { if len(step.Dependencies) > 0 { log.Warnf("step.dependencies is deprecated... Use step.deps instead.") } if len(step.Deps.Brew) > 0 || len(step.Deps.AptGet) > 0 || len(step.Deps.CheckOnly) > 0 { // // New dependency handling for _, checkOnlyDep := range step.Deps.CheckOnly { if err := bitrise.DependencyTryCheckTool(checkOnlyDep.Name); err != nil { return err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", checkOnlyDep.Name) } switch runtime.GOOS { case "darwin": for _, brewDep := range step.Deps.Brew { if err := bitrise.InstallWithBrewIfNeeded(brewDep, configs.IsCIMode); err != nil { log.Infof("Failed to install (%s) with brew", brewDep.Name) return err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", brewDep.Name) } case "linux": for _, aptGetDep := range step.Deps.AptGet { log.Infof("Start installing (%s) with apt-get", aptGetDep.Name) if err := bitrise.InstallWithAptGetIfNeeded(aptGetDep, configs.IsCIMode); err != nil { log.Infof("Failed to install (%s) with apt-get", aptGetDep.Name) return err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", aptGetDep.Name) } default: return errors.New("Unsupported os") } } else if len(step.Dependencies) > 0 { log.Info("Deprecated dependencies found") // // Deprecated dependency handling for _, dep := range step.Dependencies { isSkippedBecauseOfPlatform := false switch dep.Manager { case depManagerBrew: if runtime.GOOS == "darwin" { err := bitrise.InstallWithBrewIfNeeded(stepmanModels.BrewDepModel{Name: dep.Name}, configs.IsCIMode) if err != nil { return err } } else { isSkippedBecauseOfPlatform = true } break case depManagerTryCheck: err := bitrise.DependencyTryCheckTool(dep.Name) if err != nil { return err } break default: return errors.New("Not supported dependency (" + dep.Manager + ") (" + dep.Name + ")") } if isSkippedBecauseOfPlatform { log.Debugf(" * Dependency (%s) skipped, manager (%s) not supported on this platform (%s)", dep.Name, dep.Manager, runtime.GOOS) } else { log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", dep.Name) } } } return nil }
func printFinishAudit(share ShareModel, toolMode bool) { fmt.Println() log.Infof(" * "+colorstring.Green("[OK]")+" Your step (%s) (%s) is valid.", share.StepID, share.StepTag) fmt.Println() fmt.Println(" " + GuideTextForShareFinish(toolMode)) }
// CheckIsPluginInstalled ... func CheckIsPluginInstalled(name string, dependency PluginDependency) error { _, found, err := plugins.LoadPlugin(name) if err != nil { return err } currentVersion := "" installOrUpdate := false if !found { fmt.Println() log.Warnf("Default plugin (%s) NOT found.", name) fmt.Println() fmt.Print("Installing...") installOrUpdate = true currentVersion = dependency.MinVersion } else { installedVersion, err := plugins.GetPluginVersion(name) if err != nil { return err } if installedVersion == nil { fmt.Println() log.Warnf("Default plugin (%s) is not installed from git, no version info available.", name) fmt.Println() currentVersion = "local" } else { currentVersion = installedVersion.String() minVersion, err := ver.NewVersion(dependency.MinVersion) if err != nil { return err } if installedVersion.LessThan(minVersion) { fmt.Println() log.Warnf("Default plugin (%s) version (%s) is lower than required (%s).", name, installedVersion.String(), minVersion.String()) fmt.Println() log.Infoln("Updating...") installOrUpdate = true currentVersion = dependency.MinVersion } } } if installOrUpdate { var plugin plugins.Plugin err := progress.SimpleProgressE(".", 2*time.Second, func() error { return retry.Times(2).Wait(5 * time.Second).Try(func(attempt uint) error { if attempt > 0 { fmt.Println() fmt.Print("==> Download failed, retrying ...") } p, _, err := plugins.InstallPlugin(dependency.Source, dependency.Binary, dependency.MinVersion) plugin = p return err }) }) fmt.Println() if err != nil { return fmt.Errorf("Failed to install plugin, error: %s", err) } if len(plugin.Description) > 0 { fmt.Println() fmt.Println(plugin.Description) fmt.Println() } } pluginDir := plugins.GetPluginDir(name) log.Infof(" * %s Plugin (%s) : %s", colorstring.Green("[OK]"), name, pluginDir) log.Infof(" version : %s", currentVersion) return nil }
func checkIsBitriseToolInstalled(toolname, minVersion string, isInstall bool) error { doInstall := func() error { officialGithub := "https://github.com/bitrise-io/" + toolname fmt.Println() log.Warnln("No supported " + toolname + " version found.") log.Infoln("You can find more information about "+toolname+" on its official GitHub page:", officialGithub) // Install fmt.Print("Installing...") err := progress.SimpleProgressE(".", 2*time.Second, func() error { return retry.Times(2).Wait(5 * time.Second).Try(func(attempt uint) error { if attempt > 0 { fmt.Println() fmt.Print("==> Download failed, retrying ...") } return tools.InstallToolFromGitHub(toolname, "bitrise-io", minVersion) }) }) fmt.Println() if err != nil { return err } // check again return checkIsBitriseToolInstalled(toolname, minVersion, false) } // check whether installed progInstallPth, err := utils.CheckProgramInstalledPath(toolname) if err != nil { if !isInstall { return err } return doInstall() } verStr, err := cmdex.RunCommandAndReturnStdout(toolname, "-version") if err != nil { log.Infoln("") return errors.New("Failed to get version") } // version check isVersionOk, err := versions.IsVersionGreaterOrEqual(verStr, minVersion) if err != nil { log.Error("Failed to validate installed version") return err } if !isVersionOk { log.Warn("Installed "+toolname+" found, but not a supported version: ", verStr) if !isInstall { return errors.New("Failed to install required version.") } log.Warn("Updating...") return doInstall() } log.Infoln(" * "+colorstring.Green("[OK]")+" "+toolname+" :", progInstallPth) log.Infoln(" version :", verStr) return nil }
func (plugin Plugin) String() string { pluginStr := colorstring.Green(plugin.Name) pluginStr += fmt.Sprintf("\n Description: %s", plugin.Description) return pluginStr }