Example #1
0
func (release *nextRelease) Start() (action.Action, error) {
	// In case there are no additional stories, we are done.
	if len(release.additionalStories) == 0 {
		return action.Noop, nil
	}

	// Add release labels to the relevant stories.
	var (
		config    = release.tracker.config
		client    = pivotal.NewClient(config.UserToken())
		projectId = config.ProjectId()
	)

	task := "Label the stories with the release label"
	log.Run(task)
	releaseLabel := getReleaseLabel(release.trunkVersion)
	stories, err := addLabel(client, projectId,
		release.additionalStories, releaseLabel)
	if err != nil {
		return nil, errs.NewError(task, err)
	}
	release.additionalStories = nil

	// Return the rollback action, which removes the release labels that were appended.
	return action.ActionFunc(func() error {
		log.Rollback(task)
		_, err := removeLabel(client, projectId, stories, releaseLabel)
		if err != nil {
			return errs.NewError("Remove the release label from the stories", err)
		}
		return nil
	}), nil
}
Example #2
0
func fetchMe() (*pivotal.Me, error) {
	config, err := LoadConfig()
	if err != nil {
		return nil, err
	}

	client := pivotal.NewClient(config.UserToken())
	me, _, err := client.Me.Get()
	return me, err
}
Example #3
0
func (tracker *issueTracker) searchStories(
	queryFormat string,
	v ...interface{},
) ([]*pivotal.Story, error) {

	var (
		client    = pivotal.NewClient(tracker.config.UserToken())
		projectId = tracker.config.ProjectId()
	)
	return searchStories(client, projectId, queryFormat, v...)
}
Example #4
0
func (tracker *issueTracker) ListStoriesByTag(tags []string) ([]common.Story, error) {
	// Convert tags to ids.
	ids := make([]string, 0, len(tags))
	for _, tag := range tags {
		id, err := tracker.StoryTagToReadableStoryId(tag)
		if err != nil {
			return nil, err
		}
		ids = append(ids, id)
	}

	// Fetch the relevant stories.
	var (
		client    = pivotal.NewClient(tracker.config.UserToken())
		projectId = tracker.config.ProjectId()
	)
	stories, err := listStoriesByIdOrdered(client, projectId, ids)
	if err != nil {
		return nil, err
	}

	// Convert to []common.Story and return.
	return toCommonStories(stories, tracker), nil
}
Example #5
0
func (release *runningRelease) Stage() (action.Action, error) {
	stageTask := "Mark the stories as delivered in Pivotal Tracker"
	log.Run(stageTask)

	// Load the assigned stories.
	stories, err := release.loadStories()
	if err != nil {
		return nil, errs.NewError(stageTask, err)
	}

	// Pick only the stories that are finished.
	// All other stories are delivered or further.
	// That is checked in EnsureStageable().
	ss := make([]*pivotal.Story, 0, len(stories))
	for _, s := range stories {
		if s.State == pivotal.StoryStateFinished {
			ss = append(ss, s)
		}
	}
	stories = ss

	// Save the original states into a map.
	originalStates := make(map[int]string, len(stories))
	for _, story := range stories {
		originalStates[story.Id] = story.State
	}

	// Set all the states to Delivered.
	updateRequest := &pivotal.StoryRequest{State: pivotal.StoryStateDelivered}
	updateFunc := func(story *pivotal.Story) *pivotal.StoryRequest {
		return updateRequest
	}
	// On rollback, get the original state from the map.
	rollbackFunc := func(story *pivotal.Story) *pivotal.StoryRequest {
		return &pivotal.StoryRequest{State: originalStates[story.Id]}
	}

	// Update the stories.
	var (
		config    = release.tracker.config
		client    = pivotal.NewClient(config.UserToken())
		projectId = config.ProjectId()
	)
	updatedStories, err := updateStories(client, projectId, stories, updateFunc, rollbackFunc)
	if err != nil {
		return nil, errs.NewError(stageTask, err)
	}
	release.stories = updatedStories

	// Return the rollback function.
	return action.ActionFunc(func() error {
		// On error, set the states back to the original ones.
		log.Rollback(stageTask)
		task := "Reset the story states back to the original ones"
		updatedStories, err := updateStories(client, projectId, release.stories, rollbackFunc, nil)
		if err != nil {
			return errs.NewError(task, err)
		}
		release.stories = updatedStories
		return nil
	}), nil
}
Example #6
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
}