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 stepmanModels.StepInfoModel var stepIdxPtr int // ------------------------------------------ // In function method - Registration methods, for register step run results. registerStepRunResults := func(runIf string, resultCode, exitCode int, err error, isLastStep, printStepHeader bool) { if printStepHeader { bitrise.PrintRunningStepHeader(stepInfoPtr, stepIdxPtr) } stepInfoCopy := stepmanModels.StepInfoModel{ ID: stepInfoPtr.ID, Title: stepInfoPtr.Title, Version: stepInfoPtr.Version, Latest: stepInfoPtr.Latest, SupportURL: stepInfoPtr.SupportURL, SourceCodeURL: stepInfoPtr.SourceCodeURL, } 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.Title, err) buildRunResults.FailedSteps = append(buildRunResults.FailedSteps, stepResults) break case models.StepRunStatusCodeFailedSkippable: log.Warnf("Step (%s) failed, but was marked as skippable, error: (%v)", stepInfoCopy.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", 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } // // Preparing the step if err := bitrise.EnvmanInitAtPath(bitrise.InputEnvstorePath); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := bitrise.ExportEnvironmentsList(*environments); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } // Get step id & version data compositeStepIDStr, workflowStep, err := models.GetStepIDStepDataPair(stepListItm) if err != nil { registerStepRunResults("", 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("", 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 := 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 { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } log.Debugln("stepAbsLocalPth:", stepAbsLocalPth, "|stepDir:", stepDir) if err := cmdex.CopyDir(stepAbsLocalPth, stepDir, true); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := cmdex.CopyFile(path.Join(stepAbsLocalPth, "step.yml"), stepYMLPth); err != nil { registerStepRunResults("", 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 { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := cmdex.CopyFile(path.Join(stepDir, "step.yml"), stepYMLPth); err != nil { registerStepRunResults("", 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } if err := cmdex.GitCloneTagOrBranch(stepIDData.IDorURI, stepDir, stepIDData.Version); err != nil { registerStepRunResults("", 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 := bitrise.StepmanSetup(stepIDData.SteplibSource); err != nil { registerStepRunResults("", 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 := bitrise.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 := bitrise.StepmanJSONStepLibStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { if buildRunResults.IsStepLibUpdated(stepIDData.SteplibSource) { registerStepRunResults("", 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 := bitrise.StepmanUpdate(stepIDData.SteplibSource); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } buildRunResults.StepmanUpdates[stepIDData.SteplibSource]++ outStr, err = bitrise.StepmanJSONStepLibStepInfo(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version) if err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, fmt.Errorf("StepmanJSONStepLibStepInfo failed, err: %s", err), isLastStep, true) continue } } stepInfo, err := stepmanModels.StepInfoModel{}.CreateFromJSON(outStr) if err != nil { registerStepRunResults("", 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 if err := bitrise.StepmanActivate(stepIDData.SteplibSource, stepIDData.IDorURI, stepIDData.Version, stepDir, stepYMLPth); err != nil { registerStepRunResults("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) 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, 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("", models.StepRunStatusCodeFailed, 1, err, isLastStep, true) continue } mergedStep, err = models.MergeStepWith(specStep, workflowStep) if err != nil { registerStepRunResults("", 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, idx) if mergedStep.RunIf != nil && *mergedStep.RunIf != "" { outStr, err := bitrise.EnvmanJSONPrint(bitrise.InputEnvstorePath) if err != nil { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeFailed, 1, fmt.Errorf("EnvmanJSONPrint failed, err: %s", err), isLastStep, false) continue } envList, err := envmanModels.EnvsJSONListModel{}.CreateFromJSON(outStr) if err != nil { registerStepRunResults(*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.RunIf, models.StepRunStatusCodeFailed, 1, err, isLastStep, false) continue } if !isRun { registerStepRunResults(*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.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.RunIf, models.StepRunStatusCodeFailedSkippable, exit, err, isLastStep, false) } else { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeFailed, exit, err, isLastStep, false) } } else { registerStepRunResults(*mergedStep.RunIf, models.StepRunStatusCodeSuccess, 0, nil, isLastStep, false) } } } return buildRunResults }
func runStep(step stepmanModels.StepModel, stepIDData models.StepIDData, stepDir string, environments []envmanModels.EnvironmentItemModel) (int, []envmanModels.EnvironmentItemModel, error) { log.Debugf("[BITRISE_CLI] - Try running step: %s (%s)", stepIDData.IDorURI, stepIDData.Version) // Check dependencies for _, dep := range step.Dependencies { isSkippedBecauseOfPlatform := false switch dep.Manager { case depManagerBrew: if runtime.GOOS == "darwin" { err := bitrise.InstallWithBrewIfNeeded(dep.Name, IsCIMode) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } } else { isSkippedBecauseOfPlatform = true } break case depManagerTryCheck: err := bitrise.DependencyTryCheckTool(dep.Name) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } break default: return 1, []envmanModels.EnvironmentItemModel{}, errors.New("Not supported dependency (" + dep.Manager + ") (" + dep.Name + ")") } if isSkippedBecauseOfPlatform { log.Debugf(" * Dependency (%s) skipped, manager (%s) not supported on this platform (%s)", dep.Name, dep.Manager, runtime.GOOS) } else { log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", dep.Name) } } // Collect step inputs environments = append(environments, step.Inputs...) // Cleanup envstore if err := bitrise.EnvmanInitAtPath(bitrise.InputEnvstorePath); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if err := bitrise.ExportEnvironmentsList(environments); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } // Run step stepCmd := path.Join(stepDir, "step.sh") log.Debug("OUTPUT:") cmd := []string{"bash", stepCmd} if exit, err := bitrise.EnvmanRun(bitrise.InputEnvstorePath, bitrise.CurrentDir, cmd, "panic"); err != nil { return exit, []envmanModels.EnvironmentItemModel{}, err } stepOutputs, err := bitrise.CollectEnvironmentsFromFile(bitrise.OutputEnvstorePath) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } log.Debugf("[BITRISE_CLI] - Step executed: %s (%s)", stepIDData.IDorURI, stepIDData.Version) return 0, stepOutputs, nil }
func runStep(step stepmanModels.StepModel, stepIDData models.StepIDData, stepDir string, environments []envmanModels.EnvironmentItemModel, buildRunResults models.BuildRunResultsModel) (int, []envmanModels.EnvironmentItemModel, error) { log.Debugf("[BITRISE_CLI] - Try running step: %s (%s)", stepIDData.IDorURI, stepIDData.Version) // Check & Install Step Dependencies if len(step.Dependencies) > 0 { log.Warnf("step.dependencies is deprecated... Use step.deps instead.") } if len(step.Deps.Brew) > 0 || len(step.Deps.AptGet) > 0 || len(step.Deps.CheckOnly) > 0 { // // New dependency handling for _, checkOnlyDep := range step.Deps.CheckOnly { if err := bitrise.DependencyTryCheckTool(checkOnlyDep.Name); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", checkOnlyDep.Name) } switch runtime.GOOS { case "darwin": for _, brewDep := range step.Deps.Brew { if err := bitrise.InstallWithBrewIfNeeded(brewDep.Name, configs.IsCIMode); err != nil { log.Infof("Failed to install (%s) with brew", brewDep.Name) return 1, []envmanModels.EnvironmentItemModel{}, err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", brewDep.Name) } case "linux": for _, aptGetDep := range step.Deps.AptGet { log.Infof("Start installing (%s) with apt-get", aptGetDep.Name) if err := bitrise.InstallWithAptGetIfNeeded(aptGetDep.Name, configs.IsCIMode); err != nil { log.Infof("Failed to install (%s) with apt-get", aptGetDep.Name) return 1, []envmanModels.EnvironmentItemModel{}, err } log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", aptGetDep.Name) } default: return 1, []envmanModels.EnvironmentItemModel{}, errors.New("Unsupported os") } } else if len(step.Dependencies) > 0 { log.Info("Deprecated dependencies found") // // Deprecated dependency handling for _, dep := range step.Dependencies { isSkippedBecauseOfPlatform := false switch dep.Manager { case depManagerBrew: if runtime.GOOS == "darwin" { err := bitrise.InstallWithBrewIfNeeded(dep.Name, configs.IsCIMode) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } } else { isSkippedBecauseOfPlatform = true } break case depManagerTryCheck: err := bitrise.DependencyTryCheckTool(dep.Name) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } break default: return 1, []envmanModels.EnvironmentItemModel{}, errors.New("Not supported dependency (" + dep.Manager + ") (" + dep.Name + ")") } if isSkippedBecauseOfPlatform { log.Debugf(" * Dependency (%s) skipped, manager (%s) not supported on this platform (%s)", dep.Name, dep.Manager, runtime.GOOS) } else { log.Infof(" * "+colorstring.Green("[OK]")+" Step dependency (%s) installed, available.", dep.Name) } } } // Collect step inputs if err := bitrise.EnvmanInitAtPath(bitrise.InputEnvstorePath); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if err := bitrise.ExportEnvironmentsList(environments); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } evaluatedInputs := []envmanModels.EnvironmentItemModel{} for _, input := range step.Inputs { key, value, err := input.GetKeyValuePair() if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } options, err := input.GetOptions() if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if options.IsTemplate != nil && *options.IsTemplate { outStr, err := bitrise.EnvmanJSONPrint(bitrise.InputEnvstorePath) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, fmt.Errorf("EnvmanJSONPrint failed, err: %s", err) } envList, err := envmanModels.EnvsJSONListModel{}.CreateFromJSON(outStr) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, fmt.Errorf("CreateFromJSON failed, err: %s", err) } evaluatedValue, err := bitrise.EvaluateTemplateToString(value, configs.IsCIMode, configs.IsPullRequestMode, buildRunResults, envList) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } input[key] = evaluatedValue } evaluatedInputs = append(evaluatedInputs, input) } environments = append(environments, evaluatedInputs...) if err := bitrise.EnvmanInitAtPath(bitrise.InputEnvstorePath); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if err := bitrise.ExportEnvironmentsList(environments); err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } // Run step stepCmd := path.Join(stepDir, "step.sh") cmd := []string{"bash", stepCmd} bitriseSourceDir, err := getCurrentBitriseSourceDir(environments) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } if bitriseSourceDir == "" { bitriseSourceDir = bitrise.CurrentDir } if exit, err := bitrise.EnvmanRun(bitrise.InputEnvstorePath, bitriseSourceDir, cmd); err != nil { stepOutputs, envErr := bitrise.CollectEnvironmentsFromFile(bitrise.OutputEnvstorePath) if envErr != nil { return 1, []envmanModels.EnvironmentItemModel{}, envErr } return exit, stepOutputs, err } stepOutputs, err := bitrise.CollectEnvironmentsFromFile(bitrise.OutputEnvstorePath) if err != nil { return 1, []envmanModels.EnvironmentItemModel{}, err } log.Debugf("[BITRISE_CLI] - Step executed: %s (%s)", stepIDData.IDorURI, stepIDData.Version) return 0, stepOutputs, nil }