func checkCIAndPRModeFromSecrets(envs []envmanModels.EnvironmentItemModel) error { for _, env := range envs { key, value, err := env.GetKeyValuePair() if err != nil { return err } if !configs.IsCIMode { if key == bitrise.CIModeEnvKey && value == "true" { configs.IsCIMode = true } } if !configs.IsPullRequestMode { if key == bitrise.PullRequestIDEnvKey && value != "" { configs.IsPullRequestMode = true } if key == bitrise.PRModeEnvKey && value == "true" { configs.IsPullRequestMode = true } } } if configs.IsCIMode { log.Info(colorstring.Yellow("bitrise runs in CI mode")) } if configs.IsPullRequestMode { log.Info(colorstring.Yellow("bitrise runs in PR mode")) } return nil }
func before(c *cli.Context) error { initLogFormatter() initHelpAndVersionFlags() initAppHelpTemplate() // Debug mode? if c.Bool(DebugModeKey) { // set for other tools, as an ENV if err := os.Setenv(bitrise.DebugModeEnvKey, "true"); err != nil { return err } IsDebugMode = true log.Warn("=> Started in DEBUG mode") } // Log level // If log level defined - use it logLevelStr := c.String(LogLevelKey) if logLevelStr == "" && IsDebugMode { // if no Log Level defined and we're in Debug Mode - set loglevel to debug logLevelStr = "debug" log.Warn("=> LogLevel set to debug") } if logLevelStr == "" { // if still empty: set the default logLevelStr = "info" } level, err := log.ParseLevel(logLevelStr) if err != nil { return err } if err := os.Setenv(bitrise.LogLevelEnvKey, level.String()); err != nil { log.Fatal("Failed to set log level env:", err) } log.SetLevel(level) // CI Mode check if c.Bool(CIKey) { // if CI mode indicated make sure we set the related env // so all other tools we use will also get it if err := os.Setenv(bitrise.CIModeEnvKey, "true"); err != nil { return err } IsCIMode = true log.Info(colorstring.Yellow("bitrise runs in CI mode")) } if err := bitrise.InitPaths(); err != nil { log.Fatalf("Failed to initialize required paths: %s", err) } // Pull Request Mode check IsPullRequestMode = (os.Getenv(bitrise.PullRequestIDEnvKey) != "") return nil }
func registerPrMode(isPRMode bool) error { configs.IsPullRequestMode = isPRMode if isPRMode { log.Info(colorstring.Yellow("bitrise runs in PR mode")) return os.Setenv(configs.PRModeEnvKey, "true") } return os.Setenv(configs.PRModeEnvKey, "false") }
func registerCIMode(isCIMode bool) error { configs.IsCIMode = isCIMode if isCIMode { log.Info(colorstring.Yellow("bitrise runs in CI mode")) return os.Setenv(configs.CIModeEnvKey, "true") } return os.Setenv(configs.CIModeEnvKey, "false") }
func stepNoteCell(stepRunResult models.StepRunResultsModel) string { iconBoxWidth := len(" ") timeBoxWidth := len(" time (s) ") titleBoxWidth := stepRunSummaryBoxWidthInChars - 4 - iconBoxWidth - timeBoxWidth - 2 stepInfo := stepRunResult.StepInfo whitespaceWidth := titleBoxWidth - len(fmt.Sprintf("update available %s -> %s", stepInfo.Version, stepInfo.Latest)) content := colorstring.Yellow(fmt.Sprintf(" Update available: %s -> %s%s", stepInfo.Version, stepInfo.Latest, strings.Repeat(" ", whitespaceWidth))) return fmt.Sprintf("|%s|%s|%s|", strings.Repeat("-", iconBoxWidth), content, strings.Repeat("-", timeBoxWidth)) }
// 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 }
func trigger(c *cli.Context) { PrintBitriseHeaderASCIIArt(c.App.Version) if !bitrise.CheckIsSetupWasDoneForVersion(c.App.Version) { log.Warnln(colorstring.Yellow("Setup was not performed for this version of bitrise, doing it now...")) if err := bitrise.RunSetup(c.App.Version, false); err != nil { log.Fatalln("Setup failed:", err) } } startTime := time.Now() // ------------------------ // Input validation // Inventory validation inventoryEnvironments, err := CreateInventoryFromCLIParams(c) if err != nil { log.Fatalf("Failed to create inventory, err: %s", err) } if err := checkCIAndPRModeFromSecrets(inventoryEnvironments); err != nil { log.Fatalf("Failed to check PR and CI mode, err: %s", err) } // Config validation bitriseConfig, err := CreateBitriseConfigFromCLIParams(c) if err != nil { log.Fatalf("Failed to create bitrise cofing, err: %s", err) } // Trigger filter validation triggerPattern := "" if len(c.Args()) < 1 { log.Errorln("No workfow specified!") } else { triggerPattern = c.Args()[0] } if triggerPattern == "" { // no trigger filter specified // list all the available ones and then exit printAvailableTriggerFilters(bitriseConfig.TriggerMap) } workflowToRunID, err := GetWorkflowIDByPattern(bitriseConfig, triggerPattern) if err != nil { log.Fatalf("Faild to select workflow by pattern (%s), err: %s", triggerPattern, err) } log.Infof("Pattern (%s) triggered workflow (%s) ", triggerPattern, workflowToRunID) // Run selected configuration if _, err := runWorkflowWithConfiguration(startTime, workflowToRunID, bitriseConfig, inventoryEnvironments); err != nil { log.Fatalln("Error: ", err) } }
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 run(c *cli.Context) { PrintBitriseHeaderASCIIArt(c.App.Version) log.Debugln("[BITRISE_CLI] - Run") if !bitrise.CheckIsSetupWasDoneForVersion(c.App.Version) { log.Warnln(colorstring.Yellow("Setup was not performed for this version of bitrise, doing it now...")) if err := bitrise.RunSetup(c.App.Version, false); err != nil { log.Fatalln("Setup failed:", err) } } startTime := time.Now() // Inventory validation inventoryEnvironments, err := CreateInventoryFromCLIParams(c) if err != nil { log.Fatalf("Failed to create inventory, err: %s", err) } if err := checkCIAndPRModeFromSecrets(inventoryEnvironments); err != nil { log.Fatalf("Failed to check PR and CI mode, err: %s", err) } // Config validation bitriseConfig, err := CreateBitriseConfigFromCLIParams(c) if err != nil { log.Fatalf("Failed to create bitrise config, err: %s", err) } // Workflow validation workflowToRunID := "" if len(c.Args()) < 1 { log.Errorln("No workfow specified!") } else { workflowToRunID = c.Args()[0] } if workflowToRunID == "" { // no workflow specified // list all the available ones and then exit printAvailableWorkflows(bitriseConfig) } if strings.HasPrefix(workflowToRunID, "_") { // util workflow specified // print about util workflows and then exit printAboutUtilityWorkflos() } // Run selected configuration if _, err := runWorkflowWithConfiguration(startTime, workflowToRunID, bitriseConfig, inventoryEnvironments); err != nil { log.Fatalln("Error: ", err) } }
// 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 }
// GuideTextForShareFinish ... func GuideTextForShareFinish(toolMode bool) string { name := "stepman" if toolMode { name = "bitrise" } guide := `Almost done! You should review your Step's step.yml file (the one added to the local StepLib), and once you're happy with it call: ` + colorstring.Blue("'"+name+" share finish'") + ` This will commit & push the step.yml ` + colorstring.Yellow("into your forked StepLib repository") + `. ` return guide }
func runAndExit(bitriseConfig models.BitriseDataModel, inventoryEnvironments []envmanModels.EnvironmentItemModel, workflowToRunID string) { if workflowToRunID == "" { log.Fatal("No workflow id specified") } if !configs.CheckIsSetupWasDoneForVersion(version.VERSION) { log.Warnln(colorstring.Yellow("Setup was not performed for this version of bitrise, doing it now...")) if err := bitrise.RunSetup(version.VERSION, false); err != nil { log.Fatalf("Setup failed, error: %s", err) } } startTime := time.Now() // Run selected configuration if buildRunResults, err := runWorkflowWithConfiguration(startTime, workflowToRunID, bitriseConfig, inventoryEnvironments); err != nil { log.Fatalf("Failed to run workflow, error: %s", err) } else if buildRunResults.IsBuildFailed() { os.Exit(1) } os.Exit(0) }
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 scanXcodeProject(cmd *cobra.Command, args []string) error { absExportOutputDirPath, err := initExportOutputDir() if err != nil { return printXcodeScanFinishedWithError("Failed to prepare Export directory: %s", err) } xcodebuildOutput := "" xcodeCmd := xcode.CommandModel{} if paramXcodebuildOutputLogFilePath != "" { xcLog, err := fileutil.ReadStringFromFile(paramXcodebuildOutputLogFilePath) if err != nil { return printXcodeScanFinishedWithError("Failed to read log from the specified log file, error: %s", err) } xcodebuildOutput = xcLog } else { projectPath := paramXcodeProjectFilePath if projectPath == "" { askText := `Please drag-and-drop your Xcode Project (` + colorstring.Green(".xcodeproj") + `) or Workspace (` + colorstring.Green(".xcworkspace") + `) file, the one you usually open in Xcode, then hit Enter. (Note: if you have a Workspace file you should most likely use that)` fmt.Println() projpth, err := goinp.AskForPath(askText) if err != nil { return printXcodeScanFinishedWithError("Failed to read input: %s", err) } projectPath = projpth } log.Debugf("projectPath: %s", projectPath) xcodeCmd.ProjectFilePath = projectPath schemeToUse := paramXcodeScheme if schemeToUse == "" { fmt.Println() fmt.Println() log.Println("🔦 Scanning Schemes ...") schemes, err := xcodeCmd.ScanSchemes() if err != nil { return printXcodeScanFinishedWithError("Failed to scan Schemes: %s", err) } log.Debugf("schemes: %v", schemes) fmt.Println() selectedScheme, err := goinp.SelectFromStrings("Select the Scheme you usually use in Xcode", schemes) if err != nil { return printXcodeScanFinishedWithError("Failed to select Scheme: %s", err) } log.Debugf("selected scheme: %v", selectedScheme) schemeToUse = selectedScheme } xcodeCmd.Scheme = schemeToUse fmt.Println() fmt.Println() log.Println("🔦 Running an Xcode Archive, to get all the required code signing settings...") xcLog, err := xcodeCmd.GenerateLog() xcodebuildOutput = xcLog // save the xcodebuild output into a debug log file xcodebuildOutputFilePath := filepath.Join(absExportOutputDirPath, "xcodebuild-output.log") { log.Infof(" 💡 "+colorstring.Yellow("Saving xcodebuild output into file")+": %s", xcodebuildOutputFilePath) if logWriteErr := fileutil.WriteStringToFile(xcodebuildOutputFilePath, xcodebuildOutput); logWriteErr != nil { log.Errorf("Failed to save xcodebuild output into file (%s), error: %s", xcodebuildOutputFilePath, logWriteErr) } else if err != nil { log.Infoln(colorstring.Yellow("Please check the logfile (" + xcodebuildOutputFilePath + ") to see what caused the error")) log.Infoln(colorstring.Red("and make sure that you can Archive this project from Xcode!")) fmt.Println() log.Infoln("Open the project:", xcodeCmd.ProjectFilePath) log.Infoln("and Archive, using the Scheme:", xcodeCmd.Scheme) fmt.Println() } } if err != nil { return printXcodeScanFinishedWithError("Failed to run Xcode Archive: %s", err) } } codeSigningSettings, err := xcodeCmd.ScanCodeSigningSettings(xcodebuildOutput) if err != nil { return printXcodeScanFinishedWithError("Failed to detect code signing settings: %s", err) } log.Debugf("codeSigningSettings: %#v", codeSigningSettings) return exportCodeSigningFiles("Xcode", absExportOutputDirPath, codeSigningSettings) }
// Warn ... func Warn(format string, v ...interface{}) { message := fmt.Sprintf(format, v...) fmt.Println(colorstring.Yellow(message)) }
func activateAndRunSteps(workflow models.WorkflowModel, defaultStepLibSource string, buildRunResults models.BuildRunResultsModel, environments *[]envmanModels.EnvironmentItemModel, isLastWorkflow bool) models.BuildRunResultsModel { log.Debugln("[BITRISE_CLI] - Activating and running steps") // ------------------------------------------ // In function global variables - These are global for easy use in local register step run result methods. var stepStartTime time.Time // ------------------------------------------ // In function method - Registration methods, for register step run results. registerStepRunResults := func(step stepmanModels.StepModel, stepInfoPtr stepmanModels.StepInfoModel, stepIdxPtr int, runIf string, resultCode, exitCode int, err error, isLastStep, printStepHeader bool) { if printStepHeader { bitrise.PrintRunningStepHeader(stepInfoPtr, step, stepIdxPtr) } stepInfoCopy := stepmanModels.StepInfoModel{ ID: stepInfoPtr.ID, Title: stepInfoPtr.Title, Version: stepInfoPtr.Version, Latest: stepInfoPtr.Latest, SupportURL: stepInfoPtr.SupportURL, SourceCodeURL: stepInfoPtr.SourceCodeURL, GlobalInfo: stepInfoPtr.GlobalInfo, } stepResults := models.StepRunResultsModel{ StepInfo: stepInfoCopy, Status: resultCode, Idx: buildRunResults.ResultsCount(), RunTime: time.Now().Sub(stepStartTime), Error: err, ExitCode: exitCode, } isExitStatusError := true if err != nil { isExitStatusError = errorutil.IsExitStatusError(err) } switch resultCode { case models.StepRunStatusCodeSuccess: buildRunResults.SuccessSteps = append(buildRunResults.SuccessSteps, stepResults) break case models.StepRunStatusCodeFailed: if !isExitStatusError { log.Errorf("Step (%s) failed, error: %s", stepInfoCopy.Title, err) } buildRunResults.FailedSteps = append(buildRunResults.FailedSteps, stepResults) break case models.StepRunStatusCodeFailedSkippable: if !isExitStatusError { log.Warnf("Step (%s) failed, but was marked as skippable, error: %s", stepInfoCopy.Title, err) } else { log.Warnf("Step (%s) failed, but was marked as skippable", stepInfoCopy.Title) } buildRunResults.FailedSkippableSteps = append(buildRunResults.FailedSkippableSteps, stepResults) break case models.StepRunStatusCodeSkipped: log.Warnf("A previous step failed, and this step (%s) was not marked as IsAlwaysRun, skipped", stepInfoCopy.Title) buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break case models.StepRunStatusCodeSkippedWithRunIf: log.Warn("The step's (" + stepInfoCopy.Title + ") Run-If expression evaluated to false - skipping") if runIf != "" { log.Info("The Run-If expression was: ", colorstring.Blue(runIf)) } buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break default: log.Error("Unkown result code") return } bitrise.PrintRunningStepFooter(stepResults, isLastStep) } // ------------------------------------------ // Main - Preparing & running the steps for idx, stepListItm := range workflow.Steps { // Per step variables stepStartTime = time.Now() isLastStep := isLastWorkflow && (idx == len(workflow.Steps)-1) stepInfoPtr := stepmanModels.StepInfoModel{} stepIdxPtr := idx // Per step cleanup if err := bitrise.SetBuildFailedEnv(buildRunResults.IsBuildFailed()); err != nil { log.Error("Failed to set Build Status envs") } if err := bitrise.CleanupStepWorkDir(); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } // // Preparing the step if err := tools.EnvmanInitAtPath(configs.InputEnvstorePath); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := bitrise.ExportEnvironmentsList(*environments); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } // Get step id & version data compositeStepIDStr, workflowStep, err := models.GetStepIDStepDataPair(stepListItm) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } stepInfoPtr.ID = compositeStepIDStr if workflowStep.Title != nil && *workflowStep.Title != "" { stepInfoPtr.Title = *workflowStep.Title } else { stepInfoPtr.Title = compositeStepIDStr } stepIDData, err := models.CreateStepIDDataFromString(compositeStepIDStr, defaultStepLibSource) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } stepInfoPtr.ID = stepIDData.IDorURI if stepInfoPtr.Title == "" { stepInfoPtr.Title = stepIDData.IDorURI } stepInfoPtr.Version = stepIDData.Version stepInfoPtr.StepLib = stepIDData.SteplibSource // // Activating the step stepDir := configs.BitriseWorkStepsDirPath stepYMLPth := filepath.Join(configs.BitriseWorkDirPath, "current_step.yml") if stepIDData.SteplibSource == "path" { log.Debugf("[BITRISE_CLI] - Local step found: (path:%s)", stepIDData.IDorURI) stepAbsLocalPth, err := pathutil.AbsPath(stepIDData.IDorURI) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } log.Debugln("stepAbsLocalPth:", stepAbsLocalPth, "|stepDir:", stepDir) if err := cmdex.CopyDir(stepAbsLocalPth, stepDir, true); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := cmdex.CopyFile(filepath.Join(stepAbsLocalPth, "step.yml"), stepYMLPth); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } } else if stepIDData.SteplibSource == "git" { log.Debugf("[BITRISE_CLI] - Remote step, with direct git uri: (uri:%s) (tag-or-branch:%s)", stepIDData.IDorURI, stepIDData.Version) if err := cmdex.GitCloneTagOrBranch(stepIDData.IDorURI, stepDir, stepIDData.Version); err != nil { if strings.HasPrefix(stepIDData.IDorURI, "git@") { fmt.Println(colorstring.Yellow(`Note: if the step's repository is an open source one,`)) fmt.Println(colorstring.Yellow(`you should probably use a "https://..." git clone URL,`)) fmt.Println(colorstring.Yellow(`instead of the "git@..." git clone URL which usually requires authentication`)) fmt.Println(colorstring.Yellow(`even if the repository is open source!`)) } registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := cmdex.CopyFile(filepath.Join(stepDir, "step.yml"), stepYMLPth); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } } else if stepIDData.SteplibSource == "_" { log.Debugf("[BITRISE_CLI] - Steplib independent step, with direct git uri: (uri:%s) (tag-or-branch:%s)", stepIDData.IDorURI, stepIDData.Version) // Steplib independent steps are completly defined in workflow stepYMLPth = "" if err := workflowStep.FillMissingDefaults(); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := cmdex.GitCloneTagOrBranch(stepIDData.IDorURI, stepDir, stepIDData.Version); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } } else if stepIDData.SteplibSource != "" { log.Debugf("[BITRISE_CLI] - Steplib (%s) step (id:%s) (version:%s) found, activating step", stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err := tools.StepmanSetup(stepIDData.SteplibSource); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } isLatestVersionOfStep := (stepIDData.Version == "") if isLatestVersionOfStep && !buildRunResults.IsStepLibUpdated(stepIDData.SteplibSource) { log.Infof("Step uses latest version -- Updating StepLib ...") if err := tools.StepmanUpdate(stepIDData.SteplibSource); err != nil { log.Warnf("Step uses latest version, but failed to update StepLib, err: %s", err) } else { buildRunResults.StepmanUpdates[stepIDData.SteplibSource]++ } } outStr, err := tools.StepmanJSONStepLibStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { if buildRunResults.IsStepLibUpdated(stepIDData.SteplibSource) { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, fmt.Errorf("StepmanJSONStepLibStepInfo failed, err: %s", err), isLastStep, true) continue } // May StepLib should be updated log.Infof("Step info not found in StepLib (%s) -- Updating ...", stepIDData.SteplibSource) if err := tools.StepmanUpdate(stepIDData.SteplibSource); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } buildRunResults.StepmanUpdates[stepIDData.SteplibSource]++ outStr, err = tools.StepmanJSONStepLibStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, fmt.Errorf("StepmanJSONStepLibStepInfo failed, err: %s", err), isLastStep, true) continue } } stepInfo, err := stepmanModels.StepInfoModel{}.CreateFromJSON(outStr) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, fmt.Errorf("CreateFromJSON failed, err: %s", err), isLastStep, true) continue } stepInfoPtr.ID = stepInfo.ID if stepInfoPtr.Title == "" { stepInfoPtr.Title = stepInfo.ID } stepInfoPtr.Version = stepInfo.Version stepInfoPtr.Latest = stepInfo.Latest stepInfoPtr.GlobalInfo = stepInfo.GlobalInfo if err := tools.StepmanActivate(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version, stepDir, stepYMLPth); err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } else { log.Debugf("[BITRISE_CLI] - Step activated: (ID:%s) (version:%s)", stepIDData.IDorURI, stepIDData.Version) } } else { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, fmt.Errorf("Invalid stepIDData: No SteplibSource or LocalPath defined (%v)", stepIDData), isLastStep, true) continue } // Fill step info with default step info, if exist mergedStep := workflowStep if stepYMLPth != "" { specStep, err := bitrise.ReadSpecStep(stepYMLPth) log.Debugf("Spec read from YML: %#v\n", specStep) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } mergedStep, err = models.MergeStepWith(specStep, workflowStep) if err != nil { registerStepRunResults(stepmanModels.StepModel{}, stepInfoPtr, stepIdxPtr, "", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } } if mergedStep.SupportURL != nil { stepInfoPtr.SupportURL = *mergedStep.SupportURL } if mergedStep.SourceCodeURL != nil { stepInfoPtr.SourceCodeURL = *mergedStep.SourceCodeURL } // // Run step bitrise.PrintRunningStepHeader(stepInfoPtr, mergedStep, idx) if mergedStep.RunIf != nil && *mergedStep.RunIf != "" { outStr, err := tools.EnvmanJSONPrint(configs.InputEnvstorePath) if err != nil { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeFailed, 1, fmt.Errorf("EnvmanJSONPrint failed, err: %s", err), isLastStep, false) continue } envList, err := envmanModels.NewEnvJSONList(outStr) if err != nil { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeFailed, 1, fmt.Errorf("CreateFromJSON failed, err: %s", err), isLastStep, false) continue } isRun, err := bitrise.EvaluateTemplateToBool(*mergedStep.RunIf, configs.IsCIMode, configs.IsPullRequestMode, buildRunResults, envList) if err != nil { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeFailed, 1, err, isLastStep, false) continue } if !isRun { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeSkippedWithRunIf, 0, err, isLastStep, false) continue } } isAlwaysRun := stepmanModels.DefaultIsAlwaysRun if mergedStep.IsAlwaysRun != nil { isAlwaysRun = *mergedStep.IsAlwaysRun } else { log.Warn("Step (%s) mergedStep.IsAlwaysRun is nil, should not!", stepIDData.IDorURI) } if buildRunResults.IsBuildFailed() && !isAlwaysRun { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeSkipped, 0, err, isLastStep, false) } else { exit, outEnvironments, err := runStep(mergedStep, stepIDData, stepDir, *environments, buildRunResults) *environments = append(*environments, outEnvironments...) if err != nil { if *mergedStep.IsSkippable { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeFailedSkippable, exit, err, isLastStep, false) } else { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeFailed, exit, err, isLastStep, false) } } else { registerStepRunResults(mergedStep, stepInfoPtr, stepIdxPtr, *mergedStep.RunIf, models.StepRunStatusCodeSuccess, 0, nil, isLastStep, false) } } } return buildRunResults }
func exportCodeSigningFiles(toolName, absExportOutputDirPath string, codeSigningSettings common.CodeSigningSettings) error { fmt.Println() fmt.Println() utils.Printlnf("=== Required Identities/Certificates (%d) ===", len(codeSigningSettings.Identities)) for idx, anIdentity := range codeSigningSettings.Identities { utils.Printlnf(" * (%d): %s", idx+1, anIdentity.Title) } fmt.Println("============================================") fmt.Println() utils.Printlnf("=== Required Provisioning Profiles (%d) ===", len(codeSigningSettings.ProvProfiles)) for idx, aProvProfile := range codeSigningSettings.ProvProfiles { utils.Printlnf(" * (%d): %s (UUID: %s)", idx+1, aProvProfile.Title, aProvProfile.UUID) } fmt.Println("==========================================") fmt.Println() utils.Printlnf("=== Team IDs (%d) ===", len(codeSigningSettings.TeamIDs)) for idx, aTeamID := range codeSigningSettings.TeamIDs { utils.Printlnf(" * (%d): %s", idx+1, aTeamID) } fmt.Println("==========================================") fmt.Println() utils.Printlnf("=== App/Bundle IDs (%d) ===", len(codeSigningSettings.AppIDs)) for idx, anAppBundleID := range codeSigningSettings.AppIDs { utils.Printlnf(" * (%d): %s", idx+1, anAppBundleID) } fmt.Println("==========================================") fmt.Println() // // --- Code Signing issue checks / report // if len(codeSigningSettings.Identities) < 1 { return printFinishedWithError(toolName, "No Code Signing Identity detected!") } if len(codeSigningSettings.Identities) > 1 { log.Warning(colorstring.Yellow("More than one Code Signing Identity (certificate) is required to sign your app!")) log.Warning("You should check your settings and make sure a single Identity/Certificate can be used") log.Warning(" for Archiving your app!") } if len(codeSigningSettings.ProvProfiles) < 1 { return printFinishedWithError(toolName, "No Provisioning Profiles detected!") } // // --- Export // if !isAllowExport { isShouldExport, err := goinp.AskForBoolWithDefault("Do you want to export these files?", true) if err != nil { return printFinishedWithError(toolName, "Failed to process your input: %s", err) } if !isShouldExport { printFinished() return nil } } else { log.Debug("Allow Export flag was set - doing export without asking") } exportedProvProfiles, err := collectAndExportProvisioningProfiles(codeSigningSettings, absExportOutputDirPath) if err != nil { return printFinishedWithError(toolName, "Failed to export Provisioning Profiles, error: %s", err) } if err := collectAndExportIdentities(codeSigningSettings, exportedProvProfiles.CollectTeamIDs(), absExportOutputDirPath); err != nil { return printFinishedWithError(toolName, "Failed to export identities, error: %s", err) } fmt.Println() fmt.Printf(colorstring.Green("Exports finished")+" you can find the exported files at: %s\n", absExportOutputDirPath) if err := cmdex.RunCommand("open", absExportOutputDirPath); err != nil { log.Errorf("Failed to open the export directory in Finder: %s", absExportOutputDirPath) } fmt.Println("Opened the directory in Finder.") fmt.Println() printFinished() 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 }
// 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 }
func collectAndExportIdentities(codeSigningSettings common.CodeSigningSettings, additionalTeamIDs []string, absExportOutputDirPath string) error { fmt.Println() log.Println("Collecting the required Identities (Certificates) for a base Xcode Archive ...") fmt.Println() identitiesWithKeychainRefs := []osxkeychain.IdentityWithRefModel{} defer osxkeychain.ReleaseIdentityWithRefList(identitiesWithKeychainRefs) for _, aIdentity := range codeSigningSettings.Identities { log.Infof(" * "+colorstring.Blue("Searching for Identity")+": %s", aIdentity.Title) validIdentityRefs, err := osxkeychain.FindAndValidateIdentity(aIdentity.Title, true) if err != nil { return fmt.Errorf("Failed to export, error: %s", err) } if len(validIdentityRefs) < 1 { return errors.New("Identity not found in the keychain, or it was invalid (expired)!") } if len(validIdentityRefs) > 1 { log.Warning(colorstring.Yellow("Multiple matching Identities found in Keychain! Most likely you have duplicated identities in separate Keychains, e.g. one in System.keychain and one in your Login.keychain, or you have revoked versions of the Certificate.")) } else { log.Infoln(" " + colorstring.Green("Found - OK")) } identitiesWithKeychainRefs = append(identitiesWithKeychainRefs, validIdentityRefs...) } fmt.Println() log.Println("Collecting additional identities, for Distribution builds ...") fmt.Println() totalTeamIDs := append(codeSigningSettings.TeamIDs, additionalTeamIDs...) for _, aTeamID := range sliceutil.UniqueStringSlice(totalTeamIDs) { log.Infof(" * "+colorstring.Blue("Searching for Identities with Team ID")+": %s", aTeamID) validIdentityRefs, err := osxkeychain.FindAndValidateIdentity(fmt.Sprintf("(%s)", aTeamID), false) if err != nil { return fmt.Errorf("Failed to export, error: %s", err) } if len(validIdentityRefs) < 1 { log.Infoln("No valid identity found for this Team ID") } log.Infoln(" "+colorstring.Green("Found identities count")+":", len(validIdentityRefs)) identitiesWithKeychainRefs = append(identitiesWithKeychainRefs, validIdentityRefs...) } fmt.Println() log.Println(colorstring.Green("Exporting the Identities") + " (Certificates):") fmt.Println() identityKechainRefs := osxkeychain.CreateEmptyCFTypeRefSlice() for _, aIdentityWithRefItm := range identitiesWithKeychainRefs { fmt.Println(" * "+colorstring.Blue("Identity")+":", aIdentityWithRefItm.Label) identityKechainRefs = append(identityKechainRefs, aIdentityWithRefItm.KeychainRef) } fmt.Println() if isAskForPassword { log.Infoln(colorstring.Blue("Exporting from Keychain")) log.Infoln(colorstring.Yellow(" You'll be asked to provide a Passphrase for the .p12 file!")) } else { log.Infoln(colorstring.Blue("Exporting from Keychain") + ", " + colorstring.Yellow("using empty Passphrase") + " ...") log.Info(" This means that " + colorstring.Yellow("if you want to import the file the passphrase at import should be left empty") + ",") log.Info(" you don't have to type in anything, just leave the passphrase input empty.") } fmt.Println() log.Info(colorstring.Blue("You'll most likely see popups") + " (one for each Identity) from Keychain,") log.Info(colorstring.Yellow(" you will have to accept (Allow)") + " those to be able to export the Identities!") fmt.Println() if err := osxkeychain.ExportFromKeychain(identityKechainRefs, filepath.Join(absExportOutputDirPath, "Identities.p12"), isAskForPassword); err != nil { return fmt.Errorf("Failed to export from Keychain: %s", err) } return nil }