trellish 0.0.15 → 0.0.18
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.md +40 -9
- data/bin/trellish +33 -19
- data/lib/trellish.rb +8 -2
- data/lib/trellish/auth.rb +6 -1
- data/lib/trellish/campfire.rb +24 -0
- data/lib/trellish/card.rb +97 -8
- data/lib/trellish/git.rb +60 -6
- data/lib/trellish/version.rb +1 -1
- data/trellish.example.yml +11 -1
- data/trellish.gemspec +1 -0
- metadata +19 -2
data/README.md
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
# Trellish
|
2
2
|
|
3
|
-
Trellish is used to finish a Trello card. It does everything necessary to move a development card from
|
3
|
+
Trellish is used to start and finish a Trello card. It does everything necessary to move a development card from "Next up" to "In Progress" when you start work on a card and again from "In Progress" to "QA" when you finish a card.
|
4
4
|
|
5
5
|
## Installation
|
6
6
|
|
@@ -16,9 +16,9 @@ Or install it yourself as:
|
|
16
16
|
|
17
17
|
$ gem install trellish
|
18
18
|
|
19
|
-
##
|
19
|
+
## Setup
|
20
20
|
|
21
|
-
Create a trellish.yml
|
21
|
+
Create a `./trellish.yml`, `~/trellish.yml` or `~/.trellish` file with the contents of [trellish.example.yml](trellish/blob/master/trellish.example.yml). Set it up like this:
|
22
22
|
|
23
23
|
1. Sign in to Trello and go to https://trello.com/1/appKey/generate.
|
24
24
|
1. Copy "Key" from that page to trello\_api\_key.
|
@@ -28,18 +28,49 @@ Create a trellish.yml file in your current directory or home directory. Set it u
|
|
28
28
|
1. Run: `curl -u 'username' -d '{"scopes":["repo"],"note":"Trellish"}' https://api.github.com/authorizations`
|
29
29
|
1. Copy the token parameter from the response to github\_oauth\_token.
|
30
30
|
|
31
|
-
|
31
|
+
Optionally, Trellish can announce the starting and finishing of cards on 37signal's [Campfire](http://campfirenow.com/). To enable this:
|
32
|
+
|
33
|
+
1. Sign in to Campfire and go to your "my info" page. You can find the link in the upper-right corner.
|
34
|
+
1. Copy your subdomain name to campfire\_subdomain.
|
35
|
+
1. Copy your API authentication token to campfire\_token.
|
36
|
+
1. Specify the room name in campfire\_room.
|
37
|
+
|
38
|
+
## Usage
|
39
|
+
|
40
|
+
By default, Trellish expects a Trello board named `Current` with 3 lists: `Next up`, `In progress`, and `QA`. You can change these defaults using the Trellish config file.
|
41
|
+
|
42
|
+
To start work on a card:
|
43
|
+
|
44
|
+
trellish start [Trello card id or URL]
|
45
|
+
|
46
|
+
This will:
|
47
|
+
|
48
|
+
- move the card from `Next up` to `In progress`,
|
49
|
+
- add you as a member,
|
50
|
+
- create a local git branch named using your git initials and the card's title.
|
51
|
+
|
52
|
+
If you don't provide a card id or URL on the command line, trellish shows you the cards in `Next up` and prompts you to select one, like so:
|
53
|
+
|
54
|
+
Select a card:
|
55
|
+
1. BUG: crash adding a comment
|
56
|
+
2. users can select an avatar
|
57
|
+
3. add iPad integration tests
|
58
|
+
>
|
59
|
+
|
60
|
+
When you're done working on a card, finish it using:
|
32
61
|
|
33
|
-
trellish
|
62
|
+
trellish finish [Trello card id or URL]
|
34
63
|
|
35
|
-
|
64
|
+
Like:
|
36
65
|
|
37
|
-
trellish a3Wbcde4
|
66
|
+
trellish finish https://trello.com/c/a3Wbcde4
|
67
|
+
trellish finish a3Wbcde4
|
68
|
+
trellish finish
|
38
69
|
|
39
70
|
This will:
|
40
71
|
|
41
|
-
- create a pull request to merge your topic branch into master
|
42
|
-
- add a link to the pull request
|
72
|
+
- create a pull request to merge your topic branch into master (with a description linking back to the card)
|
73
|
+
- add a link to the pull request from the card's description
|
43
74
|
- remove everyone from the card
|
44
75
|
- move the card to the QA list
|
45
76
|
|
data/bin/trellish
CHANGED
@@ -1,32 +1,46 @@
|
|
1
1
|
#!/usr/bin/env ruby
|
2
|
+
# encoding: utf-8
|
2
3
|
|
4
|
+
require 'readline'
|
3
5
|
require 'trellish'
|
4
6
|
require 'trellish/card'
|
5
7
|
require 'yaml'
|
6
8
|
|
7
|
-
|
8
|
-
Trellish.logger.error "Please provide a Trello card id or URL."
|
9
|
-
exit
|
10
|
-
end
|
9
|
+
# process config file
|
11
10
|
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
YAML.load(File.read(File.expand_path('~/trellish.yml')))
|
16
|
-
end
|
11
|
+
config_paths = %w(trellish.yml ~/trellish.yml ~/.trellish)
|
12
|
+
config_filepath = config_paths.collect { |e| File.expand_path(e) }.find { |e| File.exists?(e) }
|
13
|
+
config_file = YAML.load(File.read(config_filepath)) if config_filepath
|
17
14
|
|
18
15
|
unless config_file
|
19
|
-
Trellish.logger.error "Could not locate
|
16
|
+
Trellish.logger.error "Could not locate your settings in ./trellish.yml, ~/trellish.yml, or ~/.trellish"
|
20
17
|
exit
|
21
18
|
end
|
22
19
|
|
23
|
-
Trellish.configure(
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
git_base_branch: config_file['git_base_branch'] || 'master',
|
29
|
-
qa_list_name: config_file['qa_list_name'] || 'QA')
|
20
|
+
Trellish.configure(config_file)
|
21
|
+
|
22
|
+
# parse command-line arguments
|
23
|
+
|
24
|
+
case ARGV[0]
|
30
25
|
|
31
|
-
|
32
|
-
card
|
26
|
+
when 'start'
|
27
|
+
card =
|
28
|
+
if ARGV[1]
|
29
|
+
Trellish::Card.new(ARGV[1])
|
30
|
+
else
|
31
|
+
Trellish::Card.select_from_list(Trellish.config[:next_up_list_name], false)
|
32
|
+
end
|
33
|
+
puts %Q(Starting card “#{card.name}”)
|
34
|
+
card.start
|
35
|
+
|
36
|
+
when 'finish'
|
37
|
+
card =
|
38
|
+
if ARGV[1]
|
39
|
+
Trellish::Card.new(ARGV[1])
|
40
|
+
else
|
41
|
+
Trellish::Card.select_from_list(Trellish.config[:in_progress_list_name], true)
|
42
|
+
end
|
43
|
+
puts %Q(Finishing card “#{card.name}”)
|
44
|
+
card.finish
|
45
|
+
|
46
|
+
end
|
data/lib/trellish.rb
CHANGED
@@ -12,8 +12,14 @@ module Trellish
|
|
12
12
|
trello_oauth_secret: 'TRELLO_OAUTH_SECRET',
|
13
13
|
trello_oauth_token: 'TRELLO_OAUTH_TOKEN',
|
14
14
|
github_oauth_token: 'GITHUB_OAUTH_TOKEN',
|
15
|
-
git_base_branch: '
|
16
|
-
|
15
|
+
git_base_branch: 'master',
|
16
|
+
board_name: 'Current',
|
17
|
+
next_up_list_name: 'Next up',
|
18
|
+
in_progress_list_name: 'In progress',
|
19
|
+
qa_list_name: 'QA',
|
20
|
+
campfire_subdomain: nil,
|
21
|
+
campfire_token: nil,
|
22
|
+
campfire_room: nil
|
17
23
|
}
|
18
24
|
|
19
25
|
@valid_config_keys = @config.keys
|
data/lib/trellish/auth.rb
CHANGED
@@ -8,7 +8,11 @@ module Trellish
|
|
8
8
|
|
9
9
|
Trello::Authorization.const_set :AuthPolicy, OAuthPolicy
|
10
10
|
|
11
|
+
@@member = nil
|
12
|
+
|
11
13
|
def self.authorize
|
14
|
+
return @@member if @@member
|
15
|
+
|
12
16
|
OAuthPolicy.consumer_credential = OAuthCredential.new(
|
13
17
|
Trellish.config[:trello_api_key],
|
14
18
|
Trellish.config[:trello_oauth_secret])
|
@@ -17,7 +21,8 @@ module Trellish
|
|
17
21
|
# Test that the user is authorized
|
18
22
|
begin
|
19
23
|
member_id = Trello::Token.find(OAuthPolicy.token.key).member_id
|
20
|
-
Member.find(member_id)
|
24
|
+
@@member = Member.find(member_id)
|
25
|
+
@@member
|
21
26
|
rescue
|
22
27
|
Trellish.logger.error "Unable to authorize access to Trello API."
|
23
28
|
exit
|
@@ -0,0 +1,24 @@
|
|
1
|
+
require 'tinder'
|
2
|
+
|
3
|
+
module Trellish
|
4
|
+
module Campfire
|
5
|
+
|
6
|
+
def announce(message)
|
7
|
+
room.speak message if room
|
8
|
+
end
|
9
|
+
|
10
|
+
private
|
11
|
+
|
12
|
+
def room
|
13
|
+
@room ||=
|
14
|
+
if [:campfire_subdomain, :campfire_token, :campfire_room].all? { |s| Trellish.config[s] }
|
15
|
+
campfire = Tinder::Campfire.new Trellish.config[:campfire_subdomain], :token => Trellish.config[:campfire_token]
|
16
|
+
campfire.rooms.find { |room| room.name == Trellish.config[:campfire_room] }
|
17
|
+
end
|
18
|
+
rescue
|
19
|
+
Trellish.logger.error "Unable to access Campfire. Is your subdomain, token, and room name correct?"
|
20
|
+
exit
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
end
|
data/lib/trellish/card.rb
CHANGED
@@ -1,41 +1,130 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'trellish/campfire'
|
1
4
|
require 'trellish/git'
|
2
5
|
|
3
6
|
module Trellish
|
4
7
|
class Card
|
5
8
|
include Trello
|
6
9
|
include Trellish::Git
|
10
|
+
include Trellish::Campfire
|
11
|
+
|
12
|
+
def self.select_from_list(list_name, assigned_to_me = false)
|
13
|
+
me = Trellish::Auth.authorize
|
14
|
+
boards = me.boards(filter: :open)
|
15
|
+
current_board = boards.find { |board| board.name == Trellish.config[:board_name] }
|
16
|
+
if current_board.nil?
|
17
|
+
Trellish.logger.error "Unable to find a board named `#{Trellish.config[:board_name]}`."
|
18
|
+
exit
|
19
|
+
end
|
20
|
+
|
21
|
+
list = current_board.lists.find { |list| list.name == list_name }
|
22
|
+
if list.nil?
|
23
|
+
Trellish.logger.error "Unable to find a list named `#{list_name}` on board `#{Trellish.config[:board_name]}`."
|
24
|
+
exit
|
25
|
+
end
|
26
|
+
|
27
|
+
cards = list.cards
|
28
|
+
|
29
|
+
if assigned_to_me
|
30
|
+
cards = cards.find_all { |card| card.member_ids.include?(me.id) }
|
31
|
+
end
|
32
|
+
|
33
|
+
case cards.length
|
34
|
+
when 0
|
35
|
+
if assigned_to_me
|
36
|
+
Trellish.logger.error "There are no cards assigned to you in the list `#{list_name}`"
|
37
|
+
else
|
38
|
+
Trellish.logger.error "There are no cards in the list `#{list_name}`"
|
39
|
+
end
|
40
|
+
exit
|
41
|
+
when 1
|
42
|
+
index = 0
|
43
|
+
else
|
44
|
+
puts "\nSelect a card:"
|
45
|
+
cards.each_with_index { |card, index| puts "#{index+1}. #{card.name}" }
|
46
|
+
index = Readline.readline("> ")
|
47
|
+
exit if index.blank?
|
48
|
+
index = index.to_i - 1
|
49
|
+
end
|
50
|
+
|
51
|
+
Trellish::Card.new( cards[index].id )
|
52
|
+
end
|
7
53
|
|
8
54
|
def initialize(card_id_or_url)
|
9
55
|
@member = Trellish::Auth.authorize
|
10
56
|
@card = Trello::Card.find(parse_card_id(card_id_or_url))
|
11
57
|
end
|
12
58
|
|
59
|
+
def add_me_as_member
|
60
|
+
@card.add_member(@member) unless @card.member_ids.include?(@member.id)
|
61
|
+
end
|
62
|
+
|
13
63
|
def add_pull_request_link
|
14
|
-
|
15
|
-
|
64
|
+
# Unfortunately, changing the card's description changes the card's URL, which breaks
|
65
|
+
# the link from the pull request, and our announcements to Campfire. Instead, add
|
66
|
+
# a comment to the pull request.
|
67
|
+
# @card.description = "[Pull Request](#{github_pull_request_url})\n\n#{@card.description}"
|
68
|
+
# @card.update!
|
69
|
+
message = "Pull request is at #{github_pull_request_url}"
|
70
|
+
@card.add_comment message
|
71
|
+
end
|
72
|
+
|
73
|
+
def create_local_branch
|
74
|
+
branch_name = "#{git_user_initials}-#{short_name}"
|
75
|
+
git_create_local_branch(branch_name)
|
16
76
|
end
|
17
77
|
|
18
78
|
def finish
|
79
|
+
if !current_git_branch_is_up_to_date?
|
80
|
+
finish = Readline.readline("Your remote branch isn’t up-to-date. Finish anyway? [y,N] ")
|
81
|
+
exit unless ['y','yes'].include?(finish.strip.downcase)
|
82
|
+
end
|
83
|
+
|
19
84
|
add_pull_request_link
|
20
85
|
remove_all
|
21
86
|
move_to_qa
|
87
|
+
announce %Q([Trellish] Finished card “#{@card.name}” #{@card.url}. The pull request is at #{github_pull_request_url})
|
22
88
|
end
|
23
89
|
|
24
90
|
def move_to_qa
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
91
|
+
move_to_list(Trellish.config[:qa_list_name])
|
92
|
+
end
|
93
|
+
|
94
|
+
def move_to_in_progress
|
95
|
+
move_to_list(Trellish.config[:in_progress_list_name])
|
96
|
+
end
|
97
|
+
|
98
|
+
def name
|
99
|
+
@card.name
|
31
100
|
end
|
32
101
|
|
33
102
|
def remove_all
|
34
103
|
@card.remove_all_members
|
35
104
|
end
|
36
105
|
|
106
|
+
def short_name
|
107
|
+
name.strip.downcase.tr(' ','-').gsub(/[^0-9A-Za-z\-]/, '')[0..30]
|
108
|
+
end
|
109
|
+
|
110
|
+
def start
|
111
|
+
move_to_in_progress
|
112
|
+
add_me_as_member
|
113
|
+
create_local_branch
|
114
|
+
announce %Q([Trellish] Starting card “#{@card.name}” #{@card.url})
|
115
|
+
end
|
116
|
+
|
37
117
|
private
|
38
118
|
|
119
|
+
def move_to_list(list_name)
|
120
|
+
list = @card.board.lists.find { |list| list.name == list_name }
|
121
|
+
if list
|
122
|
+
@card.move_to_list(list)
|
123
|
+
else
|
124
|
+
Trellish.logger.warn "Unable to move card to #{list_name} list. No list named #{list_name} found."
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
39
128
|
def parse_card_id(card_id_or_url)
|
40
129
|
card_id_or_url.match(/[A-Za-z0-9]*$/)[0]
|
41
130
|
end
|
data/lib/trellish/git.rb
CHANGED
@@ -3,10 +3,6 @@ require 'faraday'
|
|
3
3
|
module Trellish
|
4
4
|
module Git
|
5
5
|
|
6
|
-
def current_git_branch
|
7
|
-
@current_git_branch ||= `cat .git/head`.split('/').last.strip
|
8
|
-
end
|
9
|
-
|
10
6
|
def github_pull_request_url
|
11
7
|
return @github_pull_request_url if @github_pull_request_url
|
12
8
|
conn = Faraday.new(:url => 'https://api.github.com', :ssl => {:ca_file => '/System/Library/OpenSSL/certs/ca-certificates.crt'}) do |faraday|
|
@@ -20,7 +16,8 @@ module Trellish
|
|
20
16
|
req.headers['Authorization'] = "token #{Trellish.config[:github_oauth_token]}"
|
21
17
|
req.body = {
|
22
18
|
title: @card.name,
|
23
|
-
|
19
|
+
body: "[Trello card](#{@card.url})",
|
20
|
+
base: git_base_branch,
|
24
21
|
head: "#{git_repository_owner}:#{current_git_branch}"
|
25
22
|
}.to_json
|
26
23
|
end
|
@@ -43,8 +40,65 @@ module Trellish
|
|
43
40
|
@git_repository_owner ||= matches[1]
|
44
41
|
end
|
45
42
|
|
43
|
+
def git_create_local_branch(branch_name)
|
44
|
+
`git checkout -b #{branch_name} #{git_base_branch}`
|
45
|
+
rescue
|
46
|
+
Trellish.logger.warn "Failed to create a local git branch named #{branch_name}."
|
47
|
+
end
|
48
|
+
|
49
|
+
def current_git_branch_is_up_to_date?
|
50
|
+
git_remote_up_to_date?(current_git_branch)
|
51
|
+
end
|
52
|
+
|
53
|
+
def git_user_initials
|
54
|
+
return @user_initials if @user_initials
|
55
|
+
username = presence(`git config github.user`) || presence(`git config user.email`) || presence(`whoami`)
|
56
|
+
@user_initials = username[0..2]
|
57
|
+
end
|
58
|
+
|
59
|
+
private
|
60
|
+
|
61
|
+
def current_git_branch
|
62
|
+
@current_git_branch ||= `cat #{git_dir}/head`.split('/').last.strip
|
63
|
+
end
|
64
|
+
|
65
|
+
def git_base_branch
|
66
|
+
Trellish.config[:git_base_branch]
|
67
|
+
end
|
68
|
+
|
69
|
+
def git_dir
|
70
|
+
return @git_dir if @git_dir
|
71
|
+
path = `git rev-parse --git-dir`.strip
|
72
|
+
if path[/^fatal/]
|
73
|
+
Trellish.logger.error "Failed to find your git repository."
|
74
|
+
exit
|
75
|
+
end
|
76
|
+
@git_dir = path
|
77
|
+
end
|
78
|
+
|
79
|
+
def git_hash_for_ref(ref)
|
80
|
+
`git show-ref --hash #{ref}`.strip
|
81
|
+
end
|
82
|
+
|
83
|
+
def git_remote_up_to_date?(local_branch_name)
|
84
|
+
remote_branch_name = git_remote_branch_for_local_branch(local_branch_name)
|
85
|
+
|
86
|
+
local_hash = git_hash_for_ref("heads/#{local_branch_name}")
|
87
|
+
remote_hash = git_hash_for_ref("remotes/#{remote_branch_name}")
|
88
|
+
|
89
|
+
local_hash == remote_hash
|
90
|
+
end
|
91
|
+
|
92
|
+
def git_remote_branch_for_local_branch(local_branch_name)
|
93
|
+
`git for-each-ref --format='%(upstream:short)' refs/heads/#{local_branch_name}`.strip
|
94
|
+
end
|
95
|
+
|
46
96
|
def matches
|
47
|
-
@matches ||=
|
97
|
+
@matches ||= remote_url.match(%r|^git@github.com:([^/]*)\/([^\.]*)\.git$|)
|
98
|
+
end
|
99
|
+
|
100
|
+
def presence(s)
|
101
|
+
s.strip if s && !s.strip.empty?
|
48
102
|
end
|
49
103
|
|
50
104
|
def remote_url
|
data/lib/trellish/version.rb
CHANGED
data/trellish.example.yml
CHANGED
@@ -18,5 +18,15 @@ github_oauth_token: numbers_and_letters_and_stuff
|
|
18
18
|
# The branch you want your changes merged into
|
19
19
|
git_base_branch: master
|
20
20
|
|
21
|
-
# Name of your
|
21
|
+
# Name of your Trello board
|
22
|
+
board_name: Current
|
23
|
+
|
24
|
+
# Names of your Next up, In progress, and QA lists in Trello
|
25
|
+
next_up_list_name: Next up
|
26
|
+
in_progress_list_name: In progress
|
22
27
|
qa_list_name: QA
|
28
|
+
|
29
|
+
# Campfire subdomain, room & your token
|
30
|
+
campfire_subdomain: subdomain
|
31
|
+
campfire_token: 1111111111111111111111111111111111111111
|
32
|
+
campfire_room: Development
|
data/trellish.gemspec
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: trellish
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.18
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date:
|
12
|
+
date: 2013-01-06 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: oauth2
|
@@ -43,6 +43,22 @@ dependencies:
|
|
43
43
|
- - ! '>='
|
44
44
|
- !ruby/object:Gem::Version
|
45
45
|
version: 0.4.4
|
46
|
+
- !ruby/object:Gem::Dependency
|
47
|
+
name: tinder
|
48
|
+
requirement: !ruby/object:Gem::Requirement
|
49
|
+
none: false
|
50
|
+
requirements:
|
51
|
+
- - ! '>='
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: 1.9.1
|
54
|
+
type: :runtime
|
55
|
+
prerelease: false
|
56
|
+
version_requirements: !ruby/object:Gem::Requirement
|
57
|
+
none: false
|
58
|
+
requirements:
|
59
|
+
- - ! '>='
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: 1.9.1
|
46
62
|
description: Create a pull request, put link to it on the card, remove everyone and
|
47
63
|
move the card to the QA list
|
48
64
|
email:
|
@@ -61,6 +77,7 @@ files:
|
|
61
77
|
- bin/trellish
|
62
78
|
- lib/trellish.rb
|
63
79
|
- lib/trellish/auth.rb
|
80
|
+
- lib/trellish/campfire.rb
|
64
81
|
- lib/trellish/card.rb
|
65
82
|
- lib/trellish/git.rb
|
66
83
|
- lib/trellish/version.rb
|