func saveRawOutputToLogFile(rawXcodebuildOutput string, isRunSuccess bool) error { tmpDir, err := pathutil.NormalizedOSTempDirPath("xcodebuild-output") if err != nil { return fmt.Errorf("Failed to create temp dir, error: %s", err) } logFileName := "raw-xcodebuild-output.log" logPth := filepath.Join(tmpDir, logFileName) if err := fileutil.WriteStringToFile(logPth, rawXcodebuildOutput); err != nil { return fmt.Errorf("Failed to write xcodebuild output to file, error: %s", err) } if !isRunSuccess { deployDir := os.Getenv("BITRISE_DEPLOY_DIR") if deployDir == "" { return errors.New("No BITRISE_DEPLOY_DIR found") } deployPth := filepath.Join(deployDir, logFileName) if err := cmdex.CopyFile(logPth, deployPth); err != nil { return fmt.Errorf("Failed to copy xcodebuild output log file from (%s) to (%s), error: %s", logPth, deployPth, err) } logPth = deployPth } if err := cmd.ExportEnvironmentWithEnvman("BITRISE_XCODE_RAW_TEST_RESULT_TEXT_PATH", logPth); err != nil { log.Warn("Failed to export: BITRISE_XCODE_RAW_TEST_RESULT_TEXT_PATH, error: %s", err) } return nil }
// ExportOutputFile ... func ExportOutputFile(sourcePth, destinationPth, envKey string) error { if sourcePth != destinationPth { if err := cmdex.CopyFile(sourcePth, destinationPth); err != nil { return err } } return exportEnvironmentWithEnvman(envKey, destinationPth) }
func downloadPluginBin(sourceURL, destinationPth string) error { url, err := url.Parse(sourceURL) if err != nil { return fmt.Errorf("failed to parse url (%s), error: %s", sourceURL, err) } // Download local binary if url.Scheme == "file" { src := strings.Replace(sourceURL, url.Scheme+"://", "", -1) if err := cmdex.CopyFile(src, destinationPth); err != nil { return fmt.Errorf("failed to copy (%s) to (%s)", src, destinationPth) } return nil } // Download remote binary out, err := os.Create(destinationPth) defer func() { if err := out.Close(); err != nil { log.Warnf("failed to close (%s)", destinationPth) } }() if err != nil { return fmt.Errorf("failed to create (%s), error: %s", destinationPth, err) } resp, err := http.Get(sourceURL) if err != nil { return fmt.Errorf("failed to download from (%s), error: %s", sourceURL, err) } defer func() { if err := resp.Body.Close(); err != nil { log.Warnf("failed to close (%s) body", sourceURL) } }() if resp.StatusCode != http.StatusOK { return fmt.Errorf("non success status code (%d)", resp.StatusCode) } _, err = io.Copy(out, resp.Body) if err != nil { return fmt.Errorf("failed to download from (%s), error: %s", sourceURL, err) } return nil }
func setup(c *cli.Context) error { log.Debug("Setup") // Input validation steplibURI := c.String(CollectionKey) if steplibURI == "" { log.Fatal("No step collection specified") } copySpecJSONPath := c.String(CopySpecJSONKey) if c.IsSet(LocalCollectionKey) { log.Warn("'local' flag is deprecated") log.Warn("use 'file://' prefix in steplib path instead") fmt.Println() } if c.Bool(LocalCollectionKey) { if !strings.HasPrefix(steplibURI, "file://") { log.Warnf("Appending file path prefix (file://) to StepLib (%s)", steplibURI) steplibURI = "file://" + steplibURI log.Warnf("From now you can refer to this StepLib with URI: %s", steplibURI) log.Warnf("For example, to delete StepLib call: `stepman delete --collection %s`", steplibURI) } } // Setup if err := setupSteplib(steplibURI, false); err != nil { log.Fatalf("Setup failed, error: %s", err) } // Copy spec.json if copySpecJSONPath != "" { log.Infof("Copying spec YML to path: %s", copySpecJSONPath) route, found := stepman.ReadRoute(steplibURI) if !found { log.Fatalf("No route found for steplib (%s)", steplibURI) } sourceSpecJSONPth := stepman.GetStepSpecPath(route) if err := cmdex.CopyFile(sourceSpecJSONPth, copySpecJSONPath); err != nil { log.Fatalf("Failed to copy spec.json from (%s) to (%s), error: %s", sourceSpecJSONPth, copySpecJSONPath, err) } } return nil }
func setup(c *cli.Context) { log.Debug("Setup") // Input validation steplibURI := c.String(CollectionKey) if steplibURI == "" { log.Fatal("No step collection specified") } copySpecJSONPath := c.String(CopySpecJSONKey) if c.IsSet(LocalCollectionKey) { log.Warn("'local' flag is deprecated") log.Warn("use 'file://' suffix in steplib path instead") fmt.Println() } if c.Bool(LocalCollectionKey) { if !strings.HasPrefix(steplibURI, "file://") { steplibURI = "file://" + steplibURI } } // Setup if err := setupSteplib(steplibURI, false); err != nil { log.Fatalf("Steup failed, error: %s", err) } // Copy spec.json if copySpecJSONPath != "" { log.Infof("Copying spec YML to path: %s", copySpecJSONPath) route, found := stepman.ReadRoute(steplibURI) if !found { log.Fatalf("No route found for steplib (%s)", steplibURI) } sourceSpecJSONPth := stepman.GetStepSpecPath(route) if err := cmdex.CopyFile(sourceSpecJSONPth, copySpecJSONPath); err != nil { log.Fatalf("Failed to copy spec.json from (%s) to (%s), error: %s", sourceSpecJSONPth, copySpecJSONPath, err) } } }
func activate(c *cli.Context) error { // Input validation collectionURI := c.String(CollectionKey) if collectionURI == "" { log.Fatalln("[STEPMAN] - No step collection specified") } id := c.String(IDKey) if id == "" { log.Fatal("[STEPMAN] - Missing step id") } path := c.String(PathKey) if path == "" { log.Fatal("[STEPMAN] - Missing destination path") } version := c.String(VersionKey) copyYML := c.String(CopyYMLKey) update := c.Bool(UpdateKey) // Check if step exist in collection collection, err := stepman.ReadStepSpec(collectionURI) if err != nil { log.Fatalln("[STEPMAN] - Failed to read steps spec (spec.json)") } _, stepFound := collection.GetStep(id, version) if !stepFound { if !update { if version == "" { log.Fatalf("[STEPMAN] - Collection doesn't contain any version of step (id:%s)", id) } else { log.Fatalf("[STEPMAN] - Collection doesn't contain step (id:%s) (version:%s)", id, version) } } if version == "" { log.Infof("[STEPMAN] - Collection doesn't contain any version of step (id:%s) -- Updating StepLib", id) } else { log.Infof("[STEPMAN] - Collection doesn't contain step (id:%s) (version:%s) -- Updating StepLib", id, version) } collection, err = updateCollection(collectionURI) if err != nil { log.Fatalf("Failed to update collection (%s), err: %s", collectionURI, err) } _, stepFound := collection.GetStep(id, version) if !stepFound { if version != "" { log.Fatalf("[STEPMAN] - Even the updated collection doesn't contain step (id:%s) (version:%s)", id, version) } else { log.Fatalf("[STEPMAN] - Even the updated collection doesn't contain any version of step (id:%s)", id) } } } // If version doesn't provided use latest if version == "" { log.Debug("[STEPMAN] - Missing step version -- Use latest version") latest, err := collection.GetLatestStepVersion(id) if err != nil { log.Fatal("[STEPMAN] - Failed to get step latest version: ", err) } log.Debug("[STEPMAN] - Latest version of step: ", latest) version = latest } // Check step exist in local cache step, found := collection.GetStep(id, version) if !found { log.Fatalf("[STEPMAN] - Collection doesn't contain step (id:%s) (version:%s)", id, version) } if step.Source == nil { log.Fatal("Invalid step, missing Source property") } route, found := stepman.ReadRoute(collectionURI) if !found { log.Fatalf("No route found for lib: %s", collectionURI) } stepCacheDir := stepman.GetStepCacheDirPath(route, id, version) if exist, err := pathutil.IsPathExists(stepCacheDir); err != nil { log.Fatal("[STEPMAN] - Failed to check path:", err) } else if !exist { log.Debug("[STEPMAN] - Step does not exist, download it") if err := stepman.DownloadStep(collectionURI, collection, id, version, step.Source.Commit); err != nil { log.Fatal("[STEPMAN] - Failed to download step:", err) } } // Copy to specified path srcFolder := stepCacheDir destFolder := path if exist, err := pathutil.IsPathExists(destFolder); err != nil { log.Fatalln("[STEPMAN] - Failed to check path:", err) } else if !exist { if err := os.MkdirAll(destFolder, 0777); err != nil { log.Fatalln("[STEPMAN] - Failed to create path:", err) } } if err = cmdex.CopyDir(srcFolder+"/", destFolder, true); err != nil { log.Fatalln("[STEPMAN] - Failed to copy step:", err) } // Copy step.yml to specified path if copyYML != "" { if exist, err := pathutil.IsPathExists(copyYML); err != nil { log.Fatalln("[STEPMAN] - Failed to check path:", err) } else if exist { log.Fatalln("[STEPMAN] - Copy yml destination path exist") } stepCollectionDir := stepman.GetStepCollectionDirPath(route, id, version) stepYMLSrc := stepCollectionDir + "/step.yml" if err = cmdex.CopyFile(stepYMLSrc, copyYML); err != nil { log.Fatalln("[STEPMAN] - Failed to copy step.yml:", err) } } return nil }
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 removeStepDefaultsAndFillStepOutputs(stepListItem *models.StepListItemModel, defaultStepLibSource string) error { // Create stepIDData compositeStepIDStr, workflowStep, err := models.GetStepIDStepDataPair(*stepListItem) if err != nil { return err } stepIDData, err := models.CreateStepIDDataFromString(compositeStepIDStr, defaultStepLibSource) if err != nil { return err } // Activate step - get step.yml tempStepCloneDirPath, err := pathutil.NormalizedOSTempDirPath("step_clone") if err != nil { return err } tempStepYMLDirPath, err := pathutil.NormalizedOSTempDirPath("step_yml") if err != nil { return err } tempStepYMLFilePath := filepath.Join(tempStepYMLDirPath, "step.yml") if stepIDData.SteplibSource == "path" { stepAbsLocalPth, err := pathutil.AbsPath(stepIDData.IDorURI) if err != nil { return err } if err := cmdex.CopyFile(filepath.Join(stepAbsLocalPth, "step.yml"), tempStepYMLFilePath); err != nil { return err } } else if stepIDData.SteplibSource == "git" { if err := cmdex.GitCloneTagOrBranch(stepIDData.IDorURI, tempStepCloneDirPath, stepIDData.Version); err != nil { return err } if err := cmdex.CopyFile(filepath.Join(tempStepCloneDirPath, "step.yml"), tempStepYMLFilePath); err != nil { return err } } else if stepIDData.SteplibSource == "_" { // Steplib independent steps are completly defined in workflow tempStepYMLFilePath = "" } else if stepIDData.SteplibSource != "" { if err := tools.StepmanSetup(stepIDData.SteplibSource); err != nil { return err } if err := tools.StepmanActivate(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version, tempStepCloneDirPath, tempStepYMLFilePath); err != nil { return err } } else { return errors.New("Failed to fill step ouputs: unkown SteplibSource") } // Fill outputs if tempStepYMLFilePath != "" { specStep, err := ReadSpecStep(tempStepYMLFilePath) if err != nil { return err } if workflowStep.Title != nil && specStep.Title != nil && *workflowStep.Title == *specStep.Title { workflowStep.Title = nil } if workflowStep.Description != nil && specStep.Description != nil && *workflowStep.Description == *specStep.Description { workflowStep.Description = nil } if workflowStep.Summary != nil && specStep.Summary != nil && *workflowStep.Summary == *specStep.Summary { workflowStep.Summary = nil } if workflowStep.Website != nil && specStep.Website != nil && *workflowStep.Website == *specStep.Website { workflowStep.Website = nil } if workflowStep.SourceCodeURL != nil && specStep.SourceCodeURL != nil && *workflowStep.SourceCodeURL == *specStep.SourceCodeURL { workflowStep.SourceCodeURL = nil } if workflowStep.SupportURL != nil && specStep.SupportURL != nil && *workflowStep.SupportURL == *specStep.SupportURL { workflowStep.SupportURL = nil } workflowStep.PublishedAt = nil if workflowStep.Source != nil && specStep.Source != nil { if workflowStep.Source.Git == specStep.Source.Git { workflowStep.Source.Git = "" } if workflowStep.Source.Commit == specStep.Source.Commit { workflowStep.Source.Commit = "" } } if isStringSliceWithSameElements(workflowStep.HostOsTags, specStep.HostOsTags) { workflowStep.HostOsTags = []string{} } if isStringSliceWithSameElements(workflowStep.ProjectTypeTags, specStep.ProjectTypeTags) { workflowStep.ProjectTypeTags = []string{} } if isStringSliceWithSameElements(workflowStep.TypeTags, specStep.TypeTags) { workflowStep.TypeTags = []string{} } if isDependencySliceWithSameElements(workflowStep.Dependencies, specStep.Dependencies) { workflowStep.Dependencies = []stepmanModels.DependencyModel{} } if workflowStep.IsRequiresAdminUser != nil && specStep.IsRequiresAdminUser != nil && *workflowStep.IsRequiresAdminUser == *specStep.IsRequiresAdminUser { workflowStep.IsRequiresAdminUser = nil } if workflowStep.IsAlwaysRun != nil && specStep.IsAlwaysRun != nil && *workflowStep.IsAlwaysRun == *specStep.IsAlwaysRun { workflowStep.IsAlwaysRun = nil } if workflowStep.IsSkippable != nil && specStep.IsSkippable != nil && *workflowStep.IsSkippable == *specStep.IsSkippable { workflowStep.IsSkippable = nil } if workflowStep.RunIf != nil && specStep.RunIf != nil && *workflowStep.RunIf == *specStep.RunIf { workflowStep.RunIf = nil } inputs := []envmanModels.EnvironmentItemModel{} for _, input := range workflowStep.Inputs { sameValue := false wfKey, wfValue, err := input.GetKeyValuePair() if err != nil { return err } wfOptions, err := input.GetOptions() if err != nil { return err } sInput, err := getInputByKey(specStep.Inputs, wfKey) if err != nil { return err } _, sValue, err := sInput.GetKeyValuePair() if err != nil { return err } if wfValue == sValue { sameValue = true } sOptions, err := sInput.GetOptions() if err != nil { return err } hasOptions := false if wfOptions.Title != nil && sOptions.Title != nil && *wfOptions.Title == *sOptions.Title { wfOptions.Title = nil } else { hasOptions = true } if wfOptions.Description != nil && sOptions.Description != nil && *wfOptions.Description == *sOptions.Description { wfOptions.Description = nil } else { hasOptions = true } if wfOptions.Summary != nil && sOptions.Summary != nil && *wfOptions.Summary == *sOptions.Summary { wfOptions.Summary = nil } else { hasOptions = true } if isStringSliceWithSameElements(wfOptions.ValueOptions, sOptions.ValueOptions) { wfOptions.ValueOptions = []string{} } else { hasOptions = true } if wfOptions.IsRequired != nil && sOptions.IsRequired != nil && *wfOptions.IsRequired == *sOptions.IsRequired { wfOptions.IsRequired = nil } else { hasOptions = true } if wfOptions.IsExpand != nil && sOptions.IsExpand != nil && *wfOptions.IsExpand == *sOptions.IsExpand { wfOptions.IsExpand = nil } else { hasOptions = true } if wfOptions.IsDontChangeValue != nil && sOptions.IsDontChangeValue != nil && *wfOptions.IsDontChangeValue == *sOptions.IsDontChangeValue { wfOptions.IsDontChangeValue = nil } else { hasOptions = true } if !hasOptions && sameValue { // default env } else { if hasOptions { input[envmanModels.OptionsKey] = wfOptions } else { delete(input, envmanModels.OptionsKey) } inputs = append(inputs, input) } } workflowStep.Inputs = inputs // We need only key-value and title from spec outputs outputs := []envmanModels.EnvironmentItemModel{} for _, output := range specStep.Outputs { sKey, sValue, err := output.GetKeyValuePair() if err != nil { return err } sOptions, err := output.GetOptions() if err != nil { return err } newOutput := envmanModels.EnvironmentItemModel{ sKey: sValue, envmanModels.OptionsKey: envmanModels.EnvironmentItemOptionsModel{ Title: sOptions.Title, }, } outputs = append(outputs, newOutput) } workflowStep.Outputs = outputs (*stepListItem)[compositeStepIDStr] = workflowStep } // Cleanup if err := cmdex.RemoveDir(tempStepCloneDirPath); err != nil { return errors.New(fmt.Sprint("Failed to remove step clone dir: ", err)) } if err := cmdex.RemoveDir(tempStepYMLDirPath); err != nil { return errors.New(fmt.Sprint("Failed to remove step clone dir: ", err)) } return nil }
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 // Holds pointer to current step info, for easy usage in local register step run result methods. // The value is filled with the current running step info. var stepInfoPtr models.StepInfoModel // ------------------------------------------ // In function method - Registration methods, for register step run results. registerStepRunResults := func(runIf string, resultCode, exitCode int, err error, isLastStep bool) { stepInfoCopy := models.StepInfoModel{ ID: stepInfoPtr.ID, Version: stepInfoPtr.Version, Latest: stepInfoPtr.Latest, } stepResults := models.StepRunResultsModel{ StepInfo: stepInfoCopy, Status: resultCode, Idx: buildRunResults.ResultsCount(), RunTime: time.Now().Sub(stepStartTime), Error: err, ExitCode: exitCode, } switch resultCode { case models.StepRunStatusCodeSuccess: buildRunResults.SuccessSteps = append(buildRunResults.SuccessSteps, stepResults) break case models.StepRunStatusCodeFailed: log.Errorf("Step (%s) failed, error: (%v)", stepInfoCopy.ID, err) buildRunResults.FailedSteps = append(buildRunResults.FailedSteps, stepResults) break case models.StepRunStatusCodeFailedSkippable: log.Warnf("Step (%s) failed, but was marked as skippable, error: (%v)", stepInfoCopy.ID, err) 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.ID) buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break case models.StepRunStatusCodeSkippedWithRunIf: log.Warn("The step's (" + stepInfoCopy.ID + ") 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.PrintStepSummary(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 = models.StepInfoModel{} // 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } // // Preparing the step // Get step id & version data compositeStepIDStr, workflowStep, err := models.GetStepIDStepDataPair(stepListItm) stepInfoPtr.ID = compositeStepIDStr if workflowStep.Title != nil && *workflowStep.Title != "" { stepInfoPtr.ID = *workflowStep.Title } if err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepIDData, err := models.CreateStepIDDataFromString(compositeStepIDStr, defaultStepLibSource) stepInfoPtr.ID = stepIDData.IDorURI if workflowStep.Title != nil && *workflowStep.Title != "" { stepInfoPtr.ID = *workflowStep.Title } stepInfoPtr.Version = stepIDData.Version if err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepDir := bitrise.BitriseWorkStepsDirPath stepYMLPth := path.Join(bitrise.BitriseWorkDirPath, "current_step.yml") // Activating the step 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } log.Debugln("stepAbsLocalPth:", stepAbsLocalPth, "|stepDir:", stepDir) if err := cmdex.CopyDir(stepAbsLocalPth, stepDir, true); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if err := cmdex.CopyFile(path.Join(stepAbsLocalPth, "step.yml"), stepYMLPth); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if len(stepIDData.IDorURI) > 20 { stepInfoPtr.Version = fmt.Sprintf("path:...%s", stringutil.MaxLastChars(stepIDData.IDorURI, 17)) } else { stepInfoPtr.Version = fmt.Sprintf("path:%s", stepIDData.IDorURI) } } 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 { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if err := cmdex.CopyFile(path.Join(stepDir, "step.yml"), stepYMLPth); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepInfoPtr.Version = fmt.Sprintf("git:...%s", stringutil.MaxLastChars(stepIDData.IDorURI, 10)) if stepIDData.Version != "" { stepInfoPtr.Version = stepInfoPtr.Version + "@" + stepIDData.Version } } 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if err := cmdex.GitCloneTagOrBranch(stepIDData.IDorURI, stepDir, stepIDData.Version); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepInfoPtr.Version = "_" if stepIDData.Version != "" { if len(stepIDData.Version) > 20 { stepInfoPtr.Version = stepInfoPtr.Version + "@..." + stringutil.MaxLastChars(stepIDData.Version, 17) } else { stepInfoPtr.Version = stepInfoPtr.Version + "@" + stepIDData.Version } } } 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 := bitrise.StepmanSetup(stepIDData.SteplibSource); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepInfo, err := bitrise.StepmanStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { if buildRunResults.IsStepLibUpdated(stepIDData.SteplibSource) { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } // May StepLib should be updated log.Info("Step info not found in StepLib (%s) -- Updating ...") if err := bitrise.StepmanUpdate(stepIDData.SteplibSource); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } buildRunResults.StepmanUpdates[stepIDData.SteplibSource]++ stepInfo, err = bitrise.StepmanStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } } stepInfoPtr.Version = stepInfo.Version stepInfoPtr.Latest = stepInfo.Latest if err := bitrise.StepmanActivate(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version, stepDir, stepYMLPth); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } else { log.Debugf("[BITRISE_CLI] - Step activated: (ID:%s) (version:%s)", stepIDData.IDorURI, stepIDData.Version) } } else { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, fmt.Errorf("Invalid stepIDData: No SteplibSource or LocalPath defined (%v)", stepIDData), isLastStep) 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } mergedStep, err = models.MergeStepWith(specStep, workflowStep) if err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } } // Run step bitrise.PrintRunningStep(stepInfoPtr, idx) if mergedStep.RunIf != nil && *mergedStep.RunIf != "" { isRun, err := bitrise.EvaluateStepTemplateToBool(*mergedStep.RunIf, buildRunResults) if err != nil { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if !isRun { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeSkippedWithRunIf, 0, err, isLastStep) continue } } outEnvironments := []envmanModels.EnvironmentItemModel{} 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.RunIf, models.StepRunStatusCodeSkipped, 0, err, isLastStep) } else { exit, out, err := runStep(mergedStep, stepIDData, stepDir, *environments) outEnvironments = out if err != nil { if *mergedStep.IsSkippable { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeFailedSkippable, exit, err, isLastStep) } else { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeFailed, exit, err, isLastStep) } } else { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeSuccess, 0, nil, isLastStep) *environments = append(*environments, outEnvironments...) } } } return buildRunResults }
// DownloadPluginFromURL .... func DownloadPluginFromURL(URL, dst string) error { url, err := url.Parse(URL) if err != nil { return err } scheme := url.Scheme tokens := strings.Split(URL, "/") fileName := tokens[len(tokens)-1] tmpDstFilePath := "" if scheme != "file" { OS, arch, err := getOsAndArch() if err != nil { return err } urlWithSuffix := URL urlSuffix := fmt.Sprintf("-%s-%s", OS, arch) if !strings.HasSuffix(URL, urlSuffix) { urlWithSuffix = urlWithSuffix + urlSuffix } urls := []string{urlWithSuffix, URL} tmpDir, err := pathutil.NormalizedOSTempDirPath("plugin") if err != nil { return err } tmpDst := path.Join(tmpDir, fileName) output, err := os.Create(tmpDst) if err != nil { return err } defer func() { if err := output.Close(); err != nil { log.Errorf("Failed to close file, err: %s", err) } }() success := false var response *http.Response for _, aURL := range urls { response, err = http.Get(aURL) if response != nil { defer func() { if err := response.Body.Close(); err != nil { log.Errorf("Failed to close response body, err: %s", err) } }() } if err != nil { log.Errorf("%s", err) } else { success = true break } } if !success { return err } if _, err := io.Copy(output, response.Body); err != nil { return err } tmpDstFilePath = output.Name() } else { tmpDstFilePath = strings.Replace(URL, scheme+"://", "", -1) } if err := cmdex.CopyFile(tmpDstFilePath, dst); err != nil { return err } return nil }
func main() { configs := createConfigsModelFromEnvs() 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) } if configs.ApkFileIncludeFilter == "" { configs.ApkFileIncludeFilter = "*.apk" } err := os.Chmod(configs.GradlewPath, 0770) if err != nil { log.Fail("Failed to add executable permission on gradlew file (%s), error: %s", configs.GradlewPath, err) } log.Info("Running gradle task...") if err := runGradleTask(configs.GradlewPath, configs.GradleFile, configs.GradleTasks, configs.GradleOptions); err != nil { log.Fail("Gradle task failed, error: %s", err) } // Move apk files log.Info("Move apk files...") apkFiles, err := find(".", configs.ApkFileIncludeFilter, configs.ApkFileExcludeFilter) if err != nil { log.Fail("Failed to find apk files, error: %s", err) } if len(apkFiles) == 0 { log.Warn("No apk matched the filters") } lastCopiedApkFile := "" for _, apkFile := range apkFiles { ext := filepath.Ext(apkFile) baseName := filepath.Base(apkFile) baseName = strings.TrimSuffix(baseName, ext) deployPth, err := findDeployPth(configs.DeployDir, baseName, ext) if err != nil { log.Fail("Failed to create apk deploy path, error: %s", err) } log.Detail("copy %s to %s", apkFile, deployPth) cmdex.CopyFile(apkFile, deployPth) lastCopiedApkFile = deployPth } if lastCopiedApkFile != "" { if err := exportEnvironmentWithEnvman("BITRISE_APK_PATH", lastCopiedApkFile); err != nil { log.Fail("Failed to export enviroment (BITRISE_APK_PATH), error: %s", err) } log.Done("The apk path is now available in the Environment Variable: $BITRISE_APK_PATH (value: %s)", lastCopiedApkFile) } // Move mapping files log.Info("Move mapping files...") mappingFiles, err := find(".", configs.MappingFileIncludeFilter, configs.MappingFileExcludeFilter) if err != nil { log.Fail("Failed to find mapping files, error: %s", err) } if len(mappingFiles) == 0 { log.Detail("No mapping file matched the filters") } lastCopiedMappingFile := "" for _, mappingFile := range mappingFiles { ext := filepath.Ext(mappingFile) baseName := filepath.Base(mappingFile) baseName = strings.TrimSuffix(baseName, ext) deployPth, err := findDeployPth(configs.DeployDir, baseName, ext) if err != nil { log.Fail("Failed to create mapping deploy path, error: %s", err) } log.Detail("copy %s to %s", mappingFile, deployPth) cmdex.CopyFile(mappingFile, deployPth) lastCopiedMappingFile = deployPth } if lastCopiedMappingFile != "" { if err := exportEnvironmentWithEnvman("BITRISE_MAPPING_PATH", lastCopiedMappingFile); err != nil { log.Fail("Failed to export enviroment (BITRISE_MAPPING_PATH), error: %s", err) } log.Done("The mapping path is now available in the Environment Variable: $BITRISE_MAPPING_PATH (value: %s)", lastCopiedMappingFile) } }
// InstallPlugin ... func InstallPlugin(srcURL, binURL, versionTag string) (Plugin, string, error) { // // Download plugin src pluginSrcTmpDir, err := pathutil.NormalizedOSTempDirPath("plugin-src-tmp") if err != nil { return Plugin{}, "", fmt.Errorf("failed to create plugin src temp directory, error: %s", err) } defer func() { if err := os.RemoveAll(pluginSrcTmpDir); err != nil { log.Warnf("Failed to remove path (%s)", pluginSrcTmpDir) } }() newVersionPtr, newVersinHash, err := clonePluginSrc(srcURL, versionTag, pluginSrcTmpDir) if err != nil { return Plugin{}, "", fmt.Errorf("failed to download plugin, error: %s", err) } log.Debugf("Plugin downloaded from (%s) to (%s)", srcURL, pluginSrcTmpDir) // // Parse and validate plugin.yml // Validate bitrise-plugin.yml tmpPluginYMLPath := filepath.Join(pluginSrcTmpDir, pluginYMLName) if err := validatePath(tmpPluginYMLPath); err != nil { return Plugin{}, "", fmt.Errorf("bitrise-plugin.yml validation failed, error: %s", err) } newPlugin, err := NewPluginFromYML(tmpPluginYMLPath) if err != nil { return Plugin{}, "", fmt.Errorf("failed to parse bitrise-plugin.yml (%s), error: %s", tmpPluginYMLPath, err) } // Check if executable exist if newPlugin.ExecutableURL() == "" && binURL == "" { tmpPluginExecutablePath := filepath.Join(pluginSrcTmpDir, pluginShName) if err := validatePath(tmpPluginExecutablePath); err != nil { return Plugin{}, "", fmt.Errorf("bitrise-plugin.sh validation failed, error: %s", err) } } // Check tool requirements currentVersionMap, err := version.ToolVersionMap() if err != nil { return Plugin{}, "", fmt.Errorf("failed to get current version map, error: %s", err) } if err := validateRequirements(newPlugin.Requirements, currentVersionMap); err != nil { return Plugin{}, "", fmt.Errorf("requirements validation failed, error: %s", err) } log.Debugf("Downloaded plugin: %#v validated", newPlugin) // // Check if plugin already installed if route, found, err := ReadPluginRoute(newPlugin.Name); err != nil { return Plugin{}, "", fmt.Errorf("failed to check if plugin already installed, error: %s", err) } else if found { log.Debugf("Plugin already installed with name (%s)", newPlugin.Name) if route.Source != srcURL { return Plugin{}, "", fmt.Errorf("plugin already installed with name (%s) from different source (%s)", route.Name, route.Source) } installedPluginVersionPtr, err := GetPluginVersion(route.Name) if err != nil { return Plugin{}, "", fmt.Errorf("failed to check installed plugin (%s) version, error: %s", route.Name, err) } if newVersionPtr != nil && installedPluginVersionPtr != nil && installedPluginVersionPtr.GreaterThan(newVersionPtr) { return Plugin{}, "", fmt.Errorf("installed plugin version (%s) greater then new plugin version (%s)", installedPluginVersionPtr.String(), (*newVersionPtr).String()) } installedPluginVersion := "local" if installedPluginVersionPtr != nil { installedPluginVersion = (*installedPluginVersionPtr).String() } fmt.Println() log.Infof("Installed plugin found with version (%s), overriding it...", installedPluginVersion) } // // Intsall plugin into bitrise installSuccess := true pluginDir := GetPluginDir(newPlugin.Name) if err := os.RemoveAll(pluginDir); err != nil { return Plugin{}, "", fmt.Errorf("failed to remove plugin dir (%s), error: %s", pluginDir, err) } defer func() { if installSuccess { return } if err := os.RemoveAll(pluginDir); err != nil { log.Warnf("Failed to remove path (%s)", pluginDir) } }() // Install plugin src plginSrcDir := GetPluginSrcDir(newPlugin.Name) if err := os.MkdirAll(plginSrcDir, 0777); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to create plugin src dir (%s), error: %s", plginSrcDir, err) } if err := cmdex.CopyDir(pluginSrcTmpDir, plginSrcDir, true); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to copy plugin from temp dir (%s) to (%s), error: %s", pluginSrcTmpDir, plginSrcDir, err) } executableURL := newPlugin.ExecutableURL() if binURL != "" { executableURL = binURL } if executableURL != "" { // Install plugin bin pluginBinTmpDir, err := pathutil.NormalizedOSTempDirPath("plugin-bin-tmp") if err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to create plugin bin temp directory, error: %s", err) } defer func() { if err := os.RemoveAll(pluginBinTmpDir); err != nil { log.Warnf("Failed to remove path (%s)", pluginBinTmpDir) } }() pluginBinTmpFilePath := filepath.Join(pluginBinTmpDir, newPlugin.Name) if err := downloadPluginBin(executableURL, pluginBinTmpFilePath); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to download plugin executable from (%s), error: %s", executableURL, err) } plginBinDir := GetPluginBinDir(newPlugin.Name) if err := os.MkdirAll(plginBinDir, 0777); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to create plugin bin dir (%s), error: %s", plginBinDir, err) } pluginBinFilePath := filepath.Join(plginBinDir, newPlugin.Name) if err := cmdex.CopyFile(pluginBinTmpFilePath, pluginBinFilePath); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to copy plugin from temp dir (%s) to (%s), error: %s", pluginBinTmpFilePath, pluginBinFilePath, err) } if err := os.Chmod(pluginBinFilePath, 0777); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to make plugin bin executable, error: %s", err) } } newVersionStr := "" if newVersionPtr != nil { newVersionStr = (*newVersionPtr).String() } pluginDataDir := filepath.Join(pluginDir, "data") if err := os.MkdirAll(pluginDataDir, 0777); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to create plugin data dir (%s), error: %s", pluginDataDir, err) } if err := CreateAndAddPluginRoute(newPlugin.Name, srcURL, executableURL, newVersionStr, newVersinHash, newPlugin.TriggerEvent); err != nil { installSuccess = false return Plugin{}, "", fmt.Errorf("failed to add plugin route, error: %s", err) } if newVersionStr == "" { newVersionStr = "local" } return newPlugin, newVersionStr, nil }
func main() { configs := createConfigsModelFromEnvs() fmt.Println() configs.print() if err := configs.validate(); err != nil { fail("Issue with input: %s", err) } log.Info("step determined configs:") // Detect Xcode major version xcodebuildVersion, err := utils.XcodeBuildVersion() if err != nil { fail("Failed to determin xcode version, error: %s", err) } log.Detail("- xcodebuildVersion: %s (%s)", xcodebuildVersion.XcodeVersion.String(), xcodebuildVersion.BuildVersion) xcodeMajorVersion := xcodebuildVersion.XcodeVersion.Segments()[0] if xcodeMajorVersion < minSupportedXcodeMajorVersion { fail("Invalid xcode major version (%s), should not be less then min supported: %d", xcodeMajorVersion, minSupportedXcodeMajorVersion) } // Detect xcpretty version if configs.OutputTool == "xcpretty" { if !utils.IsXcprettyInstalled() { fail(`xcpretty is not installed For xcpretty installation see: 'https://github.com/supermarin/xcpretty', or use 'xcodebuild' as 'output_tool'.`) } xcprettyVersion, err := utils.XcprettyVersion() if err != nil { fail("Failed to determin xcpretty version, error: %s", err) } log.Detail("- xcprettyVersion: %s", xcprettyVersion.String()) } // Validation CustomExportOptionsPlistContent if configs.CustomExportOptionsPlistContent != "" && xcodeMajorVersion < 7 { log.Warn("CustomExportOptionsPlistContent is set, but CustomExportOptionsPlistContent only used if xcodeMajorVersion > 6") configs.CustomExportOptionsPlistContent = "" } if configs.ForceProvisioningProfileSpecifier != "" && xcodeMajorVersion < 8 { log.Warn("ForceProvisioningProfileSpecifier is set, but ForceProvisioningProfileSpecifier only used if xcodeMajorVersion > 7") configs.ForceProvisioningProfileSpecifier = "" } if configs.ForceTeamID == "" && xcodeMajorVersion < 8 { log.Warn("ForceTeamID is set, but ForceTeamID only used if xcodeMajorVersion > 7") configs.ForceTeamID = "" } if configs.ForceProvisioningProfileSpecifier != "" && configs.ForceProvisioningProfile != "" { log.Warn("both ForceProvisioningProfileSpecifier and ForceProvisioningProfile are set, using ForceProvisioningProfileSpecifier") configs.ForceProvisioningProfile = "" } fmt.Println() // abs out dir pth absOutputDir, err := pathutil.AbsPath(configs.OutputDir) if err != nil { fail("Failed to expand OutputDir (%s), error: %s", configs.OutputDir, err) } configs.OutputDir = absOutputDir if exist, err := pathutil.IsPathExists(configs.OutputDir); err != nil { fail("Failed to check if OutputDir exist, error: %s", err) } else if !exist { if err := os.MkdirAll(configs.OutputDir, 0777); err != nil { fail("Failed to create OutputDir (%s), error: %s", configs.OutputDir, err) } } // output files tmpArchiveDir, err := pathutil.NormalizedOSTempDirPath("__archive__") if err != nil { fail("Failed to create temp dir for archives, error: %s", err) } tmpArchivePath := filepath.Join(tmpArchiveDir, configs.ArtifactName+".xcarchive") appPath := filepath.Join(configs.OutputDir, configs.ArtifactName+".app") ipaPath := filepath.Join(configs.OutputDir, configs.ArtifactName+".ipa") exportOptionsPath := filepath.Join(configs.OutputDir, "export_options.plist") rawXcodebuildOutputLogPath := filepath.Join(configs.OutputDir, "raw-xcodebuild-output.log") dsymZipPath := filepath.Join(configs.OutputDir, configs.ArtifactName+".dSYM.zip") archiveZipPath := filepath.Join(configs.OutputDir, configs.ArtifactName+".xcarchive.zip") ideDistributionLogsZipPath := filepath.Join(configs.OutputDir, "xcodebuild.xcdistributionlogs.zip") // cleanup filesToCleanup := []string{ appPath, ipaPath, exportOptionsPath, rawXcodebuildOutputLogPath, dsymZipPath, archiveZipPath, ideDistributionLogsZipPath, } for _, pth := range filesToCleanup { if exist, err := pathutil.IsPathExists(pth); err != nil { fail("Failed to check if path (%s) exist, error: %s", pth, err) } else if exist { if err := os.RemoveAll(pth); err != nil { fail("Failed to remove path (%s), error: %s", pth, err) } } } // // Create the Archive with Xcode Command Line tools log.Info("Create the Archive ...") fmt.Println() isWorkspace := false ext := filepath.Ext(configs.ProjectPath) if ext == ".xcodeproj" { isWorkspace = false } else if ext == ".xcworkspace" { isWorkspace = true } else { fail("Project file extension should be .xcodeproj or .xcworkspace, but got: %s", ext) } archiveCmd := xcodebuild.NewArchiveCommand(configs.ProjectPath, isWorkspace) archiveCmd.SetScheme(configs.Scheme) archiveCmd.SetConfiguration(configs.Configuration) if configs.ForceTeamID != "" { log.Detail("Forcing Development Team: %s", configs.ForceTeamID) archiveCmd.SetForceDevelopmentTeam(configs.ForceTeamID) } if configs.ForceProvisioningProfileSpecifier != "" { log.Detail("Forcing Provisioning Profile Specifier: %s", configs.ForceProvisioningProfileSpecifier) archiveCmd.SetForceProvisioningProfileSpecifier(configs.ForceProvisioningProfileSpecifier) } if configs.ForceProvisioningProfile != "" { log.Detail("Forcing Provisioning Profile: %s", configs.ForceProvisioningProfile) archiveCmd.SetForceProvisioningProfile(configs.ForceProvisioningProfile) } if configs.ForceCodeSignIdentity != "" { log.Detail("Forcing Code Signing Identity: %s", configs.ForceCodeSignIdentity) archiveCmd.SetForceCodeSignIdentity(configs.ForceCodeSignIdentity) } if configs.IsCleanBuild == "yes" { archiveCmd.SetCustomBuildAction("clean") } archiveCmd.SetArchivePath(tmpArchivePath) if configs.XcodebuildOptions != "" { options, err := shellquote.Split(configs.XcodebuildOptions) if err != nil { fail("Failed to shell split XcodebuildOptions (%s), error: %s", configs.XcodebuildOptions) } archiveCmd.SetCustomOptions(options) } if configs.OutputTool == "xcpretty" { xcprettyCmd := xcpretty.New(archiveCmd) logWithTimestamp(colorstring.Green, "$ %s", xcprettyCmd.PrintableCmd()) fmt.Println() if rawXcodebuildOut, err := xcprettyCmd.Run(); err != nil { if err := utils.ExportOutputFileContent(rawXcodebuildOut, rawXcodebuildOutputLogPath, bitriseXcodeRawResultTextEnvKey); err != nil { log.Warn("Failed to export %s, error: %s", bitriseXcodeRawResultTextEnvKey, err) } else { log.Warn(`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_RESULT_TEXT_PATH environment variable`) } fail("Archive failed, error: %s", err) } } else { logWithTimestamp(colorstring.Green, "$ %s", archiveCmd.PrintableCmd()) fmt.Println() if err := archiveCmd.Run(); err != nil { fail("Archive failed, error: %s", err) } } fmt.Println() // Ensure xcarchive exists if exist, err := pathutil.IsPathExists(tmpArchivePath); err != nil { fail("Failed to check if archive exist, error: %s", err) } else if !exist { fail("No archive generated at: %s", tmpArchivePath) } // // Exporting the ipa with Xcode Command Line tools /* You'll get a "Error Domain=IDEDistributionErrorDomain Code=14 "No applicable devices found."" error if $GEM_HOME is set and the project's directory includes a Gemfile - to fix this we'll unset GEM_HOME as that's not required for xcodebuild anyway. This probably fixes the RVM issue too, but that still should be tested. See also: - http://stackoverflow.com/questions/33041109/xcodebuild-no-applicable-devices-found-when-exporting-archive - https://gist.github.com/claybridges/cea5d4afd24eda268164 */ log.Info("Exporting ipa from the archive...") fmt.Println() envsToUnset := []string{"GEM_HOME", "GEM_PATH", "RUBYLIB", "RUBYOPT", "BUNDLE_BIN_PATH", "_ORIGINAL_GEM_PATH", "BUNDLE_GEMFILE"} for _, key := range envsToUnset { if err := os.Unsetenv(key); err != nil { fail("Failed to unset (%s), error: %s", key, err) } } if xcodeMajorVersion == 6 || configs.UseDeprecatedExport == "yes" { log.Detail("Using legacy export") /* Get the name of the profile which was used for creating the archive --> Search for embedded.mobileprovision in the xcarchive. It should contain a .app folder in the xcarchive folder under the Products/Applications folder */ embeddedProfilePth, err := xcarchive.EmbeddedMobileProvisionPth(tmpArchivePath) if err != nil { fail("Failed to get embedded profile path, error: %s", err) } provProfile, err := provisioningprofile.NewFromFile(embeddedProfilePth) if err != nil { fail("Failed to create provisioning profile model, error: %s", err) } if provProfile.Name == nil { fail("Profile name empty") } legacyExportCmd := xcodebuild.NewLegacyExportCommand() legacyExportCmd.SetExportFormat("ipa") legacyExportCmd.SetArchivePath(tmpArchivePath) legacyExportCmd.SetExportPath(ipaPath) legacyExportCmd.SetExportProvisioningProfileName(*provProfile.Name) if configs.OutputTool == "xcpretty" { xcprettyCmd := xcpretty.New(legacyExportCmd) logWithTimestamp(colorstring.Green, xcprettyCmd.PrintableCmd()) fmt.Println() if rawXcodebuildOut, err := xcprettyCmd.Run(); err != nil { if err := utils.ExportOutputFileContent(rawXcodebuildOut, rawXcodebuildOutputLogPath, bitriseXcodeRawResultTextEnvKey); err != nil { log.Warn("Failed to export %s, error: %s", bitriseXcodeRawResultTextEnvKey, err) } else { log.Warn(`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_RESULT_TEXT_PATH environment variable`) } fail("Export failed, error: %s", err) } } else { logWithTimestamp(colorstring.Green, legacyExportCmd.PrintableCmd()) fmt.Println() if err := legacyExportCmd.Run(); err != nil { fail("Export failed, error: %s", err) } } } else { log.Detail("Using export options") if configs.CustomExportOptionsPlistContent != "" { log.Detail("Custom export options content provided:") fmt.Println(configs.CustomExportOptionsPlistContent) if err := fileutil.WriteStringToFile(exportOptionsPath, configs.CustomExportOptionsPlistContent); err != nil { fail("Failed to write export options to file, error: %s", err) } } else { log.Detail("Generating export options") var method exportoptions.Method if configs.ExportMethod == "auto-detect" { log.Detail("auto-detect export method, based on embedded profile") embeddedProfilePth, err := xcarchive.EmbeddedMobileProvisionPth(tmpArchivePath) if err != nil { fail("Failed to get embedded profile path, error: %s", err) } provProfile, err := provisioningprofile.NewFromFile(embeddedProfilePth) if err != nil { fail("Failed to create provisioning profile model, error: %s", err) } method = provProfile.GetExportMethod() log.Detail("detected export method: %s", method) } else { log.Detail("using export-method input: %s", configs.ExportMethod) parsedMethod, err := exportoptions.ParseMethod(configs.ExportMethod) if err != nil { fail("Failed to parse export options, error: %s", err) } method = parsedMethod } var exportOpts exportoptions.ExportOptions if method == exportoptions.MethodAppStore { options := exportoptions.NewAppStoreOptions() options.UploadBitcode = (configs.UploadBitcode == "yes") options.TeamID = configs.TeamID exportOpts = options } else { options := exportoptions.NewNonAppStoreOptions(method) options.CompileBitcode = (configs.CompileBitcode == "yes") options.TeamID = configs.TeamID exportOpts = options } log.Detail("generated export options content:") fmt.Println() fmt.Println(exportOpts.String()) if err = exportOpts.WriteToFile(exportOptionsPath); err != nil { fail("Failed to write export options to file, error: %s", err) } } fmt.Println() tmpDir, err := pathutil.NormalizedOSTempDirPath("__export__") if err != nil { fail("Failed to create tmp dir, error: %s", err) } exportCmd := xcodebuild.NewExportCommand() exportCmd.SetArchivePath(tmpArchivePath) exportCmd.SetExportDir(tmpDir) exportCmd.SetExportOptionsPlist(exportOptionsPath) if configs.OutputTool == "xcpretty" { xcprettyCmd := xcpretty.New(exportCmd) logWithTimestamp(colorstring.Green, xcprettyCmd.PrintableCmd()) fmt.Println() if xcodebuildOut, err := xcprettyCmd.Run(); err != nil { // xcodebuild raw output if err := utils.ExportOutputFileContent(xcodebuildOut, rawXcodebuildOutputLogPath, bitriseXcodeRawResultTextEnvKey); err != nil { log.Warn("Failed to export %s, error: %s", bitriseXcodeRawResultTextEnvKey, err) } else { log.Warn(`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_RESULT_TEXT_PATH environment variable`) } // xcdistributionlogs if logsDirPth, err := findIDEDistrubutionLogsPath(xcodebuildOut); err != nil { log.Warn("Failed to find xcdistributionlogs, error: %s", err) } else if err := utils.ExportOutputDirAsZip(logsDirPth, ideDistributionLogsZipPath, bitriseIDEDistributionLogsPthEnvKey); err != nil { log.Warn("Failed to export %s, error: %s", bitriseIDEDistributionLogsPthEnvKey, err) } else { log.Warn(`Also please check the xcdistributionlogs The logs directory is stored in $BITRISE_DEPLOY_DIR, and its full path is available in the $BITRISE_IDEDISTRIBUTION_LOGS_PATH environment variable`) } fail("Export failed, error: %s", err) } } else { logWithTimestamp(colorstring.Green, exportCmd.PrintableCmd()) fmt.Println() if xcodebuildOut, err := exportCmd.RunAndReturnOutput(); err != nil { // xcdistributionlogs if logsDirPth, err := findIDEDistrubutionLogsPath(xcodebuildOut); err != nil { log.Warn("Failed to find xcdistributionlogs, error: %s", err) } else if err := utils.ExportOutputDirAsZip(logsDirPth, ideDistributionLogsZipPath, bitriseIDEDistributionLogsPthEnvKey); err != nil { log.Warn("Failed to export %s, error: %s", bitriseIDEDistributionLogsPthEnvKey, err) } else { log.Warn(`If you can't find the reason of the error in the log, please check the xcdistributionlogs The logs directory is stored in $BITRISE_DEPLOY_DIR, and its full path is available in the $BITRISE_IDEDISTRIBUTION_LOGS_PATH environment variable`) } fail("Export failed, error: %s", err) } } // Search for ipa pattern := filepath.Join(tmpDir, "*.ipa") ipas, err := filepath.Glob(pattern) if err != nil { fail("Failed to collect ipa files, error: %s", err) } if len(ipas) == 0 { fail("No ipa found with pattern: %s", pattern) } else if len(ipas) == 1 { if err := cmdex.CopyFile(ipas[0], ipaPath); err != nil { fail("Failed to copy (%s) -> (%s), error: %s", ipas[0], ipaPath, err) } } else { log.Warn("More than 1 .ipa file found") for _, ipa := range ipas { base := filepath.Base(ipa) deployPth := filepath.Join(configs.OutputDir, base) if err := cmdex.CopyFile(ipa, deployPth); err != nil { fail("Failed to copy (%s) -> (%s), error: %s", ipas[0], ipaPath, err) } ipaPath = ipa } } } log.Info("Exporting outputs...") // // Export outputs // Export .xcarchive fmt.Println() if err := utils.ExportOutputDir(tmpArchivePath, tmpArchivePath, bitriseXCArchivePthEnvKey); err != nil { fail("Failed to export %s, error: %s", bitriseXCArchivePthEnvKey, err) } log.Done("The xcarchive path is now available in the Environment Variable: %s (value: %s)", bitriseXCArchivePthEnvKey, tmpArchivePath) if configs.IsExportXcarchiveZip == "yes" { if err := utils.ExportOutputDirAsZip(tmpArchivePath, archiveZipPath, bitriseXCArchiveZipPthEnvKey); err != nil { fail("Failed to export %s, error: %s", bitriseXCArchiveZipPthEnvKey, err) } log.Done("The xcarchive zip path is now available in the Environment Variable: %s (value: %s)", bitriseXCArchiveZipPthEnvKey, archiveZipPath) } // Export .app fmt.Println() exportedApp, err := xcarchive.FindApp(tmpArchivePath) if err != nil { fail("Failed to find app, error: %s", err) } if err := utils.ExportOutputDir(exportedApp, exportedApp, bitriseAppDirPthEnvKey); err != nil { fail("Failed to export %s, error: %s", bitriseAppDirPthEnvKey, err) } log.Done("The app directory is now available in the Environment Variable: %s (value: %s)", bitriseAppDirPthEnvKey, appPath) // Export .ipa fmt.Println() if err := utils.ExportOutputFile(ipaPath, ipaPath, bitriseIPAPthEnvKey); err != nil { fail("Failed to export %s, error: %s", bitriseIPAPthEnvKey, err) } log.Done("The ipa path is now available in the Environment Variable: %s (value: %s)", bitriseIPAPthEnvKey, ipaPath) // Export .dSYMs fmt.Println() appDSYM, frameworkDSYMs, err := xcarchive.FindDSYMs(tmpArchivePath) if err != nil { fail("Failed to export dsyms, error: %s", err) } dsymDir, err := pathutil.NormalizedOSTempDirPath("__dsyms__") if err != nil { fail("Failed to create tmp dir, error: %s", err) } if err := cmdex.CopyDir(appDSYM, dsymDir, false); err != nil { fail("Failed to copy (%s) -> (%s), error: %s", appDSYM, dsymDir, err) } if configs.ExportAllDsyms == "yes" { for _, dsym := range frameworkDSYMs { if err := cmdex.CopyDir(dsym, dsymDir, false); err != nil { fail("Failed to copy (%s) -> (%s), error: %s", dsym, dsymDir, err) } } } if err := utils.ExportOutputDir(dsymDir, dsymDir, bitriseDSYMDirPthEnvKey); err != nil { fail("Failed to export %s, error: %s", bitriseDSYMDirPthEnvKey, err) } log.Done("The dSYM dir path is now available in the Environment Variable: %s (value: %s)", bitriseDSYMDirPthEnvKey, dsymDir) if err := utils.ExportOutputDirAsZip(dsymDir, dsymZipPath, bitriseDSYMPthEnvKey); err != nil { fail("Failed to export %s, error: %s", bitriseDSYMPthEnvKey, err) } log.Done("The dSYM zip path is now available in the Environment Variable: %s (value: %s)", bitriseDSYMPthEnvKey, dsymZipPath) }
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") var stepStartTime time.Time registerStepRunResults := func(step stepmanModels.StepModel, resultCode, exitCode int, err error, isLastStep bool) { if step.Title == nil { log.Error("Step title is nil, should not happend!") step.Title = pointers.NewStringPtr("ERROR! Step title is nil!") } stepResults := models.StepRunResultsModel{ StepName: *step.Title, Status: resultCode, Idx: buildRunResults.ResultsCount(), RunTime: time.Now().Sub(stepStartTime), Error: err, ExitCode: exitCode, } switch resultCode { case models.StepRunStatusCodeSuccess: buildRunResults.SuccessSteps = append(buildRunResults.SuccessSteps, stepResults) break case models.StepRunStatusCodeFailed: log.Errorf("Step (%s) failed, error: (%v)", *step.Title, err) buildRunResults.FailedSteps = append(buildRunResults.FailedSteps, stepResults) break case models.StepRunStatusCodeFailedSkippable: log.Warnf("Step (%s) failed, but was marked as skippable, error: (%v)", *step.Title, err) 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", *step.Title) buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break case models.StepRunStatusCodeSkippedWithRunIf: log.Warn("The step's (" + *step.Title + ") Run-If expression evaluated to false - skipping") log.Info("The Run-If expression was: ", colorstring.Blue(*step.RunIf)) buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break default: log.Error("Unkown result code") return } bitrise.PrintStepSummary(stepResults, isLastStep) } registerStepListItemRunResults := func(stepListItem models.StepListItemModel, resultCode, exitCode int, err error, isLastStep bool) { name := "" for key := range stepListItem { name = key break } stepResults := models.StepRunResultsModel{ StepName: name, Status: resultCode, Idx: buildRunResults.ResultsCount(), RunTime: time.Now().Sub(stepStartTime), Error: err, ExitCode: exitCode, } switch resultCode { case models.StepRunStatusCodeSuccess: buildRunResults.SuccessSteps = append(buildRunResults.SuccessSteps, stepResults) break case models.StepRunStatusCodeFailed: log.Errorf("Step (%s) failed, error: (%v)", name, err) buildRunResults.FailedSteps = append(buildRunResults.FailedSteps, stepResults) break case models.StepRunStatusCodeFailedSkippable: log.Warnf("Step (%s) failed, but was marked as skippable, error: (%v)", name, err) 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", name) buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break case models.StepRunStatusCodeSkippedWithRunIf: log.Warn("The step's (" + name + ") Run-If expression evaluated to false - skipping") buildRunResults.SkippedSteps = append(buildRunResults.SkippedSteps, stepResults) break default: log.Error("Unkown result code") return } bitrise.PrintStepSummary(stepResults, isLastStep) } for idx, stepListItm := range workflow.Steps { stepStartTime = time.Now() isLastStep := isLastWorkflow && (idx == len(workflow.Steps)-1) if err := bitrise.SetBuildFailedEnv(buildRunResults.IsBuildFailed()); err != nil { log.Error("Failed to set Build Status envs") } compositeStepIDStr, workflowStep, err := models.GetStepIDStepDataPair(stepListItm) if err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepIDData, err := models.CreateStepIDDataFromString(compositeStepIDStr, defaultStepLibSource) if err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepVersionForInfoPrint := stepIDData.Version log.Debugf("[BITRISE_CLI] - Running Step: %#v", workflowStep) if err := bitrise.CleanupStepWorkDir(); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepDir := bitrise.BitriseWorkStepsDirPath stepYMLPth := path.Join(bitrise.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 { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } log.Debugln("stepAbsLocalPth:", stepAbsLocalPth, "|stepDir:", stepDir) if err := cmdex.CopyDir(stepAbsLocalPth, stepDir, true); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if err := cmdex.CopyFile(path.Join(stepAbsLocalPth, "step.yml"), stepYMLPth); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if len(stepIDData.IDorURI) > 20 { stepVersionForInfoPrint = fmt.Sprintf("path:...%s", stringutil.MaxLastChars(stepIDData.IDorURI, 17)) } else { stepVersionForInfoPrint = fmt.Sprintf("path:%s", stepIDData.IDorURI) } } 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 { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if err := cmdex.CopyFile(path.Join(stepDir, "step.yml"), stepYMLPth); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepVersionForInfoPrint = fmt.Sprintf("git:...%s", stringutil.MaxLastChars(stepIDData.IDorURI, 10)) if stepIDData.Version != "" { stepVersionForInfoPrint = stepVersionForInfoPrint + "@" + stepIDData.Version } } 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 { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if err := cmdex.GitCloneTagOrBranch(stepIDData.IDorURI, stepDir, stepIDData.Version); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepVersionForInfoPrint = "_" if stepIDData.Version != "" { if len(stepIDData.Version) > 20 { stepVersionForInfoPrint = stepVersionForInfoPrint + "@..." + stringutil.MaxLastChars(stepIDData.Version, 17) } else { stepVersionForInfoPrint = stepVersionForInfoPrint + "@" + stepIDData.Version } } } 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 := bitrise.StepmanSetup(stepIDData.SteplibSource); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } stepInfo, err := bitrise.StepmanStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { if buildRunResults.IsStepLibUpdated(stepIDData.SteplibSource) { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } // May StepLib should be updated log.Info("Step info not found in StepLib (%s) -- Updating ...") if err := bitrise.StepmanUpdate(stepIDData.SteplibSource); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } buildRunResults.StepmanUpdates[stepIDData.SteplibSource]++ stepInfo, err = bitrise.StepmanStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } } stepVersionForInfoPrint = stepInfo.StepVersion if err := bitrise.StepmanActivate(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version, stepDir, stepYMLPth); err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } else { log.Debugf("[BITRISE_CLI] - Step activated: (ID:%s) (version:%s)", stepIDData.IDorURI, stepIDData.Version) } } else { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, fmt.Errorf("Invalid stepIDData: No SteplibSource or LocalPath defined (%v)", stepIDData), isLastStep) continue } mergedStep := workflowStep if stepYMLPth != "" { specStep, err := bitrise.ReadSpecStep(stepYMLPth) log.Debugf("Spec read from YML: %#v\n", specStep) if err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } mergedStep, err = models.MergeStepWith(specStep, workflowStep) if err != nil { registerStepListItemRunResults(stepListItm, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } } // Run step bitrise.PrintRunningStep(*mergedStep.Title, stepVersionForInfoPrint, idx) if mergedStep.RunIf != nil && *mergedStep.RunIf != "" { isRun, err := bitrise.EvaluateStepTemplateToBool(*mergedStep.RunIf, buildRunResults) if err != nil { registerStepRunResults(mergedStep, models.StepRunStatusCodeFailed, 1, err, isLastStep) continue } if !isRun { registerStepRunResults(mergedStep, models.StepRunStatusCodeSkippedWithRunIf, 0, err, isLastStep) continue } } outEnvironments := []envmanModels.EnvironmentItemModel{} 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, models.StepRunStatusCodeSkipped, 0, err, isLastStep) } else { exit, out, err := runStep(mergedStep, stepIDData, stepDir, *environments) outEnvironments = out if err != nil { if *mergedStep.IsSkippable { registerStepRunResults(mergedStep, models.StepRunStatusCodeFailedSkippable, exit, err, isLastStep) } else { registerStepRunResults(mergedStep, models.StepRunStatusCodeFailed, exit, err, isLastStep) } } else { registerStepRunResults(mergedStep, models.StepRunStatusCodeSuccess, 0, nil, isLastStep) *environments = append(*environments, outEnvironments...) } } } return buildRunResults }