func registerFail(format string, v ...interface{}) { log.Error(format, v...) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if explanation, err := configs.validate(); err != nil { fmt.Println() log.Error("Issue with input: %s", err) fmt.Println() if explanation != "" { fmt.Println(explanation) fmt.Println() } os.Exit(1) } err := os.Chmod(configs.GradlewPath, 0770) if err != nil { log.Error("Failed to add executable permission on gradlew file (%s), error: %s", configs.GradlewPath, err) os.Exit(1) } fmt.Println() log.Info("Running gradle task...") if err := runGradleTask(configs.GradlewPath, configs.GradleFile, configs.UnitTestTasks, configs.UnitTestFlags); err != nil { log.Error("Gradle task failed, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_GRADLE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_GRADLE_TEST_RESULT", err) } os.Exit(1) } if err := exportEnvironmentWithEnvman("BITRISE_GRADLE_TEST_RESULT", "succeeded"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_GRADLE_TEST_RESULT", err) } }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if err := configs.validate(); err != nil { log.Error("Issue with input: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // Custom Options resultLogPth := filepath.Join(configs.DeployDir, "TestResult.xml") customOptions := []string{"--result", resultLogPth} if configs.CustomOptions != "" { options, err := shellquote.Split(configs.CustomOptions) if err != nil { log.Error("Failed to split params (%s), error: %s", configs.CustomOptions, err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } customOptions = append(customOptions, options...) } // --- // // build fmt.Println() log.Info("Runing all nunit test projects in solution: %s", configs.XamarinSolution) builder, err := builder.New(configs.XamarinSolution, []constants.ProjectType{}, false) if err != nil { log.Error("Failed to create xamarin builder, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } prepareCallback := func(solutionName string, projectName string, projectType constants.ProjectType, command *tools.Editable) { if projectType == constants.ProjectTypeNunitTest { (*command).SetCustomOptions(customOptions...) } } callback := func(solutionName string, projectName string, projectType constants.ProjectType, commandStr string, alreadyPerformed bool) { fmt.Println() if projectName == "" { log.Info("Building solution: %s", solutionName) } else { if projectType == constants.ProjectTypeNunitTest { log.Info("Building test project: %s", projectName) } else { log.Info("Building project: %s", projectName) } } log.Done("$ %s", commandStr) if alreadyPerformed { log.Warn("build command already performed, skipping...") } fmt.Println() } warnings, err := builder.BuildAllNunitTestProjects(configs.XamarinConfiguration, configs.XamarinPlatform, prepareCallback, callback) resultLog, logErr := testResultLogContent(resultLogPth) if logErr != nil { log.Warn("Failed to read test result, error: %s", logErr) } for _, warning := range warnings { log.Warn(warning) } if err != nil { log.Error("Test run failed, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } os.Exit(1) } if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "succeeded"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if err := configs.validate(); err != nil { log.Error("Issue with input: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // // build fmt.Println() log.Info("Building all iOS Xamarin UITest and Referred Projects in solution: %s", configs.XamarinSolution) builder, err := builder.New(configs.XamarinSolution, []constants.ProjectType{constants.ProjectTypeIOS}, false) if err != nil { log.Error("Failed to create xamarin builder, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } callback := func(solutionName string, projectName string, projectType constants.ProjectType, commandStr string, alreadyPerformed bool) { fmt.Println() if projectType == constants.ProjectTypeXamarinUITest { log.Info("Building test project: %s", projectName) } else { log.Info("Building project: %s", projectName) } log.Done("$ %s", commandStr) if alreadyPerformed { log.Warn("build command already performed, skipping...") } fmt.Println() } warnings, err := builder.BuildAllXamarinUITestAndReferredProjects(configs.XamarinConfiguration, configs.XamarinPlatform, nil, callback) for _, warning := range warnings { log.Warn(warning) } if err != nil { log.Error("Build failed, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } projectOutputMap, err := builder.CollectProjectOutputs(configs.XamarinConfiguration, configs.XamarinPlatform) if err != nil { log.Error("Failed to collect project outputs, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } testProjectOutputMap, warnings, err := builder.CollectXamarinUITestProjectOutputs(configs.XamarinConfiguration, configs.XamarinPlatform) for _, warning := range warnings { log.Warn(warning) } if err != nil { log.Error("Failed to collect test project output, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // --- // // Test Cloud submit solutionDir := filepath.Dir(configs.XamarinSolution) pattern := filepath.Join(solutionDir, "packages/Xamarin.UITest.*/tools/test-cloud.exe") testClouds, err := filepath.Glob(pattern) if err != nil { log.Error("Failed to find test-cloud.exe path with pattern (%s), error: %s", pattern, err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } if len(testClouds) == 0 { if err != nil { log.Error("No test-cloud.exe found path with pattern (%s)", pattern) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } } testCloud, err := testcloud.NewModel(testClouds[0]) if err != nil { log.Error("Failed to create test cloud model, error: %s", err) os.Exit(1) } testCloud.SetAPIKey(configs.APIKey) testCloud.SetUser(configs.User) testCloud.SetDevices(configs.Devices) testCloud.SetIsAsyncJSON(configs.IsAsync == "yes") testCloud.SetSeries(configs.Series) // If test cloud runs in asnyc mode test result will not be saved into file resultLogPth := filepath.Join(configs.DeployDir, "TestResult.xml") if configs.IsAsync != "yes" { testCloud.SetNunitXMLPth(resultLogPth) } // Parallelization if configs.Parallelization != "none" { parallelization, err := testcloud.ParseParallelization(configs.Parallelization) if err != nil { log.Error("Failed to parse parallelization, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } testCloud.SetParallelization(parallelization) } // --- // Custom Options if configs.CustomOptions != "" { options, err := shellquote.Split(configs.CustomOptions) if err != nil { log.Error("Failed to split params (%s), error: %s", configs.CustomOptions, err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } testCloud.SetCustomOptions(options...) } // --- // Artifacts resultLog := "" for testProjectName, testProjectOutput := range testProjectOutputMap { if len(testProjectOutput.ReferredProjectNames) == 0 { log.Warn("Test project (%s) does not refers to any project, skipping...", testProjectName) continue } for _, projectName := range testProjectOutput.ReferredProjectNames { projectOutput, ok := projectOutputMap[projectName] if !ok { continue } ipaPth := "" dsymPth := "" for _, output := range projectOutput.Outputs { if output.OutputType == constants.OutputTypeIPA { ipaPth = output.Pth } if output.OutputType == constants.OutputTypeDSYM { dsymPth = output.Pth } } if ipaPth == "" { log.Warn("No ipa generated for project: %s", projectName) } if dsymPth == "" { log.Warn("No dsym generated for project: %s", projectName) } // Submit fmt.Println() log.Info("Testing (%s) against (%s)", testProjectName, projectName) log.Detail("test dll: %s", testProjectOutput.Output.Pth) log.Detail("ipa: %s", ipaPth) log.Detail("dsym: %s", dsymPth) testCloud.SetAssemblyDir(filepath.Dir(testProjectOutput.Output.Pth)) testCloud.SetIPAPth(ipaPth) testCloud.SetDSYMPth(dsymPth) fmt.Println() log.Info("Submitting:") log.Done("$ %s", testCloud.PrintableCommand()) lines := []string{} callback := func(line string) { log.Detail(line) lines = append(lines, line) } err := testCloud.Submit(callback) // If test cloud runs in asnyc mode test result will not be saved into file if configs.IsAsync != "yes" { testLog, logErr := testResultLogContent(resultLogPth) if logErr != nil { log.Warn("Failed to read test result, error: %s", logErr) } resultLog = testLog } if err != nil { log.Error("Submit failed, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } os.Exit(1) } // --- if configs.IsAsync == "yes" { fmt.Println() log.Info("Preocessing json result:") jsonLine := "" for _, line := range lines { if strings.HasPrefix(line, "{") && strings.HasSuffix(line, "}") { jsonLine = line } } if jsonLine != "" { var result JSONResultModel if err := json.Unmarshal([]byte(jsonLine), &result); err != nil { log.Error("Failed to unmarshal result, error: %s", err) } else { for _, errorMsg := range result.ErrorMessages { log.Error(errorMsg) } if len(result.ErrorMessages) > 0 { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } os.Exit(1) } if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_TO_RUN_ID", result.TestRunID); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_TO_RUN_ID", err) } log.Done("TestRunId (%s) is available in (%s) environment variable", result.TestRunID, "BITRISE_XAMARIN_TEST_TO_RUN_ID") } } } } } // --- if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "succeeded"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } }
func main() { configs := createConfigsModelFromEnvs() configs.print() if err := configs.validate(); err != nil { log.Error("Issue with input: %s", err) os.Exit(1) } fmt.Println() log.Info("Other Configs:") cleanBuild := (configs.IsCleanBuild == "yes") generateCodeCoverage := (configs.GenerateCodeCoverageFiles == "yes") exportUITestArtifacts := (configs.ExportUITestArtifacts == "true") singleBuild := (configs.IsSingleBuild == "true") buildBeforeTest := (configs.ShouldBuildBeforeTest == "yes") retryOnFail := (configs.ShouldRetryTestOnFail == "yes") // Project-or-Workspace flag action := "" if strings.HasSuffix(configs.ProjectPath, ".xcodeproj") { action = "-project" } else if strings.HasSuffix(configs.ProjectPath, ".xcworkspace") { action = "-workspace" } else { log.Error("Iinvalid project file (%s), extension should be (.xcodeproj/.xcworkspace)", configs.ProjectPath) if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } os.Exit(1) } log.Detail("* action: %s", action) // Device Destination deviceDestination := fmt.Sprintf("platform=%s,name=%s,OS=%s", configs.SimulatorPlatform, configs.SimulatorDevice, configs.SimulatorOsVersion) log.Detail("* device_destination: %s", deviceDestination) // Output tools versions xcodebuildVersion, err := xcodeutil.GetXcodeVersion() if err != nil { log.Error("Failed to get the version of xcodebuild! Error: %s", err) if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } os.Exit(1) } log.Detail("* xcodebuild_version: %s (%s)", xcodebuildVersion.Version, xcodebuildVersion.BuildVersion) xcprettyVersion, err := cmd.GetXcprettyVersion() if err != nil { log.Warn("Failed to get the xcpretty version! Error: %s", err) } else { log.Detail("* xcpretty_version: %s", xcprettyVersion) } // Simulator infos simulator, err := xcodeutil.GetSimulator(configs.SimulatorPlatform, configs.SimulatorDevice, configs.SimulatorOsVersion) if err != nil { log.Error(fmt.Sprintf("failed to get simulator udid, error: %s", err)) if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } os.Exit(1) } log.Detail("* simulator_name: %s, UDID: %s, status: %s", simulator.Name, simulator.SimID, simulator.Status) fmt.Println() buildParams := models.XcodeBuildParamsModel{ Action: action, ProjectPath: configs.ProjectPath, Scheme: configs.Scheme, DeviceDestination: deviceDestination, CleanBuild: cleanBuild, } buildTestParams := models.XcodeBuildTestParamsModel{ BuildParams: buildParams, BuildBeforeTest: buildBeforeTest, AdditionalOptions: configs.TestOptions, GenerateCodeCoverage: generateCodeCoverage, } if singleBuild { buildTestParams.CleanBuild = cleanBuild } // // Start simulator if simulator.Status == "Shutdown" { log.Info("Booting simulator (%s)...", simulator.SimID) if err := xcodeutil.BootSimulator(simulator, xcodebuildVersion); err != nil { log.Error(fmt.Sprintf("failed to boot simulator, error: %s", err)) if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } os.Exit(1) } if configs.WaitForSimulatorBoot == "yes" { log.Detail("Waiting for simulator boot") progress.SimpleProgress(".", 1*time.Second, func() { time.Sleep(60 * time.Second) }) } fmt.Println() } // // Run build if !singleBuild { if rawXcodebuildOutput, exitCode, buildErr := runBuild(buildParams, configs.OutputTool); buildErr != nil { if err := saveRawOutputToLogFile(rawXcodebuildOutput, false); err != nil { log.Warn("Failed to save the Raw Output, err: %s", err) } log.Warn("xcode build exit code: %d", exitCode) log.Warn("xcode build log:\n%s", rawXcodebuildOutput) log.Error("xcode build failed with error: %s", buildErr) if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } os.Exit(1) } } // // Run test rawXcodebuildOutput, exitCode, testErr := runTest(buildTestParams, configs.OutputTool, configs.XcprettyTestOptions, true, retryOnFail) if err := saveRawOutputToLogFile(rawXcodebuildOutput, (testErr == nil)); err != nil { log.Warn("Failed to save the Raw Output, error %s", err) } if exportUITestArtifacts { if err := saveAttachements(configs.ProjectPath, configs.Scheme); err != nil { log.Warn("Failed to export UI test artifacts, error %s", err) } } if testErr != nil { log.Warn("xcode test exit code: %d", exitCode) log.Error("xcode test failed, error: %s", testErr) hint := `If you can't find the reason of the error in the log, please check the raw-xcodebuild-output.log The log file is stored in $BITRISE_DEPLOY_DIR, and its full path is available in the $BITRISE_XCODE_RAW_TEST_RESULT_TEXT_PATH environment variable` log.Warn(hint) if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } os.Exit(1) } if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_TEST_RESULT", "succeeded"); err != nil { log.Warn("Failed to export: BITRISE_XCODE_TEST_RESULT, error: %s", err) } }
func runTest(buildTestParams models.XcodeBuildTestParamsModel, outputTool, xcprettyOptions string, isAutomaticRetryOnReason, isRetryOnFail bool) (string, int, error) { handleTestError := func(fullOutputStr string, exitCode int, testError error) (string, int, error) { // // Automatic retry for _, retryReasonPattern := range automaticRetryReasonPatterns { if isStringFoundInOutput(retryReasonPattern, fullOutputStr) { log.Warn("Automatic retry reason found in log: %s", retryReasonPattern) if isAutomaticRetryOnReason { log.Detail("isAutomaticRetryOnReason=true - retrying...") return runTest(buildTestParams, outputTool, xcprettyOptions, false, false) } log.Error("isAutomaticRetryOnReason=false, no more retry, stopping the test!") return fullOutputStr, exitCode, testError } } // // Retry on fail if isRetryOnFail { log.Warn("Test run failed") log.Detail("isRetryOnFail=true - retrying...") return runTest(buildTestParams, outputTool, xcprettyOptions, false, false) } return fullOutputStr, exitCode, testError } buildParams := buildTestParams.BuildParams xcodebuildArgs := []string{buildParams.Action, buildParams.ProjectPath, "-scheme", buildParams.Scheme} if buildTestParams.CleanBuild { xcodebuildArgs = append(xcodebuildArgs, "clean") } // the 'build' argument is required *before* the 'test' arg, to prevent // the Xcode bug described in the README, which causes: // 'iPhoneSimulator: Timed out waiting 120 seconds for simulator to boot, current state is 1.' // in case the compilation takes a long time. // Related Radar link: https://openradar.appspot.com/22413115 // Demonstration project: https://github.com/bitrise-io/simulator-launch-timeout-includes-build-time // for builds < 120 seconds or fixed Xcode versions, one should // have the possibility of opting out, because the explicit build arg // leads the project to be compiled twice and increase the duration // Related issue link: https://github.com/bitrise-io/steps-xcode-test/issues/55 if buildTestParams.BuildBeforeTest { xcodebuildArgs = append(xcodebuildArgs, "build") } xcodebuildArgs = append(xcodebuildArgs, "test", "-destination", buildParams.DeviceDestination) if buildTestParams.GenerateCodeCoverage { xcodebuildArgs = append(xcodebuildArgs, "GCC_INSTRUMENT_PROGRAM_FLOW_ARCS=YES") xcodebuildArgs = append(xcodebuildArgs, "GCC_GENERATE_TEST_COVERAGE_FILES=YES") } if buildTestParams.AdditionalOptions != "" { options, err := shellquote.Split(buildTestParams.AdditionalOptions) if err != nil { return "", 1, fmt.Errorf("failed to parse additional options (%s), error: %s", buildTestParams.AdditionalOptions, err) } xcodebuildArgs = append(xcodebuildArgs, options...) } xcprettyArgs := []string{} if xcprettyOptions != "" { options, err := shellquote.Split(xcprettyOptions) if err != nil { return "", 1, fmt.Errorf("failed to parse additional options (%s), error: %s", xcprettyOptions, err) } // get and delete the xcpretty output file, if exists xcprettyOutputFilePath := "" isNextOptOutputPth := false for _, aOpt := range options { if isNextOptOutputPth { xcprettyOutputFilePath = aOpt break } if aOpt == "--output" { isNextOptOutputPth = true continue } } if xcprettyOutputFilePath != "" { if isExist, err := pathutil.IsPathExists(xcprettyOutputFilePath); err != nil { log.Error("Failed to check xcpretty output file status (path: %s), error: %s", xcprettyOutputFilePath, err) } else if isExist { log.Warn("=> Deleting existing xcpretty output: %s", xcprettyOutputFilePath) if err := os.Remove(xcprettyOutputFilePath); err != nil { log.Error("Failed to delete xcpretty output file (path: %s), error: %s", xcprettyOutputFilePath, err) } } } // xcprettyArgs = append(xcprettyArgs, options...) } log.Info("Running the tests...") var rawOutput string var err error var exit int if outputTool == "xcpretty" { rawOutput, exit, err = runPrettyXcodeBuildCmd(true, xcprettyArgs, xcodebuildArgs) } else { rawOutput, exit, err = runXcodeBuildCmd(true, xcodebuildArgs...) } if err != nil { return handleTestError(rawOutput, exit, err) } return rawOutput, exit, nil }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if err := configs.validate(); err != nil { log.Error("Issue with input: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // Get Simulator Infos fmt.Println() log.Info("Collecting simulator info...") simulatorInfo, err := getSimulatorInfo(configs.SimulatorOsVersion, configs.SimulatorDevice) if err != nil { log.Error("Failed to get simulator infos, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } log.Done("Simulator (%s), id: (%s), status: %s", simulatorInfo.Name, simulatorInfo.ID, simulatorInfo.Status) // --- // Nunit Console path nunitConsolePth, err := nunit.SystemNunit3ConsolePath() if err != nil { log.Error("Failed to get system insatlled nunit3-console.exe path, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // --- // // build fmt.Println() log.Info("Building all iOS Xamarin UITest and Referred Projects in solution: %s", configs.XamarinSolution) builder, err := builder.New(configs.XamarinSolution, []constants.ProjectType{constants.ProjectTypeIOS}, false) if err != nil { log.Error("Failed to create xamarin builder, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } callback := func(solutionName string, projectName string, projectType constants.ProjectType, commandStr string, alreadyPerformed bool) { fmt.Println() if projectType == constants.ProjectTypeXamarinUITest { log.Info("Building test project: %s", projectName) } else { log.Info("Building project: %s", projectName) } log.Done("$ %s", commandStr) if alreadyPerformed { log.Warn("build command already performed, skipping...") } fmt.Println() } warnings, err := builder.BuildAllXamarinUITestAndReferredProjects(configs.XamarinConfiguration, configs.XamarinPlatform, nil, callback) for _, warning := range warnings { log.Warn(warning) } if err != nil { log.Error("Build failed, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } projectOutputMap, err := builder.CollectProjectOutputs(configs.XamarinConfiguration, configs.XamarinPlatform) if err != nil { log.Error("Failed to collect project outputs, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } testProjectOutputMap, warnings, err := builder.CollectXamarinUITestProjectOutputs(configs.XamarinConfiguration, configs.XamarinPlatform) for _, warning := range warnings { log.Warn(warning) } if err != nil { log.Error("Failed to collect test project output, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // --- // // Run nunit tests nunitConsole, err := nunit.New(nunitConsolePth) if err != nil { log.Error("Failed to create nunit console model, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } resultLogPth := filepath.Join(configs.DeployDir, "TestResult.xml") nunitConsole.SetResultLogPth(resultLogPth) // Artifacts resultLog := "" for testProjectName, testProjectOutput := range testProjectOutputMap { if len(testProjectOutput.ReferredProjectNames) == 0 { log.Warn("Test project (%s) does not refers to any project, skipping...", testProjectName) continue } for _, projectName := range testProjectOutput.ReferredProjectNames { projectOutput, ok := projectOutputMap[projectName] if !ok { continue } appPth := "" for _, output := range projectOutput.Outputs { if output.OutputType == constants.OutputTypeAPP { appPth = output.Pth } } if appPth == "" { log.Error("No app generated for project: %s", projectName) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // Set APP_BUNDLE_PATH env to let the test know which .app file should be tested // This env is used in the Xamarin.UITest project to refer to the .app path if err := os.Setenv("APP_BUNDLE_PATH", appPth); err != nil { log.Error("Failed to set APP_BUNDLE_PATH environment, without this env test will fail, error: %s", err) if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } os.Exit(1) } // Run test fmt.Println() log.Info("Testing (%s) against (%s)", testProjectName, projectName) log.Detail("test dll: %s", testProjectOutput.Output.Pth) log.Detail("app: %s", appPth) nunitConsole.SetDLLPth(testProjectOutput.Output.Pth) nunitConsole.SetTestToRun(configs.TestToRun) fmt.Println() log.Info("Running Xamarin UITest") log.Done("$ %s", nunitConsole.PrintableCommand()) fmt.Println() err := nunitConsole.Run() testLog, readErr := testResultLogContent(resultLogPth) if readErr != nil { log.Warn("Failed to read test result, error: %s", readErr) } resultLog = testLog if err != nil { log.Error("Test failed, error: %s", err) if errorMsg, err := parseErrorFromResultLog(resultLog); err != nil { log.Warn("Failed to parse error message from result log, error: %s", err) } else if errorMsg != "" { log.Error("%s", errorMsg) } if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "failed"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } os.Exit(1) } } } if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_RESULT", "succeeded"); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_RESULT", err) } if resultLog != "" { if err := exportEnvironmentWithEnvman("BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", resultLog); err != nil { log.Warn("Failed to export environment: %s, error: %s", "BITRISE_XAMARIN_TEST_FULL_RESULTS_TEXT", err) } } }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if err := configs.validate(); err != nil { fmt.Println() log.Error("Issue with input: %s", err) os.Exit(1) } // // Create client fmt.Println() log.Info("Authenticateing") jwtConfig := new(jwt.Config) if configs.JSONKeyPath != "" { jsonKeyPth := "" if strings.HasPrefix(configs.JSONKeyPath, "file://") { jsonKeyPth = strings.TrimPrefix(configs.JSONKeyPath, "file://") } else { tmpDir, err := pathutil.NormalizedOSTempDirPath("__google-play-deploy__") if err != nil { log.Error("Failed to create tmp dir, error: %s", err) os.Exit(1) } jsonKeyPth = filepath.Join(tmpDir, "key.json") if err := downloadFile(configs.JSONKeyPath, jsonKeyPth); err != nil { log.Error("Failed to download json key file, error: %s", err) os.Exit(1) } } authConfig, err := jwtConfigFromJSONKeyFile(jsonKeyPth) if err != nil { log.Error("Failed to create auth config from json key file, error: %s", err) os.Exit(1) } jwtConfig = authConfig } else { p12KeyPath := "" if strings.HasPrefix(configs.P12KeyPath, "file://") { p12KeyPath = strings.TrimPrefix(configs.P12KeyPath, "file://") } else { tmpDir, err := pathutil.NormalizedOSTempDirPath("__google-play-deploy__") if err != nil { log.Error("Failed to create tmp dir, error: %s", err) os.Exit(1) } p12KeyPath = filepath.Join(tmpDir, "key.p12") if err := downloadFile(configs.P12KeyPath, p12KeyPath); err != nil { log.Error("Failed to download p12 key file, error: %s", err) os.Exit(1) } } authConfig, err := jwtConfigFromP12KeyFile(p12KeyPath, configs.ServiceAccountEmail) if err != nil { log.Error("Failed to create auth config from p12 key file, error: %s", err) os.Exit(1) } jwtConfig = authConfig } client := jwtConfig.Client(oauth2.NoContext) service, err := androidpublisher.New(client) if err != nil { log.Error("Failed to create publisher service, error: %s", err) os.Exit(1) } log.Done("Authenticated client created") // --- // // Create insert edit fmt.Println() log.Info("Create new edit") editsService := androidpublisher.NewEditsService(service) editsInsertCall := editsService.Insert(configs.PackageName, nil) appEdit, err := editsInsertCall.Do() if err != nil { log.Error("Failed to perform edit insert call, error: %s", err) os.Exit(1) } log.Detail(" editID: %s", appEdit.Id) // --- // // Upload APKs fmt.Println() log.Info("Upload apks") versionCodes := []int64{} apkPaths := strings.Split(configs.ApkPath, "|") for _, apkPath := range apkPaths { apkFile, err := os.Open(apkPath) if err != nil { log.Error("Failed to read apk (%s), error: %s", apkPath, err) os.Exit(1) } editsApksService := androidpublisher.NewEditsApksService(service) editsApksUloadCall := editsApksService.Upload(configs.PackageName, appEdit.Id) editsApksUloadCall.Media(apkFile, googleapi.ContentType("application/vnd.android.package-archive")) apk, err := editsApksUloadCall.Do() if err != nil { log.Error("Failed to upload apk, error: %s", err) os.Exit(1) } log.Detail(" uploaded apk version: %d", apk.VersionCode) versionCodes = append(versionCodes, apk.VersionCode) } // --- // // Update track fmt.Println() log.Info("Update track") editsTracksService := androidpublisher.NewEditsTracksService(service) newTrack := androidpublisher.Track{ Track: configs.Track, VersionCodes: versionCodes, } if configs.Track == "rollout" { userFraction, err := strconv.ParseFloat(configs.UserFraction, 64) if err != nil { log.Error("Failed to parse user fraction, error: %s", err) os.Exit(1) } newTrack.UserFraction = userFraction } editsTracksUpdateCall := editsTracksService.Update(configs.PackageName, appEdit.Id, configs.Track, &newTrack) track, err := editsTracksUpdateCall.Do() if err != nil { log.Error("Failed to update track, error: %s", err) os.Exit(1) } log.Detail(" updated track: %s", track.Track) log.Detail(" assigned apk versions: %v", track.VersionCodes) // --- // // Update listing if configs.WhatsnewsDir != "" { fmt.Println() log.Info("Update listing") recentChangesMap, err := readLocalisedRecentChanges(configs.WhatsnewsDir) if err != nil { log.Error("Failed to read whatsnews, error: %s", err) os.Exit(1) } editsApklistingsService := androidpublisher.NewEditsApklistingsService(service) for _, versionCode := range versionCodes { log.Detail(" updating recent changes for version: %d", versionCode) for language, recentChanges := range recentChangesMap { newApkListing := androidpublisher.ApkListing{ Language: language, RecentChanges: recentChanges, } editsApkListingsCall := editsApklistingsService.Update(configs.PackageName, appEdit.Id, versionCode, language, &newApkListing) apkListing, err := editsApkListingsCall.Do() if err != nil { log.Error("Failed to update listing, error: %s", err) os.Exit(1) } log.Detail(" - language: %s", apkListing.Language) } } } // --- // // Validate edit fmt.Println() log.Info("Validating edit") editsValidateCall := editsService.Validate(configs.PackageName, appEdit.Id) if _, err := editsValidateCall.Do(); err != nil { log.Error("Failed to validate edit, error: %s", err) os.Exit(1) } log.Done("Edit is valid") // --- // // Commit edit fmt.Println() log.Info("Committing edit") editsCommitCall := editsService.Commit(configs.PackageName, appEdit.Id) if _, err := editsCommitCall.Do(); err != nil { log.Error("Failed to commit edit, error: %s", err) os.Exit(1) } log.Done("Edit committed") // --- }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if err := configs.validate(); err != nil { log.Error("Issue with input: %s", err) os.Exit(1) } nugetPth := "/Library/Frameworks/Mono.framework/Versions/Current/bin/nuget" nugetRestoreCmdArgs := []string{nugetPth} if configs.NugetVersion == "latest" { fmt.Println() log.Info("Updating Nuget to latest version...") // "sudo $nuget update -self" cmdArgs := []string{"sudo", nugetPth, "update", "-self"} cmd, err := cmdex.NewCommandFromSlice(cmdArgs) if err != nil { log.Error("Failed to create command from args (%v), error: %s", cmdArgs, err) os.Exit(1) } cmd.SetStdout(os.Stdout) cmd.SetStderr(os.Stderr) log.Done("$ %s", cmdex.PrintableCommandArgs(false, cmdArgs)) if err := cmd.Run(); err != nil { log.Error("Failed to update nuget, error: %s", err) os.Exit(1) } } else if configs.NugetVersion != "" { fmt.Println() log.Info("Downloading Nuget %s version...", configs.NugetVersion) tmpDir, err := pathutil.NormalizedOSTempDirPath("__nuget__") if err != nil { log.Error("Failed to create tmp dir, error: %s", err) os.Exit(1) } downloadPth := filepath.Join(tmpDir, "nuget.exe") // https://dist.nuget.org/win-x86-commandline/v3.3.0/nuget.exe nugetURL := fmt.Sprintf("https://dist.nuget.org/win-x86-commandline/v%s/nuget.exe", configs.NugetVersion) log.Detail("Download URL: %s", nugetURL) if err := DownloadFile(nugetURL, downloadPth); err != nil { log.Warn("Download failed, error: %s", err) // https://dist.nuget.org/win-x86-commandline/v3.4.4/NuGet.exe nugetURL = fmt.Sprintf("https://dist.nuget.org/win-x86-commandline/v%s/NuGet.exe", configs.NugetVersion) log.Detail("Retry download URl: %s", nugetURL) if err := DownloadFile(nugetURL, downloadPth); err != nil { log.Error("Failed to download nuget, error: %s", err) os.Exit(1) } } nugetRestoreCmdArgs = []string{constants.MonoPath, downloadPth} } fmt.Println() log.Info("Restoring Nuget packages...") nugetRestoreCmdArgs = append(nugetRestoreCmdArgs, "restore", configs.XamarinSolution) if err := retry.Times(1).Try(func(attempt uint) error { if attempt > 0 { log.Warn("Attempt %d failed, retrying...", attempt) } log.Done("$ %s", cmdex.PrintableCommandArgs(false, nugetRestoreCmdArgs)) cmd, err := cmdex.NewCommandFromSlice(nugetRestoreCmdArgs) if err != nil { log.Error("Failed to create Nuget command, error: %s", err) os.Exit(1) } cmd.SetStdout(os.Stdout) cmd.SetStderr(os.Stderr) if err := cmd.Run(); err != nil { log.Error("Restore failed, error: %s", err) return err } return nil }); err != nil { log.Error("Nuget restore failed, error: %s", err) os.Exit(1) } }
func fail(format string, v ...interface{}) { log.Error(format, v...) os.Exit(1) }
func main() { configs := createConfigsModelFromEnvs() configs.print() if err := configs.validate(); err != nil { log.Error("Issue with input: %s", err) os.Exit(1) } fmt.Println() // Validate Certificates certificateURLPassphraseMap := map[string]string{} if configs.CertificateURL != "" { certificateURLs := strings.Split(configs.CertificateURL, "|") certificatePassphrases := strings.Split(configs.CertificatePassphrase, "|") if len(certificateURLs) != len(certificatePassphrases) { log.Error("Certificate url count: (%d), not equals to Certificate Passphrase count: (%d)", len(certificateURLs), len(certificatePassphrases)) os.Exit(1) } for i := 0; i < len(certificateURLs); i++ { certificateURL := certificateURLs[i] certificatePassphrase := certificatePassphrases[i] certificateURLPassphraseMap[certificateURL] = certificatePassphrase } } if configs.DefaultCertificateURL != "" { log.Detail("Default Certificate given") certificateURLPassphraseMap[configs.DefaultCertificateURL] = configs.DefaultCertificatePassphrase } certificateCount := len(certificateURLPassphraseMap) log.Detail("Provided Certificate count: %d", certificateCount) if certificateCount == 0 { log.Error("No Certificate provided") os.Exit(1) } // Validate Provisioning Profiles provisioningProfileURLs := strings.Split(configs.ProvisioningProfileURL, "|") if configs.DefaultProvisioningProfileURL != "" { log.Detail("Default Provisioning Profile given") provisioningProfileURLs = append(provisioningProfileURLs, configs.DefaultProvisioningProfileURL) } profileCount := len(provisioningProfileURLs) log.Detail("Provided Provisioning Profile count: %d", profileCount) if profileCount == 0 { log.Error("No Provisioning Profile provided") os.Exit(1) } // // Init homeDir := os.Getenv("HOME") provisioningProfileDir := path.Join(homeDir, "Library/MobileDevice/Provisioning Profiles") if exist, err := pathutil.IsPathExists(provisioningProfileDir); err != nil { log.Error("Failed to check path (%s), err: %s", provisioningProfileDir, err) os.Exit(1) } else if !exist { if err := os.MkdirAll(provisioningProfileDir, 0777); err != nil { log.Error("Failed to create path (%s), err: %s", provisioningProfileDir, err) os.Exit(1) } } tempDir, err := pathutil.NormalizedOSTempDirPath("bitrise-cert-tmp") if err != nil { log.Error("Failed to create tmp directory, err: %s", err) os.Exit(1) } if exist, err := pathutil.IsPathExists(configs.KeychainPath); err != nil { log.Error("Failed to check path (%s), err: %s", configs.KeychainPath, err) os.Exit(1) } else if !exist { fmt.Println() log.Warn("Keychain (%s) does not exist", configs.KeychainPath) keychainPth := fmt.Sprintf("%s-db", configs.KeychainPath) log.Detail(" Checking (%s)", keychainPth) if exist, err := pathutil.IsPathExists(keychainPth); err != nil { log.Error("Failed to check path (%s), err: %s", keychainPth, err) os.Exit(1) } else if !exist { log.Info("Creating keychain: %s", configs.KeychainPath) if out, err := runCommandAndReturnCombinedStdoutAndStderr("security", "-v", "create-keychain", "-p", configs.KeychainPassword, configs.KeychainPath); err != nil { log.Error("Failed to create keychain, output: %s", out) log.Error("Failed to create keychain, err: %s", err) os.Exit(1) } } } else { log.Detail("Keychain already exists, using it: %s", configs.KeychainPath) } // // Download certificate fmt.Println() log.Info("Downloading & installing Certificate(s)") certificatePassphraseMap := map[string]string{} idx := 0 for certURL, pass := range certificateURLPassphraseMap { fmt.Println() log.Detail("=> Downloading certificate: %d/%d", idx+1, certificateCount) certPath := path.Join(tempDir, fmt.Sprintf("Certificate-%d.p12", idx)) if err := downloadFile(certPath, certURL); err != nil { log.Error("Download failed, err: %s", err) os.Exit(1) } certificatePassphraseMap[certPath] = pass idx++ } // // Install certificate fmt.Println() log.Detail("=> Installing downloaded certificate") for cert, pass := range certificatePassphraseMap { // Import items into a keychain. importOut, err := runCommandAndReturnCombinedStdoutAndStderr("security", "import", cert, "-k", configs.KeychainPath, "-P", pass, "-A") if err != nil { log.Error("Command failed, output: %s", importOut) log.Error("Command failed, err: %s", err) os.Exit(1) } } // This is new behavior in Sierra, [openradar](https://openradar.appspot.com/28524119) // You need to use "security set-key-partition-list -S apple-tool:,apple: -k keychainPass keychainName" after importing the item and before attempting to use it via codesign. osVersionCmd := cmdex.NewCommand("sw_vers", "-productVersion") out, err := osVersionCmd.RunAndReturnTrimmedCombinedOutput() if err != nil { log.Error("Failed to get os version, error: %s", err) os.Exit(1) } osVersion, err := version.NewVersion(out) if err != nil { log.Error("Failed to parse os version (%s), error: %s", out, err) os.Exit(1) } sierraVersionStr := "10.12.0" sierraVersion, err := version.NewVersion(sierraVersionStr) if err != nil { log.Error("Failed to parse os version (%s), error: %s", sierraVersionStr, err) os.Exit(1) } if !osVersion.LessThan(sierraVersion) { cmd := cmdex.NewCommand("security", "set-key-partition-list", "-S", "apple-tool:,apple:", "-k", configs.KeychainPassword, configs.KeychainPath) if err := cmd.Run(); err != nil { log.Error("Failed, err: %s", err) os.Exit(1) } } // --- // Set keychain settings: Lock keychain when the system sleeps, Lock keychain after timeout interval, Timeout in seconds settingsOut, err := runCommandAndReturnCombinedStdoutAndStderr("security", "-v", "set-keychain-settings", "-lut", "72000", configs.KeychainPath) if err != nil { log.Error("Command failed, output: %s", settingsOut) log.Error("Command failed, err: %s", err) os.Exit(1) } // List keychains listKeychainsOut, err := runCommandAndReturnCombinedStdoutAndStderr("security", "list-keychains") if err != nil { log.Error("Command failed, output: %s", listKeychainsOut) log.Error("Command failed, err: %s", err) os.Exit(1) } keychainList := strings.Split(listKeychainsOut, "\n") strippedKeychainList := []string{} for _, keychain := range keychainList { strippedKeychain := strip(keychain) strippedKeychainList = append(strippedKeychainList, strippedKeychain) } strippedKeychainList = addKeyChainToList(strippedKeychainList, configs.KeychainPath) // Set keychain search path args := []string{"-v", "list-keychains", "-s"} args = append(args, strippedKeychainList...) listKeychainsOut, err = runCommandAndReturnCombinedStdoutAndStderr("security", args...) if err != nil { log.Error("Command failed, output: %s", listKeychainsOut) log.Error("Command failed, err: %s", err) os.Exit(1) } // Set the default keychain defaultKeychainOut, err := runCommandAndReturnCombinedStdoutAndStderr("security", "-v", "default-keychain", "-s", configs.KeychainPath) if err != nil { log.Error("Command failed, output: %s", defaultKeychainOut) log.Error("Command failed, err: %s", err) os.Exit(1) } // Unlock the specified keychain unlockOut, err := runCommandAndReturnCombinedStdoutAndStderr("security", "-v", "unlock-keychain", "-p", configs.KeychainPassword, configs.KeychainPath) if err != nil { log.Error("Command failed, output: %s", unlockOut) log.Error("Command failed, err: %s", err) os.Exit(1) } for cert, pass := range certificatePassphraseMap { certificateIdentity, err := certificateFriendlyName(cert, pass) if err != nil { log.Error("Failed to get cert identity, output: %s", certificateIdentity) log.Error("Failed to get cert identity, err: %s", err) os.Exit(1) } if certificateIdentity == "" { log.Error("Failed to get cert identity") os.Exit(1) } log.Done(" Installed certificate: %s", certificateIdentity) } certs, err := availableCertificates(configs.KeychainPath) if err != nil { log.Error("Failed to get certificate list, err:%s", err) os.Exit(1) } if len(certs) == 0 { log.Error("Failed to import certificate, no certificates found") os.Exit(1) } fmt.Println() log.Info("Available certificates:") fmt.Println("-----------------------") for _, cert := range certs { log.Detail(" * %s", cert) } // // Install provisioning profiles // NOTE: the URL can be a pipe (|) separated list of Provisioning Profile URLs fmt.Println() log.Info("Downloading & installing Provisioning Profile(s)") for idx, profileURL := range provisioningProfileURLs { fmt.Println() log.Detail("=> Downloading provisioning profile: %d/%d", idx+1, profileCount) provisioningProfileExt := "provisionprofile" if !strings.Contains(profileURL, "."+provisioningProfileExt) { provisioningProfileExt = "mobileprovision" } profileTmpPth := path.Join(tempDir, fmt.Sprintf("profile-%d.%s", idx, provisioningProfileExt)) if err := downloadFile(profileTmpPth, profileURL); err != nil { log.Error("Download failed, err: %s", err) os.Exit(1) } fmt.Println() fmt.Println("=> Installing provisioning profile") out, err := runCommandAndReturnCombinedStdoutAndStderr("/usr/bin/security", "cms", "-D", "-i", profileTmpPth) if err != nil { log.Error("Command failed, output: %s", out) log.Error("Command failed, err: %s", err) os.Exit(1) } outSplit := strings.Split(out, "\n") if len(outSplit) > 0 { if strings.Contains(outSplit[0], notValidParameterErrorMessage) { fixedOutSplit := outSplit[1:len(outSplit)] out = strings.Join(fixedOutSplit, "\n") } } tmpProvProfilePth := path.Join(tempDir, "prov") if err := writeBytesToFileWithPermission(tmpProvProfilePth, []byte(out), 0); err != nil { log.Error("Failed to write profile to file, error: %s", err) os.Exit(1) } profileInfos, err := printableProfileInfos(out) if err != nil { log.Error("Failed to read profile infos, err: %s", err) os.Exit(1) } fmt.Println() log.Info("Profile Infos:") log.Detail("%s", profileInfos) fmt.Println() profileUUID, err := runCommandAndReturnCombinedStdoutAndStderr("/usr/libexec/PlistBuddy", "-c", "Print UUID", tmpProvProfilePth) if err != nil { log.Error("Command failed, output: %s", profileUUID) log.Error("Command failed, err: %s", err) os.Exit(1) } log.Done(" Installed Profile UUID: %s", profileUUID) profileFinalPth := path.Join(provisioningProfileDir, profileUUID+"."+provisioningProfileExt) log.Detail(" Moving it to: %s", profileFinalPth) if out, err := runCommandAndReturnCombinedStdoutAndStderr("cp", profileTmpPth, profileFinalPth); err != nil { log.Error("Command failed, output: %s", out) log.Error("Command failed, err: %s", err) os.Exit(1) } } }
func downloadFile(destionationPath, URL string) error { url, err := url.Parse(URL) if err != nil { return err } scheme := url.Scheme tmpDstFilePath := "" if scheme != "file" { log.Detail(" Downloading (%s) to (%s)", secureInput(URL), destionationPath) tmpDir, err := pathutil.NormalizedOSTempDirPath("download") if err != nil { return err } tmpDst := path.Join(tmpDir, "tmp_file") tmpDstFile, err := os.Create(tmpDst) if err != nil { return err } defer func() { if err := tmpDstFile.Close(); err != nil { log.Error("Failed to close file (%s), error: %s", tmpDst, err) } }() success := false var response *http.Response for i := 0; i < 3 && !success; i++ { if i > 0 { fmt.Println("-> Retrying...") time.Sleep(3 * time.Second) } response, err = http.Get(URL) if err != nil { log.Error(err.Error()) } else { success = true } if response != nil { defer func() { if err := response.Body.Close(); err != nil { log.Error("Failed to close response body, error: %s", err) } }() } } if !success { return err } _, err = io.Copy(tmpDstFile, response.Body) if err != nil { return err } tmpDstFilePath = tmpDstFile.Name() } else { log.Detail(" Moving (%s) to (%s)", secureInput(URL), destionationPath) tmpDstFilePath = strings.Replace(URL, scheme+"://", "", -1) } if out, err := runCommandAndReturnCombinedStdoutAndStderr("cp", tmpDstFilePath, destionationPath); err != nil { log.Detail("Move out: %s", out) return err } return nil }