ccschmitz
6/25/2013 - 6:45 PM

A Rake task that allows you to start/finish stories.

A Rake task that allows you to start/finish stories.

require "rubygems"
require "pivotal-tracker"

namespace :pt do
  # In order to use these tasks you'll need to install the pivotal tracker gem:
  #
  # gem install pivotal-tracker
  #
  # Then setup your Pivotal Tracker information in your git config file:
  #
  # [pivotal]
  #   token = abcdefghijklmnopqrstuvwxyz => probably want this in ~/.gitconfig
  #   projectid = 123456                 => your project's ID
  #   fullname = Al Johnson              => has to be your full name, no quotes

  token = `git config --get pivotal.token`
  projectid = `git config --get pivotal.projectid`

  PivotalTracker::Client.token = token
  PivotalTracker::Client.use_ssl = false
  pt_project = PivotalTracker::Project.find(projectid)

  desc "list available pivotal tracker stories from which to create a git feature branch"
  task :list do
    def truncate_words(text, length = 5)
      return if text == nil
      text = text.gsub(/[^0-9a-z _]/i, '')
      words = text.split()
      words = words - %w(feature scenario for in on a an the of so that they be able to are it its with) #remove non critical words (experiment with this)
      words[0..(length-1)].uniq.join(' ')
    end

    def gitflow_helper(story_type, branch_name, parent_branch)
      puts ""
      puts "Summary of actions:"
      puts "- A file located at doc/current_pt_story.txt was created with the details of this story."
      puts "    Reference that file easily with cat doc/current_pt_story.txt"
      puts "- A new branch '#{story_type}/#{branch_name}' was created, based on '#{parent_branch}'"
      puts "- You are now on branch '#{story_type}/#{branch_name}'"
      puts ""
      puts "Now, start committing on your #{story_type}. When done, use:"
      puts ""
      if %w(feature hotfix).include?(story_type)
        puts "     git flow #{story_type} finish #{branch_name}"
      else
        puts "     git checkout #{parent_branch}"
        puts "     git merge --no-ff #{branch_name}"
        puts "     git branch -d #{branch_name}"
      end
      puts ""
    end

    conditions = {
      state: ['rejected', 'started', 'unstarted'],
      story_type: ['feature', 'bug', 'chore'],
      limit: 20 # adjust this if you want to disply more/less stories
    }
    stories = pt_project.stories.all(conditions)

    if stories.count > 0
      branches = []
      the_stories = []

      puts ""
      puts "Here are the next #{conditions[:limit]} stories in your queue:"
      puts ""
      puts "=================================================="
      puts ""

      stories.each_with_index do |story, index|
        name = story.name.downcase
        # remove individual offending chars: single quote, double quote, open
        # paren, close paren, colon, backslash, forwardslash and replace with
        # an empty string (aka nothing)
        name.gsub!(/[`'"\.\(\)\[\]:\/\\&,]/,"")
        # remove what remains of cl.ly links
        name.gsub!(/httpclly\S*/,"")
        # remove dash and replace with space
        name.gsub!(/-/," ")
        # do the truncate here, after all the removal & before the _ injection
        name = truncate_words(name)
        # replace all instances of one or more spaces with _
        name.gsub!(/\s+/,"_")
        # create final display
        display = "#{story.id}_#{name}"
        branches[index + 1] = "#{display}"
        the_stories[index + 1] = story
        if index + 1 < 10
          padding = ' '
        else
          padding = ''
        end
        puts "  (#{index + 1})#{padding} #{display}"
      end

      puts ""
      puts "  (0) EXIT WITHOUT ANY ACTION"
      puts ""
      puts "=================================================="
      puts ""
      puts "For which story do you want to create a new git feature branch?"
      puts ""
      puts "    Tip: `1` to create a feature branch for the first story."
      puts "    Tip: `1,hotfix` to create a hotfix for the first story."
      puts "    Tip: `1,bugfix` to create a bugfix for the first story."
      puts ""

      input = STDIN.gets.strip
      story_num = input.split(",")[0].to_i
      story_type = input.split(",")[1]
      story_type ||= "feature"

      if (1..stories.count).include?(story_num)
        story = the_stories[story_num]
        story_description = <<EOF

Tracker Story: #{story.id} | Type: #{story.story_type} | Estimate: #{story.estimate}

Tracker Name:
-------------
#{story.name}

Tracker Description:
====================
#{story.description}

EOF
        File.open(Rails.root.join("doc/current_pt_story.txt"), 'w') {|f| f.write(story_description) }

        story = pt_project.stories.find(story.id)

        if story_type == 'feature'
          gitflow_helper(story_type, branches[story_num], 'develop')
          `git flow #{story_type} start "#{branches[story_num]}"`
          story.update(current_state: 'started')
        elsif story_type == 'hotfix'
          gitflow_helper(story_type, branches[story_num], 'master')
          `git flow #{story_type} start "#{branches[story_num]}"`
          story.update(current_state: 'started')
        elsif story_type == 'bugfix'
          parent_branch = `git branch`.split("\n").delete_if { |i| i[0] != "*" }[0].gsub('* ','')
          gitflow_helper(story_type, branches[story_num], parent_branch)
          `git checkout -b bugfix/"#{branches[story_num]}"`
          story.update(current_state: 'started')
        else
          puts "Error: unknown feature type"
        end
      else
        exit
      end
    else
      puts "There are no available stories right now!"
    end
  end

  desc "sets the state of a pivotal tracker story to 'delivered'"
  task :deliver do
    if Rails.env == "development"
      conditions = {
        state: ['finished'],
        story_type: ['feature', 'bug'],
        label: 'app'
      }
      stories = pt_project.stories.all(conditions)

      stories.each do |story|
        labels = story.labels
        story.update(current_state: "delivered", labels: "#{labels},#{ENV['DEPLOY_TO']}")
        puts "Delivered: '##{story.id} - #{story.name}'"
      end
    end
  end
end