geordi 10.1.0 → 11.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 3324f1c6c2c99a458d355b5a83a915ff96c80a2307f7245780a254df5a2d03fe
4
- data.tar.gz: c8d1fdfd659292dfa1aaa7e2feb08ffdca7c4ce996b295637249b6f2d970e852
3
+ metadata.gz: 310a3633bce8944bb4c56987a2d12cf2ed69013ef978e91f48063acba801c634
4
+ data.tar.gz: f5e53286619679e6b5c3f0e2439e5c7589ed7c2e2866c0078b51f056658cc3dd
5
5
  SHA512:
6
- metadata.gz: 89e43ce2db60271bf2cbde52c6255c4331b64c719bfe7ffb28187976513b7efaeceb94da2d2cfbf2427162dd68cd84320cd9cf55e2b8f883f1669076826d7b87
7
- data.tar.gz: 5f27fa4ef40ef8c5f135151fb919fbe6626adad1885b96494e51a4e9137895d9b12c51256ff157158d2e1d2a8c87cd36a12526b7cfe9ab73b8ac23d7e37b9623
6
+ metadata.gz: 692cd04428cdbc217969425a9c3fcc1ea62f95f0a0692aabec218afdb1b804c1e705a181f387c6ba581c5e3dc25acdb5a0485565571c73d7f33d4521629ce300
7
+ data.tar.gz: 8f9c32e800de8532265c3b302bfd821c0a24295c55bfac166ddb1390a2a0e93fb15e6028d1a63526ec0fc8268b1a617687152d8a72055c75a0546f45fec292c4
data/CHANGELOG.md CHANGED
@@ -9,6 +9,24 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
9
9
 
10
10
  ### Breaking changes
11
11
 
12
+ ## 11.1.0 2024-11-20
13
+
14
+ ### Compatible changes
15
+
16
+ * Skip `yarn install` for other package managers:
17
+ * Before: Check for a `package.json`
18
+ * After: Check for a `yarn.lock`
19
+
20
+
21
+ ## 11.0.0 2024-11-13
22
+
23
+ ### Compatible changes
24
+ * `geordi unit` now supports `parallel_tests`, binstubs and `bundle exec`
25
+
26
+ ### Breaking changes
27
+ * `geordi branch` now checks out a feature branch based on a Linear issue instead of a story from Pivotal Tracker
28
+ * `geordi commit` now uses a Linear issue title instead of a story title from Pivotal Tracker
29
+
12
30
 
13
31
  # 10.1.0 2024-07-23
14
32
 
data/Gemfile CHANGED
@@ -10,5 +10,4 @@ gem 'highline'
10
10
  gem 'parallel_tests'
11
11
  gem 'launchy'
12
12
  gem 'pry-byebug'
13
- gem 'tracker_api'
14
13
  gem 'cucumber', '~> 4'
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- geordi (10.1.0)
4
+ geordi (11.1.0)
5
5
  thor (~> 1)
6
6
 
7
7
  GEM
@@ -22,17 +22,11 @@ GEM
22
22
  ffi (~> 1.9)
23
23
  rspec-expectations (>= 2.99)
24
24
  thor (>= 0.19, < 2.0)
25
- axiom-types (0.1.1)
26
- descendants_tracker (~> 0.0.4)
27
- ice_nine (~> 0.11.0)
28
- thread_safe (~> 0.3, >= 0.3.1)
29
25
  builder (3.2.4)
30
26
  byebug (11.1.3)
31
27
  childprocess (1.0.1)
32
28
  rake (< 13.0)
33
29
  coderay (1.1.3)
34
- coercible (1.0.0)
35
- descendants_tracker (~> 0.0.1)
36
30
  concurrent-ruby (1.1.9)
37
31
  contracts (0.16.0)
38
32
  cucumber (4.0.0)
@@ -62,36 +56,17 @@ GEM
62
56
  cucumber-core (~> 7.0, >= 7.0.0)
63
57
  cucumber-cucumber-expressions (~> 10.1, >= 10.1.0)
64
58
  cucumber-messages (~> 12.1, >= 12.1.1)
