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 +4 -4
- data/CHANGELOG.md +18 -0
- data/Gemfile +0 -1
- data/Gemfile.lock +1 -47
- data/README.md +9 -4
- data/lib/geordi/commands/branch.rb +4 -4
- data/lib/geordi/commands/commit.rb +4 -4
- data/lib/geordi/commands/unit.rb +13 -1
- data/lib/geordi/commands/yarn_install.rb +1 -1
- data/lib/geordi/gitlinear.rb +188 -0
- data/lib/geordi/settings.rb +30 -76
- data/lib/geordi/version.rb +1 -1
- metadata +4 -4
- data/lib/geordi/gitpt.rb +0 -189
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 310a3633bce8944bb4c56987a2d12cf2ed69013ef978e91f48063acba801c634
|
4
|
+
data.tar.gz: f5e53286619679e6b5c3f0e2439e5c7589ed7c2e2866c0078b51f056658cc3dd
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
data/Gemfile.lock
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
PATH
|
2
2
|
remote: .
|
3
3
|
specs:
|
4
|
-
geordi (
|
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
|
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
|
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
|
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
|
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
|
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
|
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/
|
13
|
-
|
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
|
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
|
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/
|
13
|
-
|
12
|
+
require 'geordi/gitlinear'
|
13
|
+
Gitlinear.new.commit(git_args)
|
14
14
|
|
15
15
|
Hint.did_you_know [
|
16
16
|
:branch,
|
data/lib/geordi/commands/unit.rb
CHANGED
@@ -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
|
-
|
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?('
|
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
|
data/lib/geordi/settings.rb
CHANGED
@@ -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
|
-
|
17
|
-
|
15
|
+
linear_api_key
|
16
|
+
linear_team_ids
|
18
17
|
].freeze
|
19
18
|
|
20
|
-
ALLOWED_LOCAL_SETTINGS = %w[
|
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
|
34
|
-
@global_settings['
|
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
|
38
|
-
@global_settings['
|
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
|
65
|
-
|
66
|
-
|
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
|
-
|
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
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
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
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
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
|
-
|
161
|
-
|
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
|
-
|
132
|
+
team_ids
|
179
133
|
when String
|
180
|
-
|
134
|
+
team_ids.split(/[\s,;]+/)
|
181
135
|
when Integer
|
182
|
-
[
|
136
|
+
[team_ids]
|
183
137
|
else
|
184
138
|
[]
|
185
139
|
end
|
data/lib/geordi/version.rb
CHANGED
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:
|
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-
|
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/
|
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.
|
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
|