Example #1
0
func (story *story) SetAssignees(users []common.User) error {
	task := fmt.Sprintf("Set owners for story %v", story.Story.Id)

	ownerIds := make([]int, len(users))
	for i, user := range users {
		id, err := strconv.Atoi(user.Id())
		if err != nil {
			return errs.NewError(task, err)
		}
		ownerIds[i] = id
	}

	var (
		config    = story.tracker.config
		client    = pivotal.NewClient(config.UserToken)
		projectId = config.ProjectId
	)
	updateRequest := &pivotal.StoryRequest{OwnerIds: &ownerIds}
	updatedStory, _, err := client.Stories.Update(projectId, story.Story.Id, updateRequest)
	if err != nil {
		return errs.NewError(task, err)
	}
	story.Story = updatedStory
	return nil
}
Example #2
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 #3
0
func (tracker *issueTracker) searchStories(
	queryFormat string,
	v ...interface{},
) ([]*pivotal.Story, error) {

	// Get the client.
	var (
		client    = pivotal.NewClient(tracker.config.UserToken)
		projectId = tracker.config.ProjectId
	)

	// Generate the query.
	query := fmt.Sprintf(queryFormat, v...)

	// Automatically limit the story type.
	query = fmt.Sprintf("(type:%v OR type:%v) AND (%v)",
		pivotal.StoryTypeFeature, pivotal.StoryTypeBug, query)

	// Send the query to PT.
	stories, err := client.Stories.List(projectId, query)

	// Filter stories by the component label when necessary.
	if label := tracker.config.ComponentLabel; label != "" {
		stories = filterStories(stories, func(story *pivotal.Story) bool {
			return labeled(story, label)
		})
	}

	return stories, err
}
Example #4
0
func (tracker *issueTracker) CurrentUser() (common.User, error) {
	client := pivotal.NewClient(tracker.config.UserToken)
	me, _, err := client.Me.Get()
	if err != nil {
		return nil, err
	}
	return &user{me}, nil
}
Example #5
0
func (tracker *issueTracker) updateStories(
	stories []*pivotal.Story,
	updateFunc storyUpdateFunc,
	rollbackFunc storyUpdateFunc,
) ([]*pivotal.Story, error) {
	var (
		client    = pivotal.NewClient(tracker.config.UserToken)
		projectId = tracker.config.ProjectId
	)
	return updateStories(client, projectId, stories, updateFunc, rollbackFunc)
}
Example #6
0
func (story *story) Start() error {
	task := fmt.Sprintf("Start Pivotal Tracker story %v", story.Story.Id)

	if s := story.Story; s.Type == pivotal.StoryTypeFeature && s.Estimate == nil {
		panic(errors.New("story not estimated"))
	}

	var (
		config    = story.tracker.config
		client    = pivotal.NewClient(config.UserToken)
		projectId = config.ProjectId
	)
	updateRequest := &pivotal.StoryRequest{State: pivotal.StoryStateStarted}
	updatedStory, _, err := client.Stories.Update(projectId, story.Story.Id, updateRequest)
	if err != nil {
		return errs.NewError(task, err)
	}
	story.Story = updatedStory
	return nil
}
Example #7
0
func (story *story) MarkAsImplemented() (action.Action, error) {
	// Make sure the story is started.
	switch story.Story.State {
	case pivotal.StoryStateStarted:
		// Continue further to set the state to finished.
	case pivotal.StoryStateFinished:
		// Nothing to do here.
		return nil, nil
	default:
		// Foobar, an unexpected story state encountered.
		return nil, fmt.Errorf("unexpected story state: %v", story.State)
	}

	// Set the story state to finished.
	var (
		config    = story.tracker.config
		client    = pivotal.NewClient(config.UserToken)
		projectId = config.ProjectId
	)

	updateTask := fmt.Sprintf("Update Pivotal Tracker story (id = %v)", story.Story.Id)
	updateRequest := &pivotal.StoryRequest{State: pivotal.StoryStateFinished}
	updatedStory, _, err := client.Stories.Update(projectId, story.Story.Id, updateRequest)
	if err != nil {
		return nil, errs.NewError(updateTask, err)
	}
	originalStory := story.Story
	story.Story = updatedStory

	return action.ActionFunc(func() error {
		log.Rollback(updateTask)
		updateRequest := &pivotal.StoryRequest{State: originalStory.State}
		updatedStory, _, err := client.Stories.Update(projectId, story.Story.Id, updateRequest)
		if err != nil {
			return err
		}
		story.Story = updatedStory
		return nil
	}), nil
}
Example #8
0
// PromptUserForConfig is a part of loader.ConfigContainer interface.
func (local *LocalConfig) PromptUserForConfig() error {
	c := LocalConfig{spec: local.spec}

	// Prompt for the project ID.
	task := "Fetch available Pivotal Tracker projects"
	log.Run(task)

	client := pivotal.NewClient(local.spec.global.UserToken)

	projects, _, err := client.Projects.List()
	if err != nil {
		return errs.NewError(task, err)
	}
	sort.Sort(ptProjects(projects))

	fmt.Println()
	fmt.Println("Available Pivotal Tracker projects:")
	fmt.Println()
	for i, project := range projects {
		fmt.Printf("  [%v] %v\n", i+1, project.Name)
	}
	fmt.Println()
	fmt.Println("Choose the project to associate this repository with.")
	index, err := prompt.PromptIndex("Project number: ", 1, len(projects))
	if err != nil {
		if err == prompt.ErrCanceled {
			prompt.PanicCancel()
		}

		return err
	}
	fmt.Println()

	c.ProjectId = projects[index-1].Id

	// Prompt for the labels.
	promptForLabel := func(dst *string, labelName, defaultValue string) {
		if err != nil {
			return
		}
		question := fmt.Sprintf("%v label", labelName)
		var label string
		label, err = prompt.PromptDefault(question, defaultValue)
		if err == nil {
			*dst = label
		}
	}

	var componentLabel string
	promptForLabel(&componentLabel, "Component", "")
	c.ComponentLabel = &componentLabel

	promptForLabel(&c.Labels.PointMeLabel, "Point me", DefaultPointMeLabel)
	promptForLabel(&c.Labels.ReviewedLabel, "Reviewed", DefaultReviewedLabel)
	promptForLabel(&c.Labels.SkipReviewLabel, "Skip review", DefaultSkipReviewLabel)
	promptForLabel(&c.Labels.TestedLabel, "Testing passed", DefaultTestedLabel)
	promptForLabel(&c.Labels.SkipTestingLabel, "Skip testing", DefaultSkipTestingLabel)
	if err != nil {
		return err
	}

	// Prompt for the release skip check labels.
	skipCheckLabelsString, err := prompt.Prompt(fmt.Sprintf(
		"Skip check labels, comma-separated (%v always included): ",
		strings.Join(DefaultSkipCheckLabels, ", ")))
	if err != nil {
		if err != prompt.ErrCanceled {
			return err
		}
	}

	// Append the new labels to the default ones.
	// Make sure there are no duplicates and empty strings.
	var (
		insertedLabels = strings.Split(skipCheckLabelsString, ",")
		lenDefault     = len(DefaultSkipCheckLabels)
		lenInserted    = len(insertedLabels)
	)

	// Save a few allocations.
	skipCheckLabels := make([]string, lenDefault, lenDefault+lenInserted)
	copy(skipCheckLabels, DefaultSkipCheckLabels)

LabelLoop:
	for _, insertedLabel := range insertedLabels {
		// Trim spaces.
		insertedLabel = strings.TrimSpace(insertedLabel)

		// Skip empty strings.
		if insertedLabel == "" {
			continue
		}

		// Make sure there are no duplicates.
		for _, existingLabel := range skipCheckLabels {
			if insertedLabel == existingLabel {
				continue LabelLoop
			}
		}

		// Append the label.
		skipCheckLabels = append(skipCheckLabels, insertedLabel)
	}
	c.Labels.SkipCheckLabels = skipCheckLabels

	// Success!
	*local = c
	return nil
}