65
- declarative (0.0.10)
66
- declarative-option (0.1.0)
67
- descendants_tracker (0.0.4)
68
- thread_safe (~> 0.3, >= 0.3.1)
69
59
  diff-lcs (1.4.4)
70
- equalizer (0.0.11)
71
- excon (0.78.0)
72
- faraday (0.17.3)
73
- multipart-post (>= 1.2, < 3)
74
- faraday_middleware (0.14.0)
75
- faraday (>= 0.7.4, < 1.0)
76
60
  ffi (1.12.2)
77
61
  highline (2.0.3)
78
62
  i18n (1.8.10)
79
63
  concurrent-ruby (~> 1.0)
80
- ice_nine (0.11.2)
81
64
  launchy (2.4.3)
82
65
  addressable (~> 2.3)
83
66
  method_source (1.0.0)
84
67
  middleware (0.1.0)
85
- mimemagic (0.3.9)
86
- nokogiri (~> 1)
87
- rake
88
- mini_portile2 (2.4.0)
89
68
  minitest (5.14.4)
90
- multi_json (1.15.0)
91
69
  multi_test (0.1.2)
92
- multipart-post (2.1.1)
93
- nokogiri (1.9.1)
94
- mini_portile2 (~> 2.4.0)
95
70
  parallel (1.19.2)
96
71
  parallel_tests (2.32.0)
97
72
  parallel
@@ -108,10 +83,6 @@ GEM
108
83
  pry (~> 0.13.0)
109
84
  public_suffix (3.1.1)
110
85
  rake (12.3.3)
111
- representable (3.0.4)
112
- declarative (< 0.1.0)
113
- declarative-option (< 0.2.0)
114
- uber (< 0.2.0)
115
86
  rspec (3.10.0)
116
87
  rspec-core (~> 3.10.0)
117
88
  rspec-expectations (~> 3.10.0)
@@ -129,24 +100,8 @@ GEM
129
100
  ffi (~> 1.1)
130
101
  thor (1.0.1)
131
102
  thread_safe (0.3.6)
132
- tracker_api (1.11.0)
133
- addressable
134
- equalizer
135
- excon
136
- faraday
137
- faraday_middleware
138
- mimemagic
139
- multi_json
140
- representable
141
- virtus
142
103
  tzinfo (2.0.4)
143
104
  concurrent-ruby (~> 1.0)
144
- uber (0.1.0)
145
- virtus (1.0.5)
146
- axiom-types (~> 0.1)
147
- coercible (~> 1.0)
148
- descendants_tracker (~> 0.0, >= 0.0.3)
149
- equalizer (~> 0.0, >= 0.0.9)
150
105
  zeitwerk (2.4.2)
151
106
 
152
107
  PLATFORMS
@@ -162,7 +117,6 @@ DEPENDENCIES
162
117
  pry-byebug
163
118
  rake (< 13)
164
119
  rspec
165
- tracker_api
166
120
 
167
121
  BUNDLED WITH
168
122
  2.3.25
data/README.md CHANGED
@@ -24,11 +24,11 @@ Commands will occasionally print "did you know" hints of other Geordi features.
24
24
  You can always run `geordi help <command>` to quickly look up command help.
25
25
 
26
26
  ### `geordi branch`
27
- Check out a feature branch based on a story from Pivotal Tracker.
27
+ Check out a feature branch based on a Linear issue.
28
28
 
29
29
  Example: `geordi branch`
30
30
 
31
- On the first execution we ask for your Pivotal Tracker API token and for your Git user initials. Both will be
31
+ On the first execution we ask for your Linear API token. It will be
32
32
  stored in `~/.config/geordi/global.yml`.
33
33
 
34
34
  **Options**
@@ -59,13 +59,13 @@ Remove unneeded files from the current directory.
59
59
 
60
60
 
61
61
  ### `geordi commit`
62
- Commit using a story title from Pivotal Tracker.
62
+ Commit using an issue title from Linear.
63
63
 
64
64
  Example: `geordi commit`
65
65
 
66
66
  Any extra arguments are forwarded to `git commit -m <message>`.
67
67
 
68
- On the first execution we ask for your Pivotal Tracker API token. It will be
68
+ On the first execution we ask for your Linear API token. It will be
69
69
  stored in `~/.config/geordi/global.yml`.
70
70
 
