cp8_cli 4.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +10 -0
  3. data/.travis.yml +4 -0
  4. data/CODE_OF_CONDUCT.md +49 -0
  5. data/Gemfile +4 -0
  6. data/LICENSE.txt +21 -0
  7. data/README.md +19 -0
  8. data/Rakefile +11 -0
  9. data/cp8_cli.gemspec +42 -0
  10. data/exe/cp8 +34 -0
  11. data/exe/git-cleanup +7 -0
  12. data/exe/git-finish +19 -0
  13. data/exe/git-open +7 -0
  14. data/exe/git-start +7 -0
  15. data/lib/cp8_cli.rb +9 -0
  16. data/lib/cp8_cli/branch.rb +61 -0
  17. data/lib/cp8_cli/branch_name.rb +30 -0
  18. data/lib/cp8_cli/cleanup.rb +36 -0
  19. data/lib/cp8_cli/command.rb +61 -0
  20. data/lib/cp8_cli/config_store.rb +32 -0
  21. data/lib/cp8_cli/current_user.rb +29 -0
  22. data/lib/cp8_cli/github/base.rb +13 -0
  23. data/lib/cp8_cli/github/issue.rb +92 -0
  24. data/lib/cp8_cli/github/parsed_short_link.rb +25 -0
  25. data/lib/cp8_cli/github/parsed_url.rb +25 -0
  26. data/lib/cp8_cli/global_config.rb +51 -0
  27. data/lib/cp8_cli/local_config.rb +29 -0
  28. data/lib/cp8_cli/main.rb +74 -0
  29. data/lib/cp8_cli/pull_request.rb +63 -0
  30. data/lib/cp8_cli/repo.rb +21 -0
  31. data/lib/cp8_cli/story_query.rb +23 -0
  32. data/lib/cp8_cli/table.rb +38 -0
  33. data/lib/cp8_cli/table/row.rb +45 -0
  34. data/lib/cp8_cli/trello/base.rb +36 -0
  35. data/lib/cp8_cli/trello/board.rb +13 -0
  36. data/lib/cp8_cli/trello/card.rb +71 -0
  37. data/lib/cp8_cli/trello/error.rb +5 -0
  38. data/lib/cp8_cli/trello/error_handler.rb +15 -0
  39. data/lib/cp8_cli/trello/json_parser.rb +13 -0
  40. data/lib/cp8_cli/trello/label.rb +15 -0
  41. data/lib/cp8_cli/trello/list.rb +32 -0
  42. data/lib/cp8_cli/trello/member.rb +11 -0
  43. data/lib/cp8_cli/version.rb +27 -0
  44. metadata +357 -0
