func printRawStepInfo(stepInfo models.StepInfoModel, isShort, isLocal bool) { if isLocal { fmt.Println(colorstring.Bluef("Local step info, yml path (%s):", stepInfo.StepLib)) } else { fmt.Println(colorstring.Bluef("Step info in StepLib (%s):", stepInfo.StepLib)) } if stepInfo.GlobalInfo.RemovalDate != "" { fmt.Println("") fmt.Println(colorstring.Red("This step is deprecated!")) fmt.Printf("%s %s\n", colorstring.Red("removal date:"), stepInfo.GlobalInfo.RemovalDate) if stepInfo.GlobalInfo.DeprecateNotes != "" { fmt.Printf("%s\n%s\n", colorstring.Red("deprecate notes:"), stepInfo.GlobalInfo.DeprecateNotes) } } if stepInfo.ID != "" { fmt.Printf("%s: %s\n", colorstring.Blue("ID"), stepInfo.ID) } if stepInfo.Version != "" { fmt.Printf("%s: %s\n", colorstring.Blue("version"), stepInfo.Version) } if stepInfo.Latest != "" { fmt.Printf("%s: %s\n", colorstring.Blue("latest"), stepInfo.Latest) } if !isShort { fmt.Printf("%s: %s\n", colorstring.Blue("source"), stepInfo.Source) fmt.Printf("%s:\n", colorstring.Blue("description")) fmt.Printf("%s\n", stepInfo.Description) fmt.Println() if len(stepInfo.Inputs) > 0 { fmt.Printf("%s:\n", colorstring.Blue("inputs")) for _, input := range stepInfo.Inputs { printRawEnvInfo(input) } } if len(stepInfo.Outputs) > 0 { if len(stepInfo.Inputs) > 0 { fmt.Println() } fmt.Printf("%s:\n", colorstring.Blue("outputs")) for _, output := range stepInfo.Outputs { printRawEnvInfo(output) } } } fmt.Println() fmt.Println() }
func printFinishedWithError(toolName, format string, args ...interface{}) error { fmt.Println() fmt.Println("------------------------------") fmt.Println("First of all " + colorstring.Red("please make sure that you can Archive your app from "+toolName+".")) fmt.Println("codesigndoc only works if you can archive your app from " + toolName + ".") fmt.Println("If you can, and you get a valid IPA file if you export from " + toolName + ",") fmt.Println(colorstring.Red("please create an issue") + " on GitHub at: https://github.com/bitrise-tools/codesigndoc/issues") fmt.Println("with as many details & logs as you can share!") fmt.Println("------------------------------") fmt.Println() return fmt.Errorf(colorstring.Red("Error: ")+format, args...) }
func (v ValidateResponseModel) String() string { if v.Error != "" { msg := fmt.Sprintf("%s: %s", colorstring.Red("Error"), v.Error) if len(v.Warnings) > 0 { msg += "\nWarning(s):\n" for i, warning := range v.Warnings { msg += fmt.Sprintf("- %s", warning) if i != len(v.Warnings)-1 { msg += "\n" } } } return msg } if v.Data != nil { msg := v.Data.String() if len(v.Warnings) > 0 { msg += "\nWarning(s):\n" for i, warning := range v.Warnings { msg += fmt.Sprintf("- %s", warning) if i != len(v.Warnings)-1 { msg += "\n" } } } return msg } return "" }
func getRunningStepFooterMainSection(stepRunResult models.StepRunResultsModel) string { iconBoxWidth := len(" ") timeBoxWidth := len(" time (s) ") titleBoxWidth := stepRunSummaryBoxWidthInChars - 4 - iconBoxWidth - timeBoxWidth - 1 icon := "" title := getTrimmedStepName(stepRunResult) coloringFunc := colorstring.Green switch stepRunResult.Status { case models.StepRunStatusCodeSuccess: icon = "✓" coloringFunc = colorstring.Green break case models.StepRunStatusCodeFailed: icon = "x" coloringFunc = colorstring.Red break case models.StepRunStatusCodeFailedSkippable: icon = "!" coloringFunc = colorstring.Yellow break case models.StepRunStatusCodeSkipped, models.StepRunStatusCodeSkippedWithRunIf: icon = "-" coloringFunc = colorstring.Blue break default: log.Error("Unkown result code") return "" } iconBox := fmt.Sprintf(" %s ", coloringFunc(icon)) titleWhiteSpaceWidth := titleBoxWidth - len(title) coloredTitle := title if strings.HasPrefix(title, "[Deprecated]") { title := strings.TrimPrefix(title, "[Deprecated]") coloredTitle = fmt.Sprintf("%s%s", colorstring.Red("[Deprecated]"), coloringFunc(title)) } else { coloredTitle = coloringFunc(title) } titleBox := fmt.Sprintf(" %s%s", coloredTitle, strings.Repeat(" ", titleWhiteSpaceWidth)) runTimeStr, err := FormattedSecondsToMax8Chars(stepRunResult.RunTime) if err != nil { log.Errorf("Failed to format time, error: %s", err) runTimeStr = "999+ hour" } timeWhiteSpaceWidth := timeBoxWidth - len(runTimeStr) - 1 if timeWhiteSpaceWidth < 0 { log.Errorf("Invalid time box size for RunTime: %#v", stepRunResult.RunTime) timeWhiteSpaceWidth = 0 } timeBox := fmt.Sprintf(" %s%s", runTimeStr, strings.Repeat(" ", timeWhiteSpaceWidth)) return fmt.Sprintf("|%s|%s|%s|", iconBox, titleBox, timeBox) }
func printRawValidation(validation ValidationModel) error { validConfig := true if validation.Config != nil { fmt.Println(colorstring.Blue("Config validation result:")) configValidation := *validation.Config if configValidation.IsValid { fmt.Printf("is valid: %s\n", colorstring.Greenf("%v", configValidation.IsValid)) } else { fmt.Printf("is valid: %s\n", colorstring.Redf("%v", configValidation.IsValid)) fmt.Printf("error: %s\n", colorstring.Red(configValidation.Error)) validConfig = false } fmt.Println() } validSecrets := true if validation.Secrets != nil { fmt.Println(colorstring.Blue("Secret validation result:")) secretValidation := *validation.Secrets if secretValidation.IsValid { fmt.Printf("is valid: %s\n", colorstring.Greenf("%v", secretValidation.IsValid)) } else { fmt.Printf("is valid: %s\n", colorstring.Redf("%v", secretValidation.IsValid)) fmt.Printf("error: %s\n", colorstring.Red(secretValidation.Error)) validSecrets = false } } if !validConfig && !validSecrets { return errors.New("Config and secrets are invalid") } else if !validConfig { return errors.New("Config is invalid") } else if !validSecrets { return errors.New("Secret is invalid") } return nil }
// String ... func (v ValidationModel) String() string { msg := "" if v.Config != nil { config := *v.Config if config.IsValid { msg += fmt.Sprintf("Config is valid: %s", colorstring.Greenf("%v", true)) } else { msg += fmt.Sprintf("Config is valid: %s", colorstring.Redf("%v", false)) msg += fmt.Sprintf("\nError: %s", colorstring.Red(config.Error)) } if len(config.Warnings) > 0 { msg += "\nWarning(s):\n" for i, warning := range config.Warnings { msg += fmt.Sprintf("- %s", warning) if i != len(config.Warnings)-1 { msg += "\n" } } } } if v.Secrets != nil { if v.Config != nil { msg += "\n" } secret := *v.Secrets if secret.IsValid { msg += fmt.Sprintf("Secret is valid: %s", colorstring.Greenf("%v", true)) } else { msg += fmt.Sprintf("Secret is valid: %s", colorstring.Redf("%v", false)) msg += fmt.Sprintf("\nError: %s", colorstring.Red(secret.Error)) } } return msg }
func getRunningStepFooterMainSection(stepRunResult models.StepRunResultsModel) string { iconBoxWidth := len(" ") timeBoxWidth := len(" time (s) ") titleBoxWidth := stepRunSummaryBoxWidthInChars - 4 - iconBoxWidth - timeBoxWidth - 1 icon := "" title := getTrimmedStepName(stepRunResult) runTimeStr := TimeToFormattedSeconds(stepRunResult.RunTime, " sec") coloringFunc := colorstring.Green switch stepRunResult.Status { case models.StepRunStatusCodeSuccess: icon = "✅" coloringFunc = colorstring.Green break case models.StepRunStatusCodeFailed: icon = "🚫" coloringFunc = colorstring.Red break case models.StepRunStatusCodeFailedSkippable: icon = "⚠�" coloringFunc = colorstring.Yellow break case models.StepRunStatusCodeSkipped, models.StepRunStatusCodeSkippedWithRunIf: icon = "➡" coloringFunc = colorstring.Blue break default: log.Error("Unkown result code") return "" } iconBox := fmt.Sprintf(" %s ", icon) titleWhiteSpaceWidth := titleBoxWidth - len(title) coloredTitle := title if strings.HasPrefix(title, "[Deprecated]") { title := strings.TrimPrefix(title, "[Deprecated]") coloredTitle = fmt.Sprintf("%s%s", colorstring.Red("[Deprecated]"), coloringFunc(title)) } else { coloredTitle = coloringFunc(title) } titleBox := fmt.Sprintf(" %s%s", coloredTitle, strings.Repeat(" ", titleWhiteSpaceWidth)) timeWhiteSpaceWidth := timeBoxWidth - len(runTimeStr) - 1 timeBox := fmt.Sprintf(" %s%s", runTimeStr, strings.Repeat(" ", timeWhiteSpaceWidth)) return fmt.Sprintf("|%s|%s|%s|", iconBox, titleBox, timeBox) }
func getRunningStepFooterSubSection(stepRunResult models.StepRunResultsModel) string { stepInfo := stepRunResult.StepInfo removalDate := stepInfo.GlobalInfo.RemovalDate deprecateNotes := stepInfo.GlobalInfo.DeprecateNotes removalDateRow := "" deprecateNotesRow := "" if removalDate != "" { removalDateValue := removalDate removalDateKey := colorstring.Red("Removal date:") removalDateRow = fmt.Sprintf("| Removal date: %s |", removalDateValue) charDiff := len(removalDateRow) - stepRunSummaryBoxWidthInChars removalDateRow = fmt.Sprintf("| %s %s%s |", removalDateKey, removalDateValue, strings.Repeat(" ", -charDiff)) if deprecateNotes != "" { deprecateNotesStr := fmt.Sprintf("Removal notes: %s", deprecateNotes) deprecateNotesRow = getDeprecateNotesRows(deprecateNotesStr) } } isUpdateAvailable := isUpdateAvailable(stepRunResult.StepInfo) updateRow := "" if isUpdateAvailable { updateRow = fmt.Sprintf("| Update available: %s -> %s |", stepInfo.Version, stepInfo.Latest) charDiff := len(updateRow) - stepRunSummaryBoxWidthInChars if charDiff < 0 { // shorter than desired - fill with space updateRow = fmt.Sprintf("| Update available: %s -> %s%s |", stepInfo.Version, stepInfo.Latest, strings.Repeat(" ", -charDiff)) } else if charDiff > 0 { // longer than desired - trim title if charDiff > 6 { updateRow = fmt.Sprintf("| Update available!%s |", strings.Repeat(" ", -len("| Update available! |")-stepRunSummaryBoxWidthInChars)) } else { updateRow = fmt.Sprintf("| Update available: -> %s%s |", stepInfo.Latest, strings.Repeat(" ", -len("| Update available: -> %s |")-stepRunSummaryBoxWidthInChars)) } } } issueRow := "" sourceRow := "" if stepRunResult.Error != nil { // Support URL var coloringFunc func(...interface{}) string supportURL := stepInfo.SupportURL if supportURL == "" { coloringFunc = colorstring.Yellow supportURL = "Not provided" } issueRow = fmt.Sprintf("| Issue tracker: %s |", supportURL) charDiff := len(issueRow) - stepRunSummaryBoxWidthInChars if charDiff <= 0 { // shorter than desired - fill with space if coloringFunc != nil { // We need to do this after charDiff calculation, // because of coloring characters increase the text length, but they do not printed supportURL = coloringFunc("Not provided") } issueRow = fmt.Sprintf("| Issue tracker: %s%s |", supportURL, strings.Repeat(" ", -charDiff)) } else if charDiff > 0 { // longer than desired - trim title trimmedWidth := len(supportURL) - charDiff if trimmedWidth < 4 { log.Errorf("Support url too long, can't present support url at all! : %s", supportURL) } else { issueRow = fmt.Sprintf("| Issue tracker: %s |", stringutil.MaxLastCharsWithDots(supportURL, trimmedWidth)) } } // Source Code URL coloringFunc = nil sourceCodeURL := stepInfo.SourceCodeURL if sourceCodeURL == "" { coloringFunc = colorstring.Yellow sourceCodeURL = "Not provided" } sourceRow = fmt.Sprintf("| Source: %s |", sourceCodeURL) charDiff = len(sourceRow) - stepRunSummaryBoxWidthInChars if charDiff <= 0 { // shorter than desired - fill with space if coloringFunc != nil { // We need to do this after charDiff calculation, // because of coloring characters increase the text length, but they do not printed sourceCodeURL = coloringFunc("Not provided") } sourceRow = fmt.Sprintf("| Source: %s%s |", sourceCodeURL, strings.Repeat(" ", -charDiff)) } else if charDiff > 0 { // longer than desired - trim title trimmedWidth := len(sourceCodeURL) - charDiff if trimmedWidth < 4 { log.Errorf("Source url too long, can't present source url at all! : %s", sourceCodeURL) } else { sourceRow = fmt.Sprintf("| Source: %s |", stringutil.MaxLastCharsWithDots(sourceCodeURL, trimmedWidth)) } } } // Update available content := "" if isUpdateAvailable { content = fmt.Sprintf("%s", updateRow) } // Support URL if content != "" { content = fmt.Sprintf("%s\n%s", content, issueRow) } else { content = fmt.Sprintf("%s", issueRow) } // Source Code URL if content != "" { content = fmt.Sprintf("%s\n%s", content, sourceRow) } else { content = fmt.Sprintf("%s", sourceRow) } // Deprecation if removalDate != "" { if content != "" { content = fmt.Sprintf("%s\n%s", content, removalDateRow) } else { content = fmt.Sprintf("%s", removalDateRow) } if deprecateNotes != "" { if content != "" { content = fmt.Sprintf("%s\n%s", content, deprecateNotesRow) } else { content = fmt.Sprintf("%s", deprecateNotesRow) } } } return content }
func getDeprecateNotesRows(notes string) string { colorDeprecateNote := func(line string) string { if strings.HasPrefix(line, "Removal notes:") { line = strings.TrimPrefix(line, "Removal notes:") line = fmt.Sprintf("%s%s", colorstring.Red("Removal notes:"), line) } return line } boxContentWidth := stepRunSummaryBoxWidthInChars - 4 notesWithoutNewLine := strings.Replace(notes, "\n", " ", -1) words := strings.Split(notesWithoutNewLine, " ") if len(words) == 0 { return "" } formattedNote := "" line := "" for i, word := range words { isLastLine := (i == len(words)-1) expectedLine := "" if line == "" { expectedLine = word } else { expectedLine = line + " " + word } if utf8.RuneCountInString(expectedLine) > boxContentWidth { // expected line would be to long, so print the previous line, and start a new with the last word. noteRow := fmt.Sprintf("| %s |", line) charDiff := len(noteRow) - stepRunSummaryBoxWidthInChars if charDiff <= 0 { // shorter than desired - fill with space line = colorDeprecateNote(line) noteRow = fmt.Sprintf("| %s%s |", line, strings.Repeat(" ", -charDiff)) } else if charDiff > 0 { // longer than desired - should not log.Errorln("Should not be longer then expected") } if formattedNote == "" { formattedNote = noteRow } else { formattedNote = fmt.Sprintf("%s\n%s", formattedNote, noteRow) } line = word if isLastLine { noteRow := fmt.Sprintf("| %s |", line) charDiff := len(noteRow) - stepRunSummaryBoxWidthInChars if charDiff < 0 { // shorter than desired - fill with space line = colorDeprecateNote(line) noteRow = fmt.Sprintf("| %s%s |", line, strings.Repeat(" ", -charDiff)) } else if charDiff > 0 { // longer than desired - should not log.Errorln("Should not be longer then expected") } if formattedNote == "" { formattedNote = noteRow } else { formattedNote = fmt.Sprintf("%s\n%s", formattedNote, noteRow) } } } else { // expected line is not to long, just keep growing the line line = expectedLine if isLastLine { noteRow := fmt.Sprintf("| %s |", line) charDiff := len(noteRow) - stepRunSummaryBoxWidthInChars if charDiff <= 0 { // shorter than desired - fill with space line = colorDeprecateNote(line) noteRow = fmt.Sprintf("| %s%s |", line, strings.Repeat(" ", -charDiff)) } else if charDiff > 0 { // longer than desired - should not log.Errorln("Should not be longer then expected") } if formattedNote == "" { formattedNote = noteRow } else { formattedNote = fmt.Sprintf("%s\n%s", formattedNote, noteRow) } } } } return formattedNote }
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) }
// Error ... func Error(format string, v ...interface{}) { message := fmt.Sprintf(format, v...) fmt.Println(colorstring.Red(message)) }
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) }