예제 #1
0
// FindProvProfilesByAppID ...
// `appID`` supports "glob", e.g.: *.bundle.id will match any Prov Profile with ".bundle.id"
//   app ID suffix
func FindProvProfilesByAppID(appID string) ([]ProvisioningProfileFileInfoModel, error) {
	absProvProfileDirPath, err := pathutil.AbsPath(provProfileSystemDirPath)
	if err != nil {
		return nil, fmt.Errorf("Failed to get Absolute path of Provisioning Profiles dir: %s", err)
	}

	pths, err := filepath.Glob(absProvProfileDirPath + "/*.mobileprovision")
	if err != nil {
		return nil, fmt.Errorf("Failed to perform *.mobileprovision search, error: %s", err)
	}

	provProfilePathsToReturn := []ProvisioningProfileFileInfoModel{}
	for _, aPth := range pths {
		provProfileData, err := CreateProvisioningProfileModelFromFile(aPth)
		if err != nil {
			return nil, fmt.Errorf("Failed to read Provisioning Profile infos from file (path: %s), error: %s",
				aPth, err)
		}

		if glob.Glob(appID, provProfileData.Entitlements.AppID) {
			provProfilePathsToReturn = append(provProfilePathsToReturn, ProvisioningProfileFileInfoModel{
				Path: aPth,
				ProvisioningProfileInfo: provProfileData,
			})
		}
	}

	return provProfilePathsToReturn, nil
}
// NewModel ...
func NewModel(testCloudExexPth string) (*Model, error) {
	absTestCloudExexPth, err := pathutil.AbsPath(testCloudExexPth)
	if err != nil {
		return nil, fmt.Errorf("Failed to expand path (%s), error: %s", testCloudExexPth, err)
	}

	return &Model{testCloudExePth: absTestCloudExexPth}, nil
}
// New ...
func New(nunitConsolePth string) (*Model, error) {
	absNunitConsolePth, err := pathutil.AbsPath(nunitConsolePth)
	if err != nil {
		return nil, fmt.Errorf("Failed to expand path (%s), error: %s", nunitConsolePth, err)
	}

	return &Model{nunitConsolePth: absNunitConsolePth}, nil
}
// New ...
func New(solutionPth, projectPth string) (*Model, error) {
	absSolutionPth, err := pathutil.AbsPath(solutionPth)
	if err != nil {
		return nil, fmt.Errorf("Failed to expand path (%s), error: %s", solutionPth, err)
	}

	absProjectPth := ""
	if projectPth != "" {
		absPth, err := pathutil.AbsPath(projectPth)
		if err != nil {
			return nil, fmt.Errorf("Failed to expand path (%s), error: %s", projectPth, err)
		}
		absProjectPth = absPth
	}

	return &Model{solutionPth: absSolutionPth, projectPth: absProjectPth, buildTool: constants.XbuildPath}, nil
}
// New ...
func New(solutionPth string) (*Model, error) {
	absSolutionPth, err := pathutil.AbsPath(solutionPth)
	if err != nil {
		return nil, fmt.Errorf("Failed to expand path (%s), error: %s", solutionPth, err)
	}

	return &Model{solutionPth: absSolutionPth, buildTool: constants.MDToolPath}, nil
}
예제 #6
0
// NewHelper ...
func NewHelper(destinationDir, remoteURI string, resetRepository bool) (Helper, error) {
	if destinationDir == "" {
		return Helper{}, errors.New("destination dir path is empty")
	}

	if remoteURI == "" {
		return Helper{}, errors.New("remote URI is empty")
	}

	// Expand destination dir
	fullDestinationDir, err := pathutil.AbsPath(destinationDir)
	if err != nil {
		return Helper{}, err
	}

	helper := Helper{
		destinationDir: fullDestinationDir,
		remoteURI:      remoteURI,
	}

	// Check if .git exist
	gitDirPth := filepath.Join(fullDestinationDir, ".git")
	if exist, err := pathutil.IsDirExists(gitDirPth); err != nil {
		return Helper{}, err
	} else if exist {
		if remotes, err := helper.RemoteList(); err != nil {
			return Helper{}, err
		} else {
			if !strings.Contains(remotes, remoteURI) {
				return Helper{}, fmt.Errorf(".git folder already exists in the destination dir: %s, using a different remote", fullDestinationDir)
			} else {
				if resetRepository {
					if err = helper.Clean(); err != nil {
						return Helper{}, err
					}
				}
				helper.originPresent = true
			}
		}
	}

	// Create destination dir if not exist
	if exist, err := pathutil.IsDirExists(fullDestinationDir); err != nil {
		return Helper{}, err
	} else if !exist {
		if err := os.MkdirAll(fullDestinationDir, 0777); err != nil {
			return Helper{}, err
		}
	}

	return helper, nil
}
예제 #7
0
// PathDepth ...
func PathDepth(pth string) (int, error) {
	abs, err := pathutil.AbsPath(pth)
	if err != nil {
		return 0, err
	}
	comp := strings.Split(abs, string(os.PathSeparator))

	fixedComp := []string{}
	for _, c := range comp {
		if c != "" {
			fixedComp = append(fixedComp, c)
		}
	}

	return len(fixedComp), nil
}
예제 #8
0
// FindProvProfileByUUID ...
func FindProvProfileByUUID(provProfileUUID string) (ProvisioningProfileFileInfoModel, error) {
	absProvProfileDirPath, err := pathutil.AbsPath(provProfileSystemDirPath)
	if err != nil {
		return ProvisioningProfileFileInfoModel{}, fmt.Errorf("Failed to get Absolute path of Provisioning Profiles dir: %s", err)
	}

	// iOS / .mobileprovision
	{
		mobileProvPth := filepath.Join(absProvProfileDirPath, provProfileUUID+".mobileprovision")
		exist, err := pathutil.IsPathExists(mobileProvPth)
		if !exist || err != nil {
			log.Debugf("No mobileprovision file found at: %s | err: %s", mobileProvPth, err)
		} else {
			provProfileData, err := CreateProvisioningProfileModelFromFile(mobileProvPth)
			if err != nil {
				return ProvisioningProfileFileInfoModel{},
					fmt.Errorf("Failed to read Provisioning Profile infos from file (path: %s), error: %s",
						mobileProvPth, err)
			}
			return ProvisioningProfileFileInfoModel{
				Path: mobileProvPth,
				ProvisioningProfileInfo: provProfileData,
			}, nil
		}
	}

	// Mac / .provisionprofile
	{
		macProvProfPth := filepath.Join(absProvProfileDirPath, provProfileUUID+".provisionprofile")
		exist, err := pathutil.IsPathExists(macProvProfPth)
		if !exist || err != nil {
			log.Debugf("No provisionprofile file found at: %s | err: %s", macProvProfPth, err)
			return ProvisioningProfileFileInfoModel{}, fmt.Errorf("Failed to find Provisioning Profile with UUID: %s", provProfileUUID)
		}

		provProfileData, err := CreateProvisioningProfileModelFromFile(macProvProfPth)
		if err != nil {
			return ProvisioningProfileFileInfoModel{},
				fmt.Errorf("Failed to read Provisioning Profile infos from file (path: %s), error: %s",
					macProvProfPth, err)
		}
		return ProvisioningProfileFileInfoModel{
			Path: macProvProfPth,
			ProvisioningProfileInfo: provProfileData,
		}, nil
	}
}
예제 #9
0
func initExportOutputDir() (string, error) {
	absExportOutputDirPath, err := pathutil.AbsPath(confExportOutputDirPath)
	log.Debugf("absExportOutputDirPath: %s", absExportOutputDirPath)
	if err != nil {
		return absExportOutputDirPath, fmt.Errorf("Failed to determin Absolute path of export dir: %s", confExportOutputDirPath)
	}
	if exist, err := pathutil.IsDirExists(absExportOutputDirPath); err != nil {
		return absExportOutputDirPath, fmt.Errorf("Failed to determin whether the export directory already exists: %s", err)
	} else if !exist {
		if err := os.Mkdir(absExportOutputDirPath, 0777); err != nil {
			return absExportOutputDirPath, fmt.Errorf("Failed to create export output directory at path: %s | error: %s", absExportOutputDirPath, err)
		}
	} else {
		log.Infof("Export output dir already exists at path: %s", absExportOutputDirPath)
	}
	return absExportOutputDirPath, nil
}
예제 #10
0
파일: gows.go 프로젝트: bitrise-io/bitrise
// CreateGopathBinSymlink ...
func CreateGopathBinSymlink(origGOPATH string, workspaceRootPath string) error {
	fullWorkspaceBinPath := filepath.Join(workspaceRootPath, "bin")
	originalGopathBinPath, err := pathutil.AbsPath(filepath.Join(origGOPATH, "bin"))
	if err != nil {
		return fmt.Errorf("Failed to get the path of 'bin' dir inside your GOPATH (%s), error: %s", origGOPATH, err)
	}

	log.Debugf("=> Creating Symlink: (%s) -> (%s)", originalGopathBinPath, fullWorkspaceBinPath)

	// create symlink for GOPATH/bin, if not yet created
	if err := CreateOrUpdateSymlink(originalGopathBinPath, fullWorkspaceBinPath); err != nil {
		return fmt.Errorf("Failed to create GOPATH/bin symlink, error: %s", err)
	}

	log.Debugf(" [DONE] Symlink is in place")

	return nil
}
func analyzeSolution(pth string, analyzeProjects bool) (Model, error) {
	absPth, err := pathutil.AbsPath(pth)
	if err != nil {
		return Model{}, fmt.Errorf("Failed to expand path (%s), error: %s", pth, err)
	}

	fileName := filepath.Base(absPth)
	ext := filepath.Ext(absPth)
	fileName = strings.TrimSuffix(fileName, ext)

	solution := Model{
		Pth:        absPth,
		Name:       fileName,
		ConfigMap:  map[string]string{},
		ProjectMap: map[string]project.Model{},
	}

	isSolutionConfigurationPlatformsSection := false
	isProjectConfigurationPlatformsSection := false

	solutionDir := filepath.Dir(absPth)

	content, err := fileutil.ReadStringFromFile(absPth)
	if err != nil {
		return Model{}, fmt.Errorf("failed to read solution (%s), error: %s", absPth, err)
	}

	scanner := bufio.NewScanner(strings.NewReader(content))
	for scanner.Scan() {
		line := strings.TrimSpace(scanner.Text())

		// Projects
		if matches := regexp.MustCompile(solutionProjectsPattern).FindStringSubmatch(line); len(matches) == 5 {
			ID := strings.ToUpper(matches[1])
			projectName := matches[2]
			projectID := strings.ToUpper(matches[4])
			projectRelativePth := utility.FixWindowsPath(matches[3])
			projectPth := filepath.Join(solutionDir, projectRelativePth)

			if strings.HasSuffix(projectPth, constants.CSProjExt) ||
				strings.HasSuffix(projectPth, constants.SHProjExt) ||
				strings.HasSuffix(projectPth, constants.FSProjExt) {

				project := project.Model{
					ID:   projectID,
					Name: projectName,
					Pth:  projectPth,

					ConfigMap: map[string]string{},
					Configs:   map[string]project.ConfigurationPlatformModel{},
				}
				solution.ProjectMap[projectID] = project
			}

			solution.ID = ID

			continue
		}

		// GlobalSection(SolutionConfigurationPlatforms) = preSolution
		if isSolutionConfigurationPlatformsSection {
			if match := regexp.MustCompile(solutionConfigurationPlatformsSectionEndPattern).FindString(line); match != "" {
				isSolutionConfigurationPlatformsSection = false
				continue
			}
		}

		if match := regexp.MustCompile(solutionConfigurationPlatformsSectionStartPattern).FindString(line); match != "" {
			isSolutionConfigurationPlatformsSection = true
			continue
		}

		if isSolutionConfigurationPlatformsSection {
			if matches := regexp.MustCompile(solutionConfigurationPlatformPattern).FindStringSubmatch(line); len(matches) == 5 {
				configuration := matches[1]
				platform := matches[2]

				mappedConfiguration := matches[3]
				mappedPlatform := matches[4]

				solution.ConfigMap[utility.ToConfig(configuration, platform)] = utility.ToConfig(mappedConfiguration, mappedPlatform)

				continue
			}
		}

		// GlobalSection(ProjectConfigurationPlatforms) = postSolution
		if isProjectConfigurationPlatformsSection {
			if match := regexp.MustCompile(projectConfigurationPlatformsSectionEndPattern).FindString(line); match != "" {
				isProjectConfigurationPlatformsSection = false
				continue
			}
		}

		if match := regexp.MustCompile(projectConfigurationPlatformsSectionStartPattern).FindString(line); match != "" {
			isProjectConfigurationPlatformsSection = true
			continue
		}

		if isProjectConfigurationPlatformsSection {
			if matches := regexp.MustCompile(projectConfigurationPlatformPattern).FindStringSubmatch(line); len(matches) == 6 {
				projectID := strings.ToUpper(matches[1])
				solutionConfiguration := matches[2]
				solutionPlatform := matches[3]
				projectConfiguration := matches[4]
				projectPlatform := matches[5]
				if projectPlatform == "Any CPU" {
					projectPlatform = "AnyCPU"
				}

				project, found := solution.ProjectMap[projectID]
				if !found {
					return Model{}, fmt.Errorf("no project found with ID: %s", projectID)
				}

				project.ConfigMap[utility.ToConfig(solutionConfiguration, solutionPlatform)] = utility.ToConfig(projectConfiguration, projectPlatform)

				solution.ProjectMap[projectID] = project

				continue
			}
		}
	}
	if err := scanner.Err(); err != nil {
		return Model{}, err
	}

	if analyzeProjects {
		projectMap := map[string]project.Model{}

		for projectID, proj := range solution.ProjectMap {
			projectDefinition, err := project.New(proj.Pth)
			if err != nil {
				return Model{}, fmt.Errorf("failed to analyze project (%s), error: %s", proj.Pth, err)
			}

			projectDefinition.Name = proj.Name
			projectDefinition.Pth = proj.Pth
			projectDefinition.ConfigMap = proj.ConfigMap

			projectMap[projectID] = projectDefinition
		}

		solution.ProjectMap = projectMap
	}

	return solution, nil
}
예제 #12
0
파일: run_util.go 프로젝트: godrei/bitrise
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
}
예제 #13
0
파일: util.go 프로젝트: bitrise-io/bitrise
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
}
예제 #14
0
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
}
예제 #15
0
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)
}
예제 #16
0
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
}