class Aid::Scripts::Begin

Public Class Methods

description() click to toggle source
# File lib/aid/scripts/begin.rb, line 15
def self.description
  "Starts a new Asana story"
end
help() click to toggle source
# File lib/aid/scripts/begin.rb, line 19
      def self.help
        <<~HELP
          Fill me in.
          aid begin:
            Starts a story on Asana, moves it into the Work In Progress column,
            and opens a pull request on GitHub for you.
          Usage:
            aid begin "https://app.asana.com/0/1135298954694367/1135903276459289/f"
        HELP
      end

Public Instance Methods

run() click to toggle source
# File lib/aid/scripts/begin.rb, line 30
      def run
        check_for_hub!
        check_for_hub_credentials!

        prompt_for_config!(
          "asana.personal-access-token",
          "Enter your personal access token",
          <<~EOF
            1. Visit https://app.asana.com/0/developer-console
            2. Click "+ New access token" under "Personal access tokens"
            3. Give the token a name
            4. Copy the token and paste it back here.
          EOF
        )

        prompt_for_config!(
          "github.user",
          "Enter your GitHub username",
          <<~EOF
            1. Visit https://github.com and sign in
            2. Find the GitHub username you use and paste it in here
          EOF
        )

        set_asana_project_id_if_needed!
        check_for_existing_branch!
        ensure_base_branch_is_ok!

        step "Starting '#{asana_task.name}' on Asana" do
          start_task
        end

        step "Creating local branch off '#{base_branch}'" do
          check_for_staged_files!
          reset_hard_to_base_branch!
          create_git_branch!
          make_empty_commit!
        end

        step "Pushing to Github" do
          push_branch_to_github!
        end

        step "Opening pull request on Github" do
          open_pull_request_on_github!
        end
      end

Private Instance Methods

asana() click to toggle source
# File lib/aid/scripts/begin.rb, line 232
def asana
  @asana ||=
    Asana::Client.new do |client|
      client.default_headers "asana-enable" => "string_ids,new_sections"
      client.authentication :access_token,
                            git_config("asana.personal-access-token")
    end
end
asana_project() click to toggle source
# File lib/aid/scripts/begin.rb, line 179
def asana_project
  @asana_project ||= asana.projects.find_by_id(asana_project_id)
end
asana_project_id() click to toggle source
# File lib/aid/scripts/begin.rb, line 197
def asana_project_id
  git_config("asana.project-id")&.to_i
end
asana_sections() click to toggle source
# File lib/aid/scripts/begin.rb, line 187
def asana_sections
  asana.sections.find_by_project(project: asana_project.gid)
end
asana_task() click to toggle source
# File lib/aid/scripts/begin.rb, line 183
def asana_task
  @asana_task ||= asana.tasks.find_by_id(asana_task_id)
end
asana_task_id() click to toggle source
# File lib/aid/scripts/begin.rb, line 216
def asana_task_id
  @asana_task_id ||=
    begin
      result = asana_url
        .match(ASANA_URL_PATTERN)

      abort help unless result

      result[:task_id].to_i
    end
end
asana_url() click to toggle source
# File lib/aid/scripts/begin.rb, line 228
def asana_url
  argv.first.to_s
end
asana_wip_section() click to toggle source
# File lib/aid/scripts/begin.rb, line 191
def asana_wip_section
  asana_sections.detect do |task|
    task.name =~ /(Work In Progress|WIP)/i
  end
end
base_branch() click to toggle source
# File lib/aid/scripts/begin.rb, line 133
def base_branch
  @base_branch ||= prompt_for_base_branch
end
check_for_existing_branch!() click to toggle source
# File lib/aid/scripts/begin.rb, line 255
def check_for_existing_branch!
  return unless system "git ls-remote --exit-code --heads origin #{story_branch_name}"

  print colorize(
    :yellow,
    "The branch for this task (#{story_branch_name}) already exists on origin, \n"\
      "if you continue, it will be reset to master. \n"\
      "Do you want to continue? ? (y/n) > "
  )

  answer = STDIN.gets.strip.downcase

  exit if answer[0] != "y"
end
check_for_hub!() click to toggle source
# File lib/aid/scripts/begin.rb, line 295
      def check_for_hub!
        unless command?("hub")
          abort <<~EOF
            You need to install `hub` before you can use this program.
            To fix:
              $ brew install hub
          EOF
        end
      end
check_for_hub_credentials!() click to toggle source
# File lib/aid/scripts/begin.rb, line 274
      def check_for_hub_credentials!
        config_file = "#{ENV['HOME']}/.config/hub"
        credentials_exist = File.exist?(config_file) &&
                            File.read(config_file).match(/oauth_token/i)

        unless credentials_exist
          abort <<~EOF
            Your GitHub credentials are not set. Run this command:

              $ hub pull-request

            and when prompted for login details, enter them. It will give you
            an error at the end, but you can safely ignore it.
          EOF
        end
      end