71
71
 
@@ -343,6 +343,11 @@ All rspec specs and cucumber features matching the given paths will be run.
343
343
  ### `geordi unit`
344
344
  Run Test::Unit.
345
345
 
346
+ Supports `parallel_tests`, binstubs and `bundle exec`.
347
+
348
+ In order to limit processes in a parallel run, you can set an environment
349
+ variable like this: `PARALLEL_TEST_PROCESSORS=6 geordi unit`
350
+
346
351
 
347
352
  ### `geordi update`
348
353
  Bring a project up to date.
@@ -1,16 +1,16 @@
1
- desc 'branch', 'Check out a feature branch based on a story from Pivotal Tracker'
1
+ desc 'branch', 'Check out a feature branch based on a Linear issue'
2
2
  long_desc <<-LONGDESC
3
3
  Example: `geordi branch`
4
4
 
5
- On the first execution we ask for your Pivotal Tracker API token and for your Git user initials. Both will be
5
+ On the first execution we ask for your Linear API token. It will be
6
6
  stored in `~/.config/geordi/global.yml`.
7
7
  LONGDESC
8
8
 
9
9
  option :from_master, aliases: '-m', type: :boolean, desc: 'Branch from master instead of the current branch'
10
10
 
11
11
  def branch
12
- require 'geordi/gitpt'
13
- Gitpt.new.run_branch(from_master: options.from_master)
12
+ require 'geordi/gitlinear'
13
+ Gitlinear.new.branch(from_master: options.from_master)
14
14
 
