Example #1
0
func (release *nextRelease) PromptUserToConfirmStart() (bool, error) {
	// Collect the issues to be added to the current release.
	task := "Collect the issues that modified trunk since the last release"
	log.Run(task)
	ids, err := releases.ListStoryIdsToBeAssigned(release.tracker)
	if err != nil {
		return false, errs.NewError(task, err)
	}

	// Fetch the additional issues from JIRA.
	task = "Fetch the collected issues from JIRA"
	log.Run(task)
	issues, err := listStoriesById(newClient(release.tracker.config), ids)
	if len(issues) == 0 && err != nil {
		return false, errs.NewError(task, err)
	}
	if len(issues) != len(ids) {
		log.Warn("Some issues were dropped since they were not found in JIRA")
	}

	// Drop the issues that were already assigned to the right version.
	releaseLabel := release.trunkVersion.ReleaseTagString()
	filteredIssues := make([]*jira.Issue, 0, len(issues))
IssueLoop:
	for _, issue := range issues {
		// Add only the parent tasks, i.e. skip sub-tasks.
		if issue.Fields.Parent != nil {
			continue
		}
		// Add only the issues that have not been assigned to the release yet.
		for _, label := range issue.Fields.Labels {
			if label == releaseLabel {
				continue IssueLoop
			}
		}
		filteredIssues = append(filteredIssues, issue)
	}
	issues = filteredIssues

	// Present the issues to the user.
	if len(issues) != 0 {
		fmt.Println("\nThe following issues are going to be added to the release:\n")
		err := prompt.ListStories(toCommonStories(issues, release.tracker), os.Stdout)
		if err != nil {
			return false, err
		}
	}

	// Ask the user to confirm.
	ok, err := prompt.Confirm(
		fmt.Sprintf(
			"\nAre you sure you want to start release %v?",
			release.trunkVersion.BaseString()))
	if err == nil {
		release.additionalIssues = issues
	}
	return ok, err
}
Example #2
0
func (release *nextRelease) PromptUserToConfirmStart() (bool, error) {
	var (
		config       = release.tracker.config
		client       = pivotal.NewClient(config.UserToken())
		releaseLabel = getReleaseLabel(release.trunkVersion)
	)

	// Collect the commits that modified trunk since the last release.
	task := "Collect the stories that modified trunk"
	log.Run(task)
	ids, err := releases.ListStoryIdsToBeAssigned(release.tracker)
	if err != nil {
		return false, errs.NewError(task, err)
	}

	// Fetch the collected stories from Pivotal Tracker, if necessary.
	var additional []*pivotal.Story
	if len(ids) != 0 {
		task = "Fetch the collected stories from Pivotal Tracker"
		log.Run(task)

		var err error
		additional, err = listStoriesById(client, config.ProjectId(), ids)
		if len(additional) == 0 && err != nil {
			return false, errs.NewError(task, err)
		}
		if len(additional) != len(ids) {
			log.Warn("Some stories were dropped since they were not found in PT")
		}

		// Drop the issues that are already assigned to the right release.
		unassignedStories := make([]*pivotal.Story, 0, len(additional))
		for _, story := range additional {
			if labeled(story, releaseLabel) {
				continue
			}
			unassignedStories = append(unassignedStories, story)
		}
		additional = unassignedStories
	}

	// Check the Point Me label.
	task = "Make sure there are no unpointed stories"
	log.Run(task)
	pmLabel := config.PointMeLabel()

	// Fetch the already assigned but unpointed stories.
	pmStories, err := searchStories(client, config.ProjectId(),
		"label:\"%v\" AND label:\"%v\"", releaseLabel, pmLabel)
	if err != nil {
		return false, errs.NewError(task, err)
	}
	// Also add these that are to be added but are unpointed.
	for _, story := range additional {
		if labeled(story, pmLabel) {
			pmStories = append(pmStories, story)
		}
	}
	// In case there are some unpointed stories, stop the release.
	if len(pmStories) != 0 {
		fmt.Println("\nThe following stories are still yet to be pointed:\n")
		err := prompt.ListStories(toCommonStories(pmStories, release.tracker), os.Stdout)
		if err != nil {
			return false, err
		}
		fmt.Println()
		return false, errs.NewError(task, errors.New("unpointed stories detected"))
	}

	// Print the stories to be added to the release.
	if len(additional) != 0 {
		fmt.Println("\nThe following stories are going to be added to the release:\n")
		err := prompt.ListStories(toCommonStories(additional, release.tracker), os.Stdout)
		if err != nil {
			return false, err
		}
	}

	// Ask the user to confirm.
	ok, err := prompt.Confirm(
		fmt.Sprintf(
			"\nAre you sure you want to start release %v?",
			release.trunkVersion.BaseString()))
	if err == nil {
		release.additionalStories = additional
	}
	return ok, err
}
Example #3
0
func (release *nextRelease) PromptUserToConfirmStart() (bool, error) {
	var (
		client         = release.client
		productId      = release.tracker.config.ProductId()
		itemReleaseTag = getItemReleaseTag(release.trunkVersion)
	)

	// Collect the commits that modified trunk since the last release.
	task := "Collect the stories that modified trunk"
	log.Run(task)
	ids, err := releases.ListStoryIdsToBeAssigned(release.tracker)
	if err != nil {
		return false, errs.NewError(task, err)
	}

	// Get the story ids associated with these commits.
	numbers := make([]int, 0, len(ids))
	for _, id := range ids {
		number, err := strconv.Atoi(id)
		if err != nil {
			return false, errs.NewError(task, fmt.Errorf("invalid item number: %v", id), nil)
		}
		numbers = append(numbers, number)
	}

	// Fetch the collected items from Sprintly, if necessary.
	var additional []sprintly.Item
	if len(numbers) != 0 {
		var err error
		// listItemsByNumber lists children as well, so there is no way we can miss a sub-item.
		additional, err = listItemsByNumber(client, productId, numbers)
		if err != nil {
			return false, err
		}

		// Drop the issues that are already assigned to the right release.
		unassignedItems := make([]sprintly.Item, 0, len(additional))
		for _, item := range additional {
			if tagged(&item, itemReleaseTag) {
				continue
			}
			unassignedItems = append(unassignedItems, item)
		}
		additional = unassignedItems
	}

	// Make sure there are no unrated items.
	task = "Make sure there are no unrated items"
	log.Run(task)

	// Check the additional items and collect the unrated ones.
	unrated := make([]sprintly.Item, 0)
	for _, item := range additional {
		if item.Score == sprintly.ItemScoreUnset {
			unrated = append(unrated, item)
		}
	}

	// Fetch the items that were assigned manually.
	assignedItems, err := listItemsByTag(client, productId, []string{itemReleaseTag})
	if err != nil {
		return false, errs.NewError(task, err)
	}

	// Check the manually assigned items and collect the unrated ones.
	for _, item := range assignedItems {
		if item.Score == sprintly.ItemScoreUnset {
			unrated = append(unrated, item)
		}

		// Also, since the sub-items of the assigned items are returned as well,
		// they may be missing the release tag, so let's register them to be tagged.
		if !tagged(&item, itemReleaseTag) {
			additional = append(additional, item)
		}
	}

	// In case there are some unrated items, abort the release process.
	if len(unrated) != 0 {
		fmt.Println("\nThe following items have not been rated yet:\n")
		err := prompt.ListStories(toCommonStories(unrated), os.Stdout)
		if err != nil {
			return false, err
		}
		fmt.Println()
		return false, errs.NewError(task, errors.New("unrated items detected"), nil)
	}

	// Print the items to be added to the release.
	if len(additional) != 0 {
		fmt.Println("\nThe following items are going to be added to the release:\n")
		err := prompt.ListStories(toCommonStories(additional), os.Stdout)
		if err != nil {
			return false, err
		}
	}

	// Ask the user to confirm.
	ok, err := prompt.Confirm(
		fmt.Sprintf("\nAre you sure you want to start release %v?", release.trunkVersion))
	if err == nil {
		release.additionalItems = additional
	}
	return ok, err
}