@@ -0,0 +1,32 @@
1
+ module Cp8Cli
2
+ class ConfigStore
3
+ def initialize(path)
4
+ @path = path
5
+ end
6
+
7
+ def [](key)
8
+ data[key]
9
+ end
10
+
11
+ def save(key, value)
12
+ data[key] = value
13
+ File.new(path, "w") unless File.exists?(path)
14
+ File.open(path, "w") { |f| f.write(data.to_yaml) }
15
+ value
16
+ end
17
+
18
+ private
19
+
20
+ attr_reader :path
21
+
22
+ def data
23
+ @_data ||= load_data
24
+ end
25
+
26
+ def load_data
27
+ YAML.load File.read(path)
28
+ rescue
29
+ {}
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,29 @@
1
+ module Cp8Cli
2
+ class CurrentUser
3
+ def initials
4
+ git_user_name.parameterize(separator: " ").split.map(&:first).join
5
+ end
6
+
7
+ def trello_id
8
+ trello_user.id
9
+ end
10
+
11
+ def github_login
12
+ github_user.login
13
+ end
14
+
15
+ private
16
+
17
+ def git_user_name
18
+ @_git_user_name ||= Command.read("git config user.name")
19
+ end
20
+
21
+ def github_user
22
+ @_github_user ||= Github::Base.client.user
23
+ end
24
+
25
+ def trello_user
26
+ @_trello_user ||= Trello::Member.current
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,13 @@
1
+ require "octokit"
2
+
3
+ module Cp8Cli
4
+ module Github
5
+ class Base
6
+ cattr_accessor :client
7
+
8
+ def self.configure(token:)
9
+ self.client = Octokit::Client.new(access_token: token)
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,92 @@
1
+ require "cp8_cli/github/base"
2
+ require "cp8_cli/github/parsed_url"
3
+ require "cp8_cli/github/parsed_short_link"
4
+
5
+ module Cp8Cli
6
+ module Github
7
+ class Issue < Base
8
+ def initialize(number:, repo:, attributes:)
9
+ @number = number
10
+ @repo = repo
11
+ @attributes = attributes
12
+ end
13
+
14
+ def self.fields
15
+ [:title]
16
+ end
17
+
18
+ def self.find_by_url(url)
19
+ url = ParsedUrl.new(url)
20
+ issue = client.issue(url.repo, url.number)
21
+ new number: url.number, repo: url.repo, attributes: issue
22
+ end
23
+
24
+ def self.find_by_short_link(short_link)
25
+ short_link = ParsedShortLink.new(short_link)
26
+ issue = client.issue(short_link.repo, short_link.number)
27
+ new number: short_link.number, repo: short_link.repo, attributes: issue
28
+ end
29
+
30
+ def title
31
+ attributes[:title]
32
+ end
33
+
34
+ def pr_title
35
+ title
36
+ end
37
+
38
+ def url
39
+ attributes[:html_url]
40
+ end
41
+
42
+ def summary
43
+ "Closes #{short_link}"
44
+ end
45
+
46
+ def start
47
+ # noop for now
48
+ end
49
+
50
+ def finish
51
+ # noop for now
52
+ end
53
+
54
+ def accept
55
+ # noop for now
56
+ end
57
+
58
+ def assign(user)
59
+ # add_assignes not released as gem yet https://github.com/octokit/octokit.rb/pull/894
60
+ client.post "#{Octokit::Repository.path repo}/issues/#{number}/assignees", assignees: [user.github_login]
61
+ end
62
+
63
+ def add_label(label)
64
+ self.class.request(:post, "cards/#{id}/idLabels", value: label.id)
65
+ end
66
+
67
+ def attach(url:)
68
+ self.class.request(:post, "cards/#{id}/attachments", url: url)
69
+ end
70
+
71
+ def short_link
72
+ "#{repo}##{number}"
73
+ end
74
+
75
+ def short_url
76
+ attributes[:shortUrl]
77
+ end
78
+
79
+ private
80
+
81
+ attr_reader :number, :repo, :attributes
82
+
83
+ def move_to(list)
84
+ self.class.with("cards/:id/idList").where(id: id, value: list.id).put
85
+ end
86
+
87
+ def member_ids
88
+ attributes["idMembers"] || []
89
+ end
90
+ end
91
+ end
92
+ end
@@ -0,0 +1,25 @@
1
+ module Cp8Cli
2
+ module Github
3
+ class ParsedShortLink
4
+ def initialize(short_link)
5
+ @short_link = short_link
6
+ end
7
+
8
+ def number
9
+ parts.last
10
+ end
11
+
12
+ def repo
13
+ parts.first
14
+ end
15
+
16
+ private
17
+
18
+ attr_accessor :short_link
19
+
20
+ def parts
21
+ short_link.split("#")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,25 @@
1
+ module Cp8Cli
2
+ module Github
3
+ class ParsedUrl
4
+ def initialize(url)
5
+ @url = url
6
+ end
7
+
8
+ def number
9
+ parts.last
10
+ end
11
+
12
+ def repo
13
+ parts[3, 2].join("/")
14
+ end
15
+
16
+ private
17
+
18
+ attr_accessor :url
19
+
20
+ def parts
21
+ url.split("/")
22
+ end
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,51 @@
1
+ require "cp8_cli/config_store"
2
+ require "cp8_cli/trello/base"
3
+
4
+ module Cp8Cli
5
+ class GlobalConfig
6
+ PATH = ENV["HOME"] + "/.trello_flow"
7
+
8
+ def initialize(store = nil)
9
+ @store = store || ConfigStore.new(PATH)
10
+ end
11
+
12
+ def trello_key
13
+ @_trello_key ||= store[:key] || configure_trello_key
14
+ end
15
+
16
+ def trello_token
17
+ @_trello_token ||= store[:token] || configure_trello_token
18
+ end
19
+
20
+ def github_token
21
+ @_github_token ||= store[:github_token] || env_github_token || configure_github_token
22
+ end
23
+
24
+ private
25
+
26
+ attr_reader :store
27
+
28
+ def configure_trello_key
29
+ Command.ask "Press enter to setup Trello for this project (will open public key url)"
30
+ Command.open_url "https://trello.com/app-key"
31
+ store.save :key, Command.ask("Input Developer API key")
32
+ end
33
+
34
+ def configure_trello_token
35
+ Command.open_url trello_authorize_url
36
+ store.save :token, Command.ask("Input member token")
37
+ end
38
+
39
+ def env_github_token
40
+ ENV["OCTOKIT_ACCESS_TOKEN"]
41
+ end
42
+
43
+ def configure_github_token
44
+ store.save :github_token, Command.ask("Input GitHub token")
45
+ end
46
+
47
+ def trello_authorize_url
48
+ "https://trello.com/1/authorize?key=#{trello_key}&name=Trello-Flow&scope=read,write,account&expiration=never&response_type=token"
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,29 @@
1
+ module Cp8Cli
2
+ class LocalConfig
3
+ PATH = ".trello_flow"
4
+
5
+ def initialize(store = nil)
6
+ @store = store || ConfigStore.new(PATH)
7
+ end
8
+
9
+ def board
10
+ @_board ||= Trello::Board.find(board_id)
11
+ end
12
+
13
+ private
14
+
15
+ attr_reader :store
16
+
17
+ def board_id
18
+ @_board_id ||= store[:board_id] || configure_board_id
19
+ end
20
+
21
+ def configure_board_id
22
+ store.save :board_id, Table.pick(trello_user.boards.active).id
23
+ end
24
+
25
+ def trello_user
26
+ Trello::Member.current
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,74 @@
1
+ require "cp8_cli/version"
2
+ require "cp8_cli/local_config"
3
+ require "cp8_cli/global_config"
4
+ require "cp8_cli/github/issue"
5
+ require "cp8_cli/current_user"
6
+
7
+ module Cp8Cli
8
+ class Main
9
+ def initialize(global_config = GlobalConfig.new, local_config = LocalConfig.new)
10
+ Trello::Base.configure(key: global_config.trello_key, token: global_config.trello_token)
11
+ Github::Base.configure(token: global_config.github_token)
12
+ @local_config = local_config
13
+ end
14
+
15
+ def start(name)
16
+ Command.error "Your `cp8_cli` version is out of date. Please run `gem update cp8_cli`." unless Version.latest?
17
+ story = create_or_pick_story(name)
18
+ story.assign(current_user)
19
+ story.start
20
+ Branch.from_story(user: current_user, story: story).checkout
21
+ rescue Trello::Error => error
22
+ Command.error(error.message)
23
+ end
24
+
25
+ def open
26
+ Branch.current.open_story_in_browser
27
+ end
28
+
29
+ def finish(options = {})
30
+ branch = Branch.current
31
+ branch.push
32
+ branch.open_pull_request(options)
33
+ end
34
+
35
+ def cleanup
36
+ Cleanup.new(Branch.current.target).run
37
+ end
38
+
39
+ private
40
+
41
+ attr_reader :local_config
42
+
43
+ def board
44
+ @_board ||= local_config.board
45
+ end
46
+
47
+ def create_or_pick_story(name)
48
+ if name.to_s.start_with?("https://github.com")
49
+ Github::Issue.find_by_url(name)
50
+ elsif name.to_s.start_with?("http")
51
+ Trello::Card.find_by_url(name)
52
+ elsif name.present?
53
+ create_new_card(name)
54
+ else
55
+ pick_existing_card
56
+ end
57
+ end
58
+
59
+ def create_new_card(name)
60
+ label = Table.pick board.labels, caption: "Add label:"
61
+ card = board.lists.backlog.cards.create name: name
62
+ card.add_label(label) if label
63
+ card
64
+ end
65
+
66
+ def pick_existing_card
67
+ Table.pick board.lists.backlog.cards
68
+ end
69
+
70
+ def current_user
71
+ @_current_user ||= CurrentUser.new
72
+ end
73
+ end
74
+ end
@@ -0,0 +1,63 @@
1
+ require "cp8_cli/repo"
2
+
3
+ module Cp8Cli
4
+ class PullRequest
5
+ def initialize(from:, target:, story: nil, **options)
6
+ @story = story
7
+ @from = from
8
+ @target = target
9
+ @options = options
10
+ end
11
+
12
+ def open
13
+ Command.open_url url
14
+ end
15
+
16
+ private
17
+
18
+ attr_reader :story, :from, :target, :options
19
+
20
+ def url
21
+ repo.url + "/compare/#{target}...#{escape from}?expand=1&title=#{escape title_with_prefixes}&body=#{escape body}"
22
+ end
23
+
24
+ def title
25
+ return unless story
26
+ story.pr_title
27
+ end
28
+
29
+ def body
30
+ return unless story
31
+ body = story.summary
32
+ body << release_note unless release_branch?
33
+ body
34
+ end
35
+
36
+ def release_note
37
+ "\n\n_Release note: #{story.title}_"
38
+ end
39
+
40
+ def prefixes
41
+ prefixes = []
42
+ prefixes << "[WIP]" if options[:wip]
43
+ prefixes << "[#{target.titleize}]" if release_branch?
44
+ prefixes.join(" ")
45
+ end
46
+
47
+ def release_branch?
48
+ target != "master"
49
+ end
50
+
51
+ def title_with_prefixes
52
+ "#{prefixes} #{title}".strip
53
+ end
54
+
55
+ def escape(text)
56
+ URI.escape text.to_s.strip
57
+ end
58
+
59
+ def repo
60
+ @_repo ||= Repo.new
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,21 @@
1
+ module Cp8Cli
2
+ class Repo
3
+ def user
4
+ path.split('/').first
5
+ end
6
+
7
+ def name
8
+ path.split('/').last
9
+ end
10
+
11
+ def url
12
+ "https://github.com/#{user}/#{name}"
13
+ end
14
+
15
+ private
16
+
17
+ def path
18
+ @_path ||= Command.read("git config --get remote.origin.url").match(/github.com[:\/](\S+\/\S+)\.git/)[1]
19
+ end
20
+ end
21
+ end