15
15
  Hint.did_you_know [
16
16
  :commit,
@@ -1,16 +1,16 @@
1
- desc 'commit', 'Commit using a story title from Pivotal Tracker'
1
+ desc 'commit', 'Commit using an issue title from Linear'
2
2
  long_desc <<-LONGDESC
3
3
  Example: `geordi commit`
4
4
 
5
5
  Any extra arguments are forwarded to `git commit -m <message>`.
6
6
 
7
- On the first execution we ask for your Pivotal Tracker API token. It will be
7
+ On the first execution we ask for your Linear API token. It will be
8
8
  stored in `~/.config/geordi/global.yml`.
9
9
  LONGDESC
10
10
 
11
11
  def commit(*git_args)
12
- require 'geordi/gitpt'
13
- Gitpt.new.run_commit(git_args)
12
+ require 'geordi/gitlinear'
13
+ Gitlinear.new.commit(git_args)
14
14
 
15
15
  Hint.did_you_know [
16
16
  :branch,
@@ -1,11 +1,23 @@
1
1
  desc 'unit', 'Run Test::Unit'
2
+ long_desc <<-LONGDESC
3
+ Supports `parallel_tests`, binstubs and `bundle exec`.
4
+
5
+ In order to limit processes in a parallel run, you can set an environment
6
+ variable like this: `PARALLEL_TEST_PROCESSORS=6 geordi unit`
7
+ LONGDESC
2
8
  def unit
3
9
  if File.exist?('test/test_helper.rb')
4
10
  invoke_geordi 'bundle_install'
5
11
  invoke_geordi 'yarn_install'
6
12
 
7
13
  Interaction.announce 'Running Test::Unit'
8
- Util.run!([Util.binstub_or_fallback('rake'), 'test'])
14
+
15
+ if Util.file_containing?('Gemfile', /parallel_tests/)
16
+ Interaction.note 'All unit tests at once (using parallel_tests)'
17
+ Util.run!([Util.binstub_or_fallback('rake'), 'parallel:test'], fail_message: 'Test::Unit failed.')
18
+ else
19
+ Util.run!([Util.binstub_or_fallback('rake'), 'test'], fail_message: 'Test::Unit failed.')
20
+ end
9
21
  else
10
22
  Interaction.note 'Test::Unit not employed.'
11
23
  end
@@ -1,7 +1,7 @@
1
1
  desc 'yarn-install', 'Runs yarn install if required', hide: true
2
2
 
3
3
  def yarn_install
4
- if File.exist?('package.json') && !system('yarn check --integrity > /dev/null 2>&1')
4
+ if File.exist?('yarn.lock') && !system('yarn check --integrity > /dev/null 2>&1')
5
5
  Interaction.announce 'Yarn install'
6
6
  Util.run!('yarn install')
7
7
  end
@@ -0,0 +1,188 @@
1
+ require 'highline'
2
+ require 'net/http'
3
+ require 'json'
4
+
5
+ module Geordi
6
+ class Gitlinear
7
+ # This require-style is to prevent Ruby from loading files of a different
8
+ # version of Geordi.
9
+ require File.expand_path('settings', __dir__)
10
+
11
+ API_ENDPOINT = 'https://api.linear.app/graphql'.freeze
12
+
13
+ def initialize
14
+ self.highline = HighLine.new
15
+ self.settings = Settings.new
16
+ end
17
+
18
+ def commit(git_args)
19
+ Interaction.warn <<~WARNING unless Util.staged_changes?
20
+ No staged changes. Will create an empty commit.
21
+ WARNING
22
+
23
+ issue = choose_issue
24
+ create_commit "[#{issue['identifier']}] #{issue['title']}", "Issue: #{issue['url']}", *git_args
25
+ end
26
+
27
+ def branch(from_master: false)
28
+ issue = choose_issue
29
+
30
+ local_branches = local_branch_names
31
+ matching_local_branch = local_branches.find { |branch_name| branch_name == issue['branchName'] }
32
+ matching_local_branch ||= local_branches.find { |branch_name| branch_name.include? issue['identifier'].to_s }
33
+
34
+ if matching_local_branch.nil?
35
+ Util.run! ['git', 'checkout', 'master'] if from_master
36
+ Util.run! ['git', 'checkout', '-b', issue['branchName']]
37
+ else
38
+ Util.run! ['git', 'checkout', matching_local_branch]
39
+ end
40
+ end
41
+
42
+ private
43
+
44
+ attr_accessor :highline, :settings
45
+
46
+ def local_branch_names
47
+ @local_branch_names ||= begin
48
+ branch_list_string = if Util.testing?
49
+ ENV['GEORDI_TESTING_GIT_BRANCHES'].to_s
50
+ else
51
+ `git branch --format="%(refname:short)"`
52
+ end
53
+
54
+ if branch_list_string.strip.empty?
55
+ Interaction.fail 'Could not determine local Git branches.'
56
+ end
57
+
58
+ branch_list_string.split("\n")
59
+ end
60
+ end
61
+
62
+ def choose_issue
63
+ if Util.testing?
64
+ return dummy_issue_for_testing
65
+ end
66
+
67
+ loading_message = 'Connecting to Linear ...'
68
+ print(loading_message)
69
+ issues = fetch_linear_issues
70
+ reset_loading_message = "\r#{' ' * (loading_message.length + issues.length)}\r"
71
+
72
+ if issues.empty?
73
+ print reset_loading_message
74
+ Geordi::Interaction.fail('No issues to offer.')
75
+ end
76
+
77
+ highline.choose do |menu|
78
+ menu.header = 'Choose an issue'
79
+
80
+ issues.each do |issue|
81
+ state = issue['state']['name']
82
+ if issue['assignee']
83
+ assignee = issue['assignee']['name']
84
+ assignee_is_me = issue['assignee']['isMe']
85
+ else
86
+ assignee = "unassigned"
87
+ assignee_is_me = false
88
+ end
89
+
90
+ state += HighLine::BOLD if assignee_is_me
91
+
92
+ label = "(#{assignee}, #{state}) #{issue['title']}"
93
+ label = bold(label) if assignee_is_me
94
+
95
+ menu.choice(label) { return issue }
96
+ end
97
+
98
+ menu.hidden('') { Interaction.fail('No issue selected.') }
99
+ print reset_loading_message # Once menu is build
100
+ end
101
+
102
+ nil # Return nothing
103
+ end
104
+
105
+ def dummy_issue_for_testing
106
+ settings.linear_api_key
107
+ ENV['GEORDI_TESTING_NO_LINEAR_ISSUES'] == 'true' ? Geordi::Interaction.fail('No issues to offer.') : {
108
+ 'identifier' => 'team-123',
109
+ 'title' => 'Test Issue',
110
+ 'url' => 'https://www.issue-url.com',
111
+ 'branchName' => 'testuser/team-123-test-issue',
112
+ 'assignee' => { 'name' => 'Test User', 'isMe' => true },
113
+ 'state' => { 'name' => 'In Progress' }
114
+ }
115
+ end
116
+
117
+ def create_commit(title, description, *git_args)
118
+ extra = highline.ask("\nAdd an optional message").strip
119
+ title << ' - ' << extra if extra != ''
120
+ Util.run!(['git', 'commit', '--allow-empty', '-m', title, '-m', description, *git_args])
121
+ end
122
+
123
+ def fetch_linear_issues
124
+ team_ids = settings.linear_team_ids
125
+ filter = {
126
+ "team": {
127
+ "id": {
128
+ "in": team_ids,
129
+ }
130
+ },
131
+ "state": {
132
+ "type": {
133
+ "eq": "started"
134
+ }
135
+ }
136
+ }
137
+ response = query_api(<<~GRAPHQL, filter: filter)
138
+ query Issues($filter: IssueFilter) {
139
+ issues(filter: $filter) {
140
+ nodes {
141
+ title
142
+ identifier
143
+ url
144
+ branchName
145
+ assignee {
146
+ name
147
+ isMe
148
+ }
149
+ state {
150
+ name
151
+ }
152
+ }
153
+ }
154
+ }
155
+ GRAPHQL
156
+
157
+ response.dig(*%w[issues nodes])
158
+ end
159
+
160
+ def query_api(attributes, variables)
161
+ uri = URI(API_ENDPOINT)
162
+
163
+ https = Net::HTTP.new(uri.host, uri.port)
164
+ https.use_ssl = true
165
+
166
+ query = [{ query: attributes.split.join(' '), variables: variables }].to_json
167
+
168
+ request = Net::HTTP::Post.new(uri.path)
169
+ request.body = query
170
+
171
+ request['Content-Type'] = 'application/json'
172
+ request['Authorization'] = settings.linear_api_key
173
+
174
+ response = https.request(request)
175
+
176
+ parsed_response = JSON.parse(response.body)[0]
177
+ if parsed_response.key?('errors')
178
+ raise parsed_response.dig('errors')
179
+ else
180
+ parsed_response['data']
181
+ end
182
+ end
183
+
184
+ def bold(string)
185
+ HighLine::BOLD + string + HighLine::RESET
186
+ end
187
+ end
188
+ end
@@ -10,14 +10,13 @@ module Geordi
10
10
 
11
11
  ALLOWED_GLOBAL_SETTINGS = %w[
12
12
  auto_update_chromedriver
13
- git_initials
14
13
  hint_probability
15
14
  irb_flags
16
- pivotal_tracker_api_key
17
- pivotal_tracker_project_ids
15
+ linear_api_key
16
+ linear_team_ids
18
17
  ].freeze
19
18
 
20
- ALLOWED_LOCAL_SETTINGS = %w[ pivotal_tracker_project_ids ].freeze
19
+ ALLOWED_LOCAL_SETTINGS = %w[ linear_team_ids ].freeze
21
20
 
22
21
  SETTINGS_WARNED = 'GEORDI_INVALID_SETTINGS_WARNED'
23
22
 
@@ -30,12 +29,15 @@ module Geordi
30
29
  @global_settings['irb_flags']
31
30
  end
32
31
 
33
- def pivotal_tracker_api_key
34
- @global_settings['pivotal_tracker_api_key'] || gitpt_api_key_old || inquire_pt_api_key
32
+ def linear_api_key
33
+ @global_settings['linear_api_key'] || begin
34
+ Interaction.warn 'Linear API key not found'
35
+ inquire_linear_api_key
36
+ end
35
37
  end
36
38
 
37
- def pivotal_tracker_api_key=(value)
38
- @global_settings['pivotal_tracker_api_key'] = value
39
+ def linear_api_key=(value)
40
+ @global_settings['linear_api_key'] = value
39
41
  save_global_settings
40
42
  end
41
43
 
@@ -43,15 +45,6 @@ module Geordi
43
45
  @global_settings['hint_probability']
44
46
  end
45
47
 
46
- def git_initials
47
- @global_settings['git_initials']
48
- end
49
-
50
- def git_initials=(value)
51
- @global_settings['git_initials'] = value
52
- save_global_settings
53
- end
54
-
55
48
  def auto_update_chromedriver
56
49
  @global_settings["auto_update_chromedriver"] || false
57
50
  end
@@ -61,30 +54,21 @@ module Geordi
61
54
  save_global_settings
62
55
  end
63
56
 
64
- def pivotal_tracker_project_ids
65
- local_project_ids = @local_settings['pivotal_tracker_project_ids'] || pt_project_ids_old
66
- global_project_ids = @global_settings['pivotal_tracker_project_ids']
67
-
68
- local_project_ids = array_wrap_project_ids(local_project_ids)
69
- global_project_ids = array_wrap_project_ids(global_project_ids)
70
-
71
- project_ids = local_project_ids | global_project_ids
57
+ def linear_team_ids
58
+ local_team_ids = normalize_team_ids(@local_settings['linear_team_ids'])
59
+ global_team_ids = normalize_team_ids(@global_settings['linear_team_ids'])
72
60
 
73
- if project_ids.empty?
74
- puts
75
- Geordi::Interaction.warn "Sorry, I could not find a project ID in .geordi.yml :("
76
- puts
61
+ team_ids = local_team_ids | global_team_ids
77
62
 
78
- puts "Please put at least one Pivotal Tracker project id into the .geordi.yml file in this directory, e.g."
79
- puts
80
- puts "pivotal_tracker_project_ids:"
81
- puts "- 123456"
82
- puts
83
- puts 'You may add multiple IDs.'
63
+ if team_ids.empty?
64
+ Geordi::Interaction.warn 'No team id found.'
65
+ Interaction.note 'Please open a team in Linear, open the command menu with CTRL + K and choose'
66
+ Interaction.note "\"Copy model UUID\". Store that team id in #{LOCAL_SETTINGS_FILE_NAME}:"
67
+ puts 'linear_team_ids: abc-123-123-abc, def-456-456-def'
84
68
  exit 1
85
69
  end
86
70
 
87
- project_ids
71
+ team_ids
88
72
  end
89
73
 
90
74
  private
@@ -132,54 +116,24 @@ module Geordi
132
116
  end
133
117
  end
134
118
 
135
- # deprecated
136
- def gitpt_api_key_old
137
- file_path = File.join(ENV['HOME'], '.gitpt')
138
- if File.exist?(file_path) && !Util.testing?
139
- token = YAML.load_file(file_path).fetch :token
140
- self.pivotal_tracker_api_key = token
141
-
142
- Geordi::Interaction.warn "The ~/.gitpt file is deprecated."
143
- Geordi::Interaction.note "The contained setting has been moved to ~/.config/geordi/global.yml."
144
- Geordi::Interaction.note "If you don't need to work with an older version of geordi you can delete ~/.gitpt now."
145
-
146
- token
147
- end
148
- end
149
-
150
- def inquire_pt_api_key
151
- Geordi::Interaction.warn 'Your settings are missing or invalid.'
152
- Geordi::Interaction.warn "Please configure your Pivotal Tracker access."
153
- token = Geordi::Interaction.prompt('Your API key:').to_s # Just be sure
154
- self.pivotal_tracker_api_key = token
119
+ def inquire_linear_api_key
120
+ Geordi::Interaction.note 'Create an API key here: https://linear.app/makandra/settings/api'
121
+ token = Geordi::Interaction.prompt("Please enter the API key:")
122
+ self.linear_api_key = token
123
+ Interaction.note("API key stored in #{GLOBAL_SETTINGS_FILE_NAME}.")
155
124
  puts
156
125
 
157
126
  token
158
127
  end
159
128
 
160
- # deprecated
161
- def pt_project_ids_old
162
- if File.exist?('.pt_project_id')
163
- project_ids = File.read('.pt_project_id')
164
- puts # Make sure to start on a new line (e.g. when invoked after a #print)
165
- Geordi::Interaction.warn "The usage of the .pt_project_id file is deprecated."
166
- Geordi::Interaction.note(<<~INSTRUCTIONS)
167
- Please remove this file from your project and add or extend the .geordi.yml file with the following content:
168
- pivotal_tracker_project_ids: #{project_ids}
169
- INSTRUCTIONS
170
-
171
- project_ids
172
- end
173
- end
174
-
175
- def array_wrap_project_ids(project_ids)
176
- case project_ids
129
+ def normalize_team_ids(team_ids)
130
+ case team_ids
177
131
  when Array
178
- project_ids
132
+ team_ids
179
133
  when String
180
- project_ids.split(/[\s]+/).map(&:to_i)
134
+ team_ids.split(/[\s,;]+/)
181
135
  when Integer
182
- [project_ids]
136
+ [team_ids]
183
137
  else
184
138
  []
185
139
  end
@@ -1,3 +1,3 @@
1
1
  module Geordi
2
- VERSION = '10.1.0'.freeze
2
+ VERSION = '11.1.0'.freeze
3
3
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: geordi
3
3
  version: !ruby/object:Gem::Version
4
- version: 10.1.0
4
+ version: 11.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2024-07-23 00:00:00.000000000 Z
11
+ date: 2024-11-20 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -87,7 +87,7 @@ files:
87
87
  - lib/geordi/cucumber.rb
88
88
  - lib/geordi/db_cleaner.rb
89
89
  - lib/geordi/dump_loader.rb
90
- - lib/geordi/gitpt.rb
90
+ - lib/geordi/gitlinear.rb
91
91
  - lib/geordi/hint.rb
92
92
  - lib/geordi/interaction.rb
93
93
  - lib/geordi/remote.rb
@@ -117,7 +117,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
117
117
  - !ruby/object:Gem::Version
118
118
  version: '0'
119
119
  requirements: []
120
- rubygems_version: 3.2.3
120
+ rubygems_version: 3.4.10
121
121
  signing_key:
122
122
  specification_version: 4
123
123
  summary: Collection of command line tools we use in our daily work with Ruby, Rails
data/lib/geordi/gitpt.rb DELETED
@@ -1,189 +0,0 @@
1
- require 'yaml'
2
- require 'highline'
3
- require 'tracker_api'
4
-
5
- module Geordi
6
- class Gitpt
7
-
8
- # This require-style is to prevent Ruby from loading files of a different
9
- # version of Geordi.
10
- require File.expand_path('settings', __dir__)
11
-
12
- def initialize
13
- self.highline = HighLine.new
14
- self.settings = Settings.new
15
- self.client = build_client
16
- end
17
-
18
- def run_commit(git_args)
19
- Interaction.warn <<~WARNING unless Util.staged_changes?
20
- No staged changes. Will create an empty commit.
21
- WARNING
22
-
23
- story = choose_story
24
- if story
25
- create_commit "[##{story.id}] #{story.name}", "Story: #{story.url}", *git_args
26
- end
27
- end
28
-
29
- def run_branch(from_master: false)
30
- story = choose_story || Interaction.fail('No story selected.')
31
-
32
- normalized_story_name = normalize_string(story.name)
33
-
34
- branch_list_string = if Util.testing?
35
- ENV['GEORDI_TESTING_GIT_BRANCHES'] || ''
36
- else
37
- `git branch --format="%(refname:short)"`
38
- end
39
-
40
- if branch_list_string.nil? || branch_list_string.strip.empty?
41
- Interaction.fail 'Could not determine local git branches.'
42
- end
43
-
44
- new_branch_name = "#{git_user_initials}/#{normalized_story_name}-#{story.id}"
45
-
46
- local_branches = branch_list_string.split("\n")
47
- branch_name = local_branches.find { |branch_name| branch_name == new_branch_name }
48
- branch_name ||= local_branches.find { |branch_name| branch_name.include? story.id.to_s }
49
-
50
- if branch_name.present?
51
- checkout_branch branch_name, new_branch: false
52
- else
53
- checkout_branch new_branch_name, new_branch: true, from_master: from_master
54
- end
55
- end
56
-
57
- private
58
-
59
- attr_accessor :highline, :client, :settings
60
-
61
- def build_client
62
- TrackerApi::Client.new(token: settings.pivotal_tracker_api_key)
63
- end
64
-
65
- def load_projects
66
- project_ids = settings.pivotal_tracker_project_ids
67
- project_ids.collect do |project_id|
68
- begin
69
- client.project(project_id)
70
- rescue TrackerApi::Errors::ClientError
71
- puts # Start a new line
72
- Geordi::Interaction.warn "Could not access project #{project_id}. Skipping."
73
- end
74
- end.compact
75
- end
76
-
77
- def applicable_stories
78
- if Util.testing?
79
- return ENV['GEORDI_TESTING_NO_PT_STORIES'] == 'true' ? [] : [OpenStruct.new(id: 12, name: 'Test Story', url: 'https://www.story-url.com')]
80
- end
81
-
82
- projects = load_projects
83
- projects.collect do |project|
84
- project.stories(filter: 'state:started,finished,rejected', fields: ':default,owners(id,name)')
85
- end.flatten
86
- end
87
-
88
- def choose_story
89
- loading_message = 'Connecting to Pivotal Tracker ...'
90
- print(loading_message)
91
- stories = applicable_stories
92
- reset_loading_message = "\r#{' ' * (loading_message.length + stories.length)}\r"
93
-
94
- Geordi::Interaction.fail('No stories to offer.') if stories.empty?
95
-
96
- if Util.testing?
97
- return stories[0]
98
- end
99
-
100
- my_id = client.me.id
101
-
102
- highline.choose do |menu|
103
- menu.header = 'Choose a story'
104
-
105
- stories.each do |story|
106
- state = story.current_state
107
- owners = story.owners
108
- owner_is_me = owners.collect(&:id).include?(my_id)
109
-
110
- if state == 'started'
111
- state = HighLine::GREEN + state + HighLine::RESET
112
- elsif state != 'finished'
113
- state = HighLine::RED + state + HighLine::RESET
114
- end
115
-
116
- state += HighLine::BOLD if owner_is_me
117
-
118
- label = "(#{owners.collect(&:name).join(', ')}, #{state}) #{story.name}"
119
- label = bold(label) if owner_is_me
120
-
121
- menu.choice(label) { return story }
122
- end
123
-
124
- menu.hidden ''
125
- print reset_loading_message # Once menu is build
126
- end
127
-
128
- nil # Return nothing
129
- end
130
-
131
- def create_commit(title, description, *git_args)
132
- extra = highline.ask("\nAdd an optional message").strip
133
- title << ' - ' << extra if extra != ''
134
-
135
- Util.run!(['git', 'commit', '--allow-empty', '-m', title, '-m', description, *git_args])
136
- end
137
-
138
- def bold(string)
139
- HighLine::BOLD + string + HighLine::RESET
140
- end
141
-
142
- def checkout_branch(name, new_branch: false, from_master: false)
143
- if new_branch
144
- Util.run! ['git', 'checkout', 'master'] if from_master
145
- Util.run! ['git', 'checkout', '-b', name]
146
- else
147
- Util.run! ['git', 'checkout', name]
148
- end
149
- end
150
-
151
- def normalize_string(name)
152
- name.gsub!('ä', 'ae')
153
- name.gsub!('ö', 'oe')
154
- name.gsub!('ü', 'ue')
155
- name.gsub!('ß', 'ss')
156
- name.tr!('^A-Za-z0-9_ ', '')
157
- name.squeeze! ' '
158
- name.gsub!(' ', '-')
159
- name.downcase!
160
- name
161
- end
162
-
163
- def git_user_initials
164
- if settings.git_initials
165
- Interaction.note "Using Git user initials from #{Settings::GLOBAL_SETTINGS_FILE_NAME}"
166
- return settings.git_initials
167
- end
168
-
169
- stdout_str = if Util.testing?
170
- ENV['GEORDI_TESTING_GIT_USERNAME']
171
- else
172
- `git config user.name`
173
- end
174
-
175
- git_user_initials = unless stdout_str.nil?
176
- stdout_str.strip.split(' ').map(&:chars).map(&:first).join.downcase
177
- end
178
-
179
- git_user_initials = Interaction.prompt 'Enter your initials:', git_user_initials
180
-
181
- if git_user_initials.nil?
182
- Interaction.fail('Could not determine the git user\'s initials.')
183
- else
184
- settings.git_initials = git_user_initials
185
- git_user_initials
186
- end
187
- end
188
- end
189
- end