command?(name) click to toggle source
# File lib/aid/scripts/begin.rb, line 291
def command?(name)
  system("which #{name} 2>&1 >/dev/null")
end
create_git_branch!() click to toggle source
# File lib/aid/scripts/begin.rb, line 270
def create_git_branch!
  system! "git checkout -b '#{story_branch_name}'"
end
current_branch_name() click to toggle source
# File lib/aid/scripts/begin.rb, line 125
def current_branch_name
  `git rev-parse --abbrev-ref HEAD`.strip
end
ensure_base_branch_is_ok!() click to toggle source
# File lib/aid/scripts/begin.rb, line 129
def ensure_base_branch_is_ok!
  base_branch
end
github_user() click to toggle source
# File lib/aid/scripts/begin.rb, line 111
def github_user
  git_config("github.user")
end
make_empty_commit!() click to toggle source
# File lib/aid/scripts/begin.rb, line 119
def make_empty_commit!
  msg = "Initial commit for task ##{asana_task_id} [ci skip]"

  silent("git commit --allow-empty -m #{msg.inspect}")
end
open_pull_request_on_github!() click to toggle source
# File lib/aid/scripts/begin.rb, line 80
def open_pull_request_on_github!
  tempfile = Tempfile.new("begin_pull_request")

  begin
    tempfile.write(pull_request_description)
    tempfile.close

    labels = "WIP"

    url = `hub pr list -h #{current_branch_name} -f '%U'`.strip
    if url.empty?
      url = `hub pull-request -F #{tempfile.path} -l "#{labels}" -d -a #{github_user}`.strip
    end
    puts colorize(:blue, url)

    # Copy it to your clipboard
    pbcopy_exist = system("command -v pbcopy >/dev/null 2>&1")
    system("echo #{url.inspect} | pbcopy") if pbcopy_exist

    # Update metadata in .git/config
    result = url.match(%r{pull/(\d+)})
    pull_request_id = result && result[1]

    git_config("tasks.#{asana_task_id}.url", asana_url)
    git_config("tasks.#{asana_task_id}.name", asana_task.name)
    git_config("tasks.#{asana_task_id}.pull-request-id", pull_request_id)
  ensure
    tempfile.unlink
  end
end
prompt_for_base_branch() click to toggle source
# File lib/aid/scripts/begin.rb, line 137
def prompt_for_base_branch
  current_branch = current_branch_name

  if current_branch != "master"
    print colorize(
      :yellow,
      "Current branch is #{colorize(:blue, current_branch)} - do you want "\
        "to create a branch off that? (y/n) > "
    )

    answer = STDIN.gets.strip

    exit if answer[0] != "y"
  end

  current_branch
end
pull_request_description() click to toggle source
# File lib/aid/scripts/begin.rb, line 241
      def pull_request_description
        <<~EOF
          #{asana_task.name}

          #{asana_url}
        EOF
      end
push_branch_to_github!() click to toggle source
# File lib/aid/scripts/begin.rb, line 115
def push_branch_to_github!
  silent "git push --force-with-lease -u origin '#{current_branch_name}'"
end
reset_hard_to_base_branch!() click to toggle source
# File lib/aid/scripts/begin.rb, line 249
def reset_hard_to_base_branch!
  silent "git checkout #{base_branch}",
        "git fetch origin",
        "git reset --hard origin/#{base_branch}"
end
set_asana_project_id_if_needed!() click to toggle source
# File lib/aid/scripts/begin.rb, line 201
def set_asana_project_id_if_needed!
  return if asana_project_id

  result = asana_url
    .match(ASANA_URL_PATTERN)

  abort help unless result

  id = result[:project_id].to_i

  puts "Setting asana.project-id to #{id}"

  git_config("asana.project-id", id)
end
silent(*cmds) click to toggle source
# File lib/aid/scripts/begin.rb, line 305
def silent(*cmds)
  cmds.each { |cmd| system("#{cmd} >/dev/null") }
end
start_task() click to toggle source
# File lib/aid/scripts/begin.rb, line 167
def start_task
  # Assign task to runner of this script
  user = asana.users.me
  asana_task.update(assignee: user.gid)

  # Move it to the WIP column
  asana_task.add_project(
    project: asana_project.gid,
    section: asana_wip_section.gid
  )
end
story_branch_name() click to toggle source
# File lib/aid/scripts/begin.rb, line 155
def story_branch_name
  name = asana_task
    .name
    .gsub(%r{[/.]}, "-")
    .gsub(/\s+/, "-")
    .gsub(/[^\w-]/, "")
    .downcase
    .slice(0, 50)

  "#{name}-#{asana_task_id}"
end