Example #1
0
func ensureTargetBranchExists(branch, remote string) error {
	task := fmt.Sprintf("Check whether branch '%v' exists", branch)

	// In case the branch exists locally, we are done.
	localExists, err := git.LocalBranchExists(branch)
	if err != nil {
		return errs.NewError(task, err)
	}
	if localExists {
		return nil
	}

	// Otherwise try to create a tracking branch.
	remoteExists, err := git.RemoteBranchExists(branch, remote)
	if err != nil {
		return errs.NewError(task, err)
	}
	if remoteExists {
		return errs.Wrap(task, git.CreateTrackingBranch(branch, remote))
	}

	// We have failed!
	return &git.ErrRefNotFound{branch}
}
Example #2
0
func SetForBranch(ver *Version, branch string) (act action.Action, err error) {
	var mainTask = fmt.Sprintf("Bump version to %v for branch '%v'", ver, branch)

	// Make sure the repository is clean (don't check untracked files).
	task := "Make sure the repository is clean"
	if err := git.EnsureCleanWorkingTree(false); err != nil {
		return nil, errs.NewError(task, err)
	}

	// Remember the current branch.
	currentBranch, err := gitutil.CurrentBranch()
	if err != nil {
		return nil, err
	}

	// Remember the current position of the target branch.
	task = fmt.Sprintf("Remember the position of branch '%v'", branch)
	originalPosition, err := git.Hexsha("refs/heads/" + branch)
	if err != nil {
		return nil, errs.NewError(task, err)
	}

	// Checkout the target branch.
	task = fmt.Sprintf("Checkout branch '%v'", branch)
	if err := git.Checkout(branch); err != nil {
		return nil, errs.NewError(task, err)
	}
	defer func() {
		// Checkout the original branch on return.
		task := fmt.Sprintf("Checkout branch '%v'", currentBranch)
		if ex := git.Checkout(currentBranch); ex != nil {
			if err == nil {
				err = ex
			} else {
				errs.LogError(task, ex)
			}
		}
	}()

	// Set the project version to the desired value.
	if err := Set(ver); err != nil {
		if ex, ok := err.(*scripts.ErrNotFound); ok {
			return nil, fmt.Errorf(
				"custom SalsaFlow script '%v' not found on branch '%v'", ex.ScriptName(), branch)
		}
		return nil, err
	}

	// Commit changes.
	_, err = git.RunCommand("commit", "-a",
		"-m", fmt.Sprintf("Bump version to %v", ver),
		"-m", fmt.Sprintf("Story-Id: %v", git.StoryIdUnassignedTagValue))
	if err != nil {
		task := "Reset the working tree to the original state"
		if err := git.Reset("--hard"); err != nil {
			errs.LogError(task, err)
		}
		return nil, err
	}

	return action.ActionFunc(func() (err error) {
		// On rollback, reset the target branch to the original position.
		log.Rollback(mainTask)
		task := fmt.Sprintf("Reset branch '%v' to the original position", branch)

		// Get the current branch name.
		currentBranch, err := gitutil.CurrentBranch()
		if err != nil {
			return errs.NewError(task, err)
		}

		// Use SetBranch in case we are not on the branch to be modified.
		if branch != currentBranch {
			return errs.Wrap(task, git.SetBranch(branch, originalPosition))
		}

		// Otherwise use git reset --hard since currentBranch == branch.
		return errs.Wrap(task, git.Reset("--hard", originalPosition))
	}), nil
}
Example #3
0
func runMain() (err error) {
	tracker, err := modules.GetIssueTracker()
	if err != nil {
		return err
	}

	// Fetch stories from the issue tracker.
	task := "Fetch stories from the issue tracker"
	log.Run(task)
	stories, err := tracker.StartableStories()
	if err != nil {
		return errs.NewError(task, err)
	}
	if len(stories) == 0 {
		return errs.NewError(task, errors.New("no startable stories found"))
	}

	// Filter out the stories that are not relevant,
	// i.e. not owned by the current user or assigned to someone else.
	task = "Fetch the current user record from the issue tracker"
	user, err := tracker.CurrentUser()
	if err != nil {
		return errs.NewError(task, err)
	}

	var filteredStories []common.Story
StoryLoop:
	for _, story := range stories {
		assignees := story.Assignees()
		// Include the story in case there is no assignee set yet.
		if len(assignees) == 0 {
			filteredStories = append(filteredStories, story)
			continue StoryLoop
		}
		// Include the story in case the current user is assigned.
		for _, assignee := range assignees {
			if assignee.Id() == user.Id() {
				filteredStories = append(filteredStories, story)
				continue StoryLoop
			}
		}
	}
	stories = filteredStories

	// Prompt the user to select a story.
	story, err := dialog(
		"\nYou can start working on one of the following stories:", stories)
	if err != nil {
		switch err {
		case prompt.ErrNoStories:
			return errors.New("no startable stories found")
		case prompt.ErrCanceled:
			prompt.PanicCancel()
		default:
			return err
		}
	}
	fmt.Println()

	// Create the story branch, optionally.
	if flagNoBranch {
		log.Log("Not creating any feature branch")
	} else {
		var act action.Action
		act, err = createBranch()
		if err != nil {
			return err
		}
		// Roll back on error.
		defer action.RollbackTaskOnError(&err, task, act)
	}

	// Add the current user to the list of story assignees.
	task = "Amend the list of story assignees"
	log.Run(task)
	originalAssignees := story.Assignees()
	if err := story.AddAssignee(user); err != nil {
		return errs.NewError(task, err)
	}
	defer action.RollbackTaskOnError(&err, task, action.ActionFunc(func() error {
		task := "Reset the list of story assignees"
		if err := story.SetAssignees(originalAssignees); err != nil {
			return errs.NewError(task, err)
		}
		return nil
	}))

	// Start the selected story. No need to roll back.
	task = "Start the selected story"
	log.Run(task)
	return errs.Wrap(task, story.Start())
}