geordi 12.3.0 → 12.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 18e63ef1b21ad222583eaf1380a27721e3c83eb839feee455bc8ef3502a18673
4
- data.tar.gz: 9690d680031cba8969b7b8855785e02a8d9c4bb6a903a069d3dd6339d56a3426
3
+ metadata.gz: 2e8dcb193d83d352bffb4f62670c93732de779a9471e51f942a0c20ddf19b89b
4
+ data.tar.gz: 152f889f12fe2cfdeea9b1457bc1b6c9d083169d3400a1189588470fc061f4b2
5
5
  SHA512:
6
- metadata.gz: b5702571bcf187e068f3f51313f417c9ea9fbb3b0d09b5420a7ea4345d3cb465c64a67fb9ba44704264b8a9e15ad95639d34e42c54d2d2e68e5de269b8978ff4
7
- data.tar.gz: 1d7aa3890b35f011b6efb501c7c0c49f5f99947499e5ca5767c569bece5bb6389358ddacd27d21d60514b3e5fc900b5d623cd41c9d8a6947de6490461804113e
6
+ metadata.gz: 04732b8f09ba0f7eba641fe8fa5cbff9ea625f311ce42db3b588345a7565b4631d4e67e81420b3b4f357afc98fff3021a458cdb08a03daf15ea93110512e444d
7
+ data.tar.gz: '02938cfa637e316e97b70666a7615696ccc1d881711c8823954529fc109af32ef16f98f132938ce1e04e49c0f007b1e60563e725669aff040eb504e207b7dc5f'
data/CHANGELOG.md CHANGED
@@ -10,6 +10,18 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
10
10
  ### Breaking changes
11
11
 
12
12
 
13
+ ## 12.5.0 2025-09-09
14
+
15
+ ### Compatible changes
16
+ * `geordi deploy` will now offer to move deployed issues to a new state if linear_team_ids are configured.
17
+
18
+
19
+ ## 12.4.0 2025-08-29
20
+
21
+ ### Compatible changes
22
+ + `geordi console` will now also take irb_flags from the local config file into account. These will have precedence over the global config file and flags automatically set by geordi.
23
+
24
+
13
25
  ## 12.3.0 2025-08-04
14
26
 
15
27
  ### Compatible changes
@@ -19,7 +31,7 @@ This project adheres to [Semantic Versioning](http://semver.org/spec/v2.0.0.html
19
31
  ## 12.2.0 2025-07-03
20
32
 
21
33
  ### Compatible changes
22
- * `geordi deploy` will now respect the configured deploy branch from teh Capistrano config. Previously, it would choose "production" for a deploy to the production stage, and master/main else.
34
+ * `geordi deploy` will now respect the configured deploy branch from the Capistrano config. Previously, it would choose "production" for a deploy to the production stage, and master/main else.
23
35
 
24
36
 
25
37
  ## 12.1.0 2025-05-03
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- geordi (12.3.0)
4
+ geordi (12.5.0)
5
5
  highline
6
6
  thor (~> 1)
7
7
 
data/README.md CHANGED
@@ -95,7 +95,12 @@ Remote: `geordi console staging`
95
95
  Selecting the server: `geordi console staging -s` shows a menu with all available
96
96
  servers. When passed a number, directly connects to the selected server.
97
97
 
98
- IRB flags can be given as `irb_flags: '...'` in the global Geordi config file (`~/.config/geordi/global.yml`).
98
+ IRB flags can be given as `irb_flags: '...'` in the global or local Geordi config file
99
+ (`~/.config/geordi/global.yml` / `./.geordi.yml`). If you define irb_flags in both files, the local config file will be
100
+ used. For IRB >=1.2 in combination with Ruby <3 geordi automatically sets the `--nomultiline` flag, to prevent slow
101
+ pasting. You can override this behavior by setting `--multiline` in the global config file or by defining `irb_flags`
102
+ in the local config file. The latter will always turn off the automatic behavior, even if you don't set any values for
103
+ the irb_flags key.
99
104
 
100
105
  **Options**
101
106
  - `-s, --select-server=[SERVER_NUMBER]`: Select a server to connect to
@@ -165,6 +170,8 @@ Finds available Capistrano stages by their prefix, e.g. `geordi deploy p` will
165
170
  deploy production, `geordi deploy mak` will deploy a `makandra` stage if there
166
171
  is a file config/deploy/makandra.rb.
167
172
 
173
+ If Linear team ids are configured (see `geordi commit`), will offer to move deployed issues to a new state. Disable with "skip".
174
+
168
175
  When your project is running Capistrano 3, deployment will use `cap deploy`
169
176
  instead of `cap deploy:migrations`. You can force using `deploy` by passing the
170
177
  -M option: `geordi deploy -M staging`.
@@ -9,8 +9,22 @@ LONGDESC
9
9
  option :from_master, aliases: %w[-m --from-main], type: :boolean, desc: 'Branch from master instead of the current branch'
10
10
 
11
11
  def branch
12
- require 'geordi/gitlinear'
13
- Gitlinear.new.branch(from_master: options.from_master)
12
+ require 'geordi/linear_client'
13
+ require 'geordi/git'
14
+
15
+ issue = LinearClient.new.choose_issue
16
+
17
+ local_branches = Git.local_branch_names
18
+ matching_local_branch = local_branches.find { |branch_name| branch_name == issue['branchName'] }
19
+ matching_local_branch ||= local_branches.find { |branch_name| branch_name.include? issue['identifier'].to_s }
20
+
21
+ if matching_local_branch
22
+ Util.run! ['git', 'checkout', matching_local_branch]
23
+ else
24
+ default_branch = Git.default_branch
25
+ Util.run! ['git', 'checkout', default_branch] if options.from_master
26
+ Util.run! ['git', 'checkout', '-b', issue['branchName']]
27
+ end
14
28
 
15
29
  Hint.did_you_know [
16
30
  :commit,
@@ -9,8 +9,23 @@ stored in `~/.config/geordi/global.yml`.
9
9
  LONGDESC
10
10
 
11
11
  def commit(*git_args)
12
- require 'geordi/gitlinear'
13
- Gitlinear.new.commit(git_args)
12
+ require 'geordi/linear_client'
13
+ require 'geordi/git'
14
+ require 'highline'
15
+
16
+ Interaction.warn <<~WARNING unless Git.staged_changes?
17
+ No staged changes. Will create an empty commit.
18
+ WARNING
19
+
20
+ linear_client = LinearClient.new
21
+ highline = HighLine.new
22
+
23
+ issue = linear_client.issue_from_branch || linear_client.choose_issue
24
+ title = "[#{issue['identifier']}] #{issue['title']}"
25
+ description = "Issue: #{issue['url']}"
26
+ extra = highline.ask("\nAdd an optional message").strip
27
+ title << ' - ' << extra if extra != ''
28
+ Util.run!(['git', 'commit', '--allow-empty', '-m', title, '-m', description, *git_args])
14
29
 
15
30
  Hint.did_you_know [
16
31
  :branch,
@@ -7,7 +7,13 @@ Remote: `geordi console staging`
7
7
  Selecting the server: `geordi console staging -s` shows a menu with all available
8
8
  servers. When passed a number, directly connects to the selected server.
9
9
 
10
- IRB flags can be given as `irb_flags: '...'` in the global Geordi config file (`~/.config/geordi/global.yml`).
10
+ IRB flags can be given as `irb_flags: '...'` in the global or local Geordi config file
11
+ (`~/.config/geordi/global.yml` / `./.geordi.yml`). If you define irb_flags in both files, the local config file will be
12
+ used. For IRB >=1.2 in combination with Ruby <3 geordi automatically sets the `--nomultiline` flag, to prevent slow
13
+ pasting. You can override this behavior by setting `--multiline` in the global config file or by defining `irb_flags`
14
+ in the local config file. The latter will always turn off the automatic behavior, even if you don't set any values for
15
+ the irb_flags key.
16
+
11
17
  LONGDESC
12
18
 
13
19
  # This option is duplicated in shelll.rb
@@ -28,6 +28,8 @@ Finds available Capistrano stages by their prefix, e.g. `geordi deploy p` will
28
28
  deploy production, `geordi deploy mak` will deploy a `makandra` stage if there
29
29
  is a file config/deploy/makandra.rb.
30
30
 
31
+ If Linear team ids are configured (see `geordi commit`), will offer to move deployed issues to a new state. Disable with "skip".
32
+
31
33
  When your project is running Capistrano 3, deployment will use `cap deploy`
32
34
  instead of `cap deploy:migrations`. You can force using `deploy` by passing the
33
35
  -M option: `geordi deploy -M staging`.
@@ -39,6 +41,12 @@ option :current_branch, aliases: '-c', type: :boolean,
39
41
  desc: 'Set DEPLOY_BRANCH to the current branch during deploy'
40
42
 
41
43
  def deploy(target_stage = nil)
44
+ require 'geordi/git'
45
+ require 'geordi/linear_client'
46
+
47
+ settings = Settings.new
48
+ linear_client = LinearClient.new
49
+
42
50
  # Set/Infer default values
43
51
  branch_stage_map = { 'master' => 'staging', 'main' => 'staging', 'production' => 'production' }
44
52
  if target_stage && !Util.deploy_targets.include?(target_stage)
@@ -48,7 +56,7 @@ def deploy(target_stage = nil)
48
56
  end
49
57
 
50
58
  # Ask for required information
51
- target_stage ||= Interaction.prompt 'Deployment stage:', branch_stage_map.fetch(Util.current_branch, 'staging')
59
+ target_stage ||= Interaction.prompt 'Deployment stage:', branch_stage_map.fetch(Git.current_branch, 'staging')
52
60
  capistrano_config = CapistranoConfig.new(target_stage)
53
61
 
54
62
  if options.current_branch
@@ -60,18 +68,25 @@ def deploy(target_stage = nil)
60
68
  set :branch, ENV['DEPLOY_BRANCH'] || 'master'
61
69
  ERROR
62
70
 
63
- source_branch = target_branch = Util.current_branch
71
+ source_branch = target_branch = Git.current_branch
64
72
  else # Normal deploy
65
- source_branch = Interaction.prompt 'Source branch:', Util.current_branch
73
+ source_branch = Interaction.prompt 'Source branch:', Git.current_branch
66
74
 
67
75
  deploy_branch = capistrano_config.branch
68
- deploy_branch ||= Util.git_default_branch
76
+ deploy_branch ||= Git.default_branch
69
77
  target_branch = Interaction.prompt 'Deploy branch:', deploy_branch
70
78
  end
71
79
 
80
+ if settings.linear_integration_set_up?
81
+ config_state = settings.linear_state_after_deploy(target_stage)
82
+ config_state = 'skip' if config_state.empty?
83
+ target_state = Interaction.prompt("Move deployed Linear issues to state:", config_state)
84
+ target_state = '' if target_state.empty? || target_state == 'skip'
85
+ settings.persist_linear_state_after_deploy(target_stage, target_state)
86
+ end
87
+
72
88
  merge_needed = (source_branch != target_branch)
73
89
  push_needed = merge_needed || `git cherry -v | wc -l`.strip.to_i > 0
74
- push_needed = false if Util.testing? # Hard to test
75
90
 
76
91
  Interaction.announce "Checking whether your #{source_branch} branch is ready" ############
77
92
  Util.run!("git checkout #{source_branch}")
@@ -89,13 +104,23 @@ def deploy(target_stage = nil)
89
104
 
90
105
  Interaction.announce 'You are about to:' #################################################
91
106
  Interaction.note "Merge branch #{source_branch} into #{target_branch}" if merge_needed
107
+ linear_issue_ids = []
92
108
  if push_needed
93
- Interaction.note 'Push these commits:' if push_needed
109
+ Interaction.note 'Push these commits:'
94
110
  Util.run!("git --no-pager log origin/#{target_branch}..#{source_branch} --oneline")
111
+
112
+ commit_messages = Git.commits_between(source_branch, target_branch)
113
+ linear_issue_ids = linear_client.extract_issue_ids(commit_messages)
95
114
  end
96
115
  Interaction.note "Deploy to #{target_stage}"
97
116
  Interaction.note "From current branch #{source_branch}" if options.current_branch
98
117
 
118
+ if !linear_issue_ids.empty? && target_state && !target_state.empty?
119
+ relevant_commits = linear_client.filter_by_issue_ids(commit_messages, linear_issue_ids)
120
+ Interaction.note("Move these Linear issues to state \"#{target_state}\":")
121
+ puts relevant_commits.join("\n")
122
+ end
123
+
99
124
  if Interaction.prompt('Go ahead with the deployment?', 'n', /y|yes/)
100
125
  puts
101
126
  git_call = []
@@ -115,6 +140,10 @@ def deploy(target_stage = nil)
115
140
 
116
141
  Util.run!(capistrano_call, show_cmd: true)
117
142
 
143
+ if !linear_issue_ids.empty? && target_state && !target_state.empty?
144
+ linear_client.move_issues_to_state(linear_issue_ids, target_state)
145
+ end
146
+
118
147
  Interaction.success 'Deployment complete.'
119
148
 
120
149
  Hint.did_you_know [
@@ -9,7 +9,9 @@ will tell each step before performing it.
9
9
  LONGDESC
10
10
 
11
11
  def security_update(step = 'prepare')
12
- master = Util.git_default_branch
12
+ require 'geordi/git'
13
+
14
+ master = Git.default_branch
13
15
 
14
16
  case step
15
17
  when 'prepare'
data/lib/geordi/git.rb ADDED
@@ -0,0 +1,53 @@
1
+ module Geordi
2
+ class Git
3
+ class << self
4
+ def local_branch_names
5
+ @local_branch_names ||= begin
6
+ branch_list_string = if Util.testing?
7
+ ENV['GEORDI_TESTING_GIT_BRANCHES'].to_s
8
+ else
9
+ `git branch --format="%(refname:short)"`
10
+ end
11
+
12
+ branch_list_string.strip.split("\n")
13
+ end
14
+ end
15
+
16
+ def current_branch
17
+ if Util.testing?
18
+ default_branch
19
+ else
20
+ `git rev-parse --abbrev-ref HEAD`.strip
21
+ end
22
+ end
23
+
24
+ def staged_changes?
25
+ if Util.testing?
26
+ ENV['GEORDI_TESTING_STAGED_CHANGES'] == 'true'
27
+ else
28
+ statuses = `git status --porcelain`.split("\n")
29
+ statuses.any? { |l| /^[A-Z]/i =~ l }
30
+ end
31
+ end
32
+
33
+ def default_branch
34
+ default_branch = if Util.testing?
35
+ ENV['GEORDI_TESTING_DEFAULT_BRANCH']
36
+ else
37
+ head_symref = `git ls-remote --symref origin HEAD`
38
+ head_symref[%r{\Aref: refs/heads/(\S+)\sHEAD}, 1]
39
+ end
40
+
41
+ default_branch || 'master'
42
+ end
43
+
44
+ def commits_between(source_branch, target_branch)
45
+ return [ENV['GEORDI_TESTING_GIT_COMMIT']] if Util.testing?
46
+
47
+ commits = `git --no-pager log --pretty=format:%s origin/#{target_branch}..#{source_branch}`
48
+
49
+ commits&.split("\n")
50
+ end
51
+ end
52
+ end
53
+ end
@@ -4,7 +4,7 @@ require 'net/http'
4
4
  require 'json'
5
5
 
6
6
  module Geordi
7
- class Gitlinear
7
+ class LinearClient
8
8
  # This require-style is to prevent Ruby from loading files of a different
9
9
  # version of Geordi.
10
10
  require File.expand_path('settings', __dir__)
@@ -16,47 +16,6 @@ module Geordi
16
16
  self.settings = Settings.new
17
17
  end
18
18
 
19
- def commit(git_args)
20
- Interaction.warn <<~WARNING unless Util.staged_changes?
21
- No staged changes. Will create an empty commit.
22
- WARNING
23
-
24
- issue = issue_from_branch || choose_issue
25
- create_commit "[#{issue['identifier']}] #{issue['title']}", "Issue: #{issue['url']}", *git_args
26
- end
27
-
28
- def branch(from_master: false)
29
- issue = choose_issue
30
-
31
- local_branches = local_branch_names
32
- matching_local_branch = local_branches.find { |branch_name| branch_name == issue['branchName'] }
33
- matching_local_branch ||= local_branches.find { |branch_name| branch_name.include? issue['identifier'].to_s }
34
-
35
- if matching_local_branch
36
- Util.run! ['git', 'checkout', matching_local_branch]
37
- else
38
- default_branch = Util.git_default_branch
39
- Util.run! ['git', 'checkout', default_branch] if from_master
40
- Util.run! ['git', 'checkout', '-b', issue['branchName']]
41
- end
42
- end
43
-
44
- private
45
-
46
- attr_accessor :highline, :settings
47
-
48
- def local_branch_names
49
- @local_branch_names ||= begin
50
- branch_list_string = if Util.testing?
51
- ENV['GEORDI_TESTING_GIT_BRANCHES'].to_s
52
- else
53
- `git branch --format="%(refname:short)"`
54
- end
55
-
56
- branch_list_string.strip.split("\n")
57
- end
58
- end
59
-
60
19
  def choose_issue
61
20
  if Util.testing?
62
21
  return dummy_issue_for_testing
@@ -94,23 +53,64 @@ module Geordi
94
53
  nil
95
54
  end
96
55
 
56
+ def move_issues_to_state(issue_identifiers, state)
57
+ return if Util.testing?
58
+
59
+ issues = fetch_linear_issues # This only retrieves issues for the configured linear team ids
60
+ state_ids_by_team_id = state_ids_by_team_id(state)
61
+
62
+ issue_identifiers.each do |identifier|
63
+ issue = issues.find { |i| i['identifier'] == identifier }
64
+
65
+ skip unless issue && (state_id = state_ids_by_team_id[issue.dig('team', 'id')])
66
+
67
+ update_issue_state(issue['id'], state_id)
68
+ end
69
+ end
70
+
97
71
  def issue_from_branch
98
72
  issue = if Util.testing?
99
73
  dummy_issue_for_testing if ENV['GEORDI_TESTING_ISSUE_MATCHES'] == 'true'
100
74
  else
101
- current_branch = Util.current_branch
102
- issue = fetch_linear_issues.find { |issue| issue['branchName'] == current_branch }
75
+ current_branch = Git.current_branch
76
+ fetch_linear_issues.find { |issue| issue['branchName'] == current_branch }
103
77
  end
104
78
 
105
- return unless issue
79
+ if issue
80
+ id = issue['identifier']
81
+ title = issue['title']
82
+
83
+ Interaction.note 'Auto-detected issue from branch name:'
84
+ puts HighLine::BOLD + "[#{id}] #{title}" + HighLine::RESET
85
+
86
+ issue if Interaction.prompt('Use it?', 'y', /y|yes/i)
87
+ end
88
+ end
89
+
90
+ def extract_issue_ids(commit_messages)
91
+ found_ids = []
92
+
93
+ regex = /^\[[A-Z]+\d*-\d+\]/
94
+
95
+ commit_messages&.each do |line|
96
+ line&.scan(regex) do |match|
97
+ found_ids << match
98
+ end
99
+ end
106
100
 
107
- id = issue['identifier']
108
- title = issue['title']
101
+ found_ids.map { |id| id.delete('[]') } # [W-365] => W-365
102
+ end
109
103
 
110
- Interaction.note "Auto-detected issue #{HighLine::BOLD}[#{id}] #{title}#{HighLine::RESET} from branch name."
111
- Interaction.prompt("Use it?", "y", /y|yes/i) ? issue : nil
104
+ def filter_by_issue_ids(list_of_strings, issue_ids)
105
+ list_of_strings.select do |message|
106
+ issue_ids.any? { |id| message.start_with?("[#{id}]") }
107
+ end
112
108
  end
113
109
 
110
+ private
111
+
112
+ attr_accessor :highline, :settings
113
+
114
114
  def dummy_issue_for_testing
115
115
  settings.linear_api_key
116
116
  ENV['GEORDI_TESTING_NO_LINEAR_ISSUES'] == 'true' ? Geordi::Interaction.fail('No issues to offer.') : {
@@ -123,12 +123,6 @@ module Geordi
123
123
  }
124
124
  end
125
125
 
126
- def create_commit(title, description, *git_args)
127
- extra = highline.ask("\nAdd an optional message").strip
128
- title << ' - ' << extra if extra != ''
129
- Util.run!(['git', 'commit', '--allow-empty', '-m', title, '-m', description, *git_args])
130
- end
131
-
132
126
  def fetch_linear_issues
133
127
  @linear_issues ||= begin
134
128
  team_ids = settings.linear_team_ids
@@ -150,6 +144,7 @@ module Geordi
150
144
  nodes {
151
145
  title
152
146
  identifier
147
+ id
153
148
  url
154
149
  branchName
155
150
  assignee {
@@ -159,7 +154,10 @@ module Geordi
159
154
  state {
160
155
  name
161
156
  position
162
- }
157
+ }
158
+ team {
159
+ id
160
+ }
163
161
  }
164
162
  }
165
163
  }
@@ -169,6 +167,70 @@ module Geordi
169
167
  end
170
168
  end
171
169
 
170
+ def state_ids_by_team_id(state_name)
171
+ result = {}
172
+
173
+ team_ids = settings.linear_team_ids
174
+ filter = {
175
+ "team": {
176
+ "id": {
177
+ "in": team_ids,
178
+ }
179
+ }
180
+ }
181
+ response = query_api(<<~GRAPHQL, filter: filter)
182
+ query workflowStates($filter: WorkflowStateFilter) {
183
+ workflowStates(filter: $filter) {
184
+ nodes {
185
+ id
186
+ name
187
+ team {
188
+ id
189
+ name
190
+ }
191
+ }
192
+ }
193
+ }
194
+ GRAPHQL
195
+
196
+ response = response.dig(*%w[workflowStates nodes])
197
+
198
+ team_ids.each do |team_id|
199
+ found_state = response.find do |item|
200
+ item["team"]["id"] == team_id && item["name"] == state_name
201
+ end
202
+
203
+ if found_state
204
+ result[team_id] = found_state["id"]
205
+ else
206
+ team_identifier = response.find { |item| item.dig('team', 'id') == team_id }&.dig('team', 'name') || team_id
207
+ Interaction.warn("Could not find the state \"#{state_name}\" for team \"#{team_identifier}\". Skipping its issues.")
208
+ end
209
+ end
210
+
211
+ if result.empty?
212
+ Interaction.fail("The issue state #{state_name.inspect} does not exist.")
213
+ end
214
+
215
+ result
216
+ end
217
+
218
+ def update_issue_state(issue_id, state_id)
219
+ query_api(<<~GRAPHQL, nil)
220
+ mutation UpdateIssueState {
221
+ issueUpdate(
222
+ id: "#{issue_id}"
223
+ input: {
224
+ stateId: "#{state_id}"
225
+ }
226
+ )
227
+ {
228
+ success
229
+ }
230
+ }
231
+ GRAPHQL
232
+ end
233
+
172
234
  def query_api(attributes, variables)
173
235
  uri = URI(API_ENDPOINT)
174
236
  loading_message = "Connecting to #{uri.host} ... "
@@ -16,7 +16,7 @@ module Geordi
16
16
  linear_team_ids
17
17
  ].freeze
18
18
 
19
- ALLOWED_LOCAL_SETTINGS = %w[ linear_team_ids ].freeze
19
+ ALLOWED_LOCAL_SETTINGS = %w[ linear_team_ids linear_state_after_deploy irb_flags].freeze
20
20
 
21
21
  SETTINGS_WARNED = 'GEORDI_INVALID_SETTINGS_WARNED'
22
22
 
@@ -26,7 +26,11 @@ module Geordi
26
26
 
27
27
  # Global settings
28
28
  def irb_flags
29
- @global_settings['irb_flags']
29
+ if @local_settings.key? 'irb_flags'
30
+ [@local_settings['irb_flags'].to_s, :local]
31
+ elsif @global_settings.key? 'irb_flags'
32
+ [@global_settings['irb_flags'].to_s, :global]
33
+ end
30
34
  end
31
35
 
32
36
  def linear_api_key
@@ -71,6 +75,32 @@ module Geordi
71
75
  team_ids
72
76
  end
73
77
 
78
+ def linear_integration_set_up?
79
+ team_ids = get_linear_team_ids
80
+ !team_ids.empty?
81
+ end
82
+
83
+ def linear_state_after_deploy(stage)
84
+ config_state = @local_settings['linear_state_after_deploy']
85
+
86
+ if config_state && config_state[stage]
87
+ config_state[stage]
88
+ else
89
+ ''
90
+ end
91
+ end
92
+
93
+ def persist_linear_state_after_deploy(stage, target_state)
94
+ config_state = @local_settings.dig('linear_state_after_deploy', stage)
95
+
96
+ unless target_state.eql?(config_state)
97
+ @local_settings['linear_state_after_deploy'] ||= Hash.new
98
+ @local_settings['linear_state_after_deploy'][stage] = target_state
99
+ save_local_settings
100
+ end
101
+ end
102
+
103
+
74
104
  private
75
105
 
76
106
  def read_settings
@@ -121,6 +151,16 @@ module Geordi
121
151
  end
122
152
  end
123
153
 
154
+ def save_local_settings
155
+ unless Util.testing?
156
+ local_path = LOCAL_SETTINGS_FILE_NAME
157
+
158
+ File.open(local_path, 'w') do |file|
159
+ file.write @local_settings.to_yaml
160
+ end
161
+ end
162
+ end
163
+
124
164
  def inquire_linear_api_key
125
165
  Geordi::Interaction.note 'Create a personal API key here: https://linear.app/makandra/settings/account/security'
126
166
  token = Geordi::Interaction.prompt("Please enter the API key:")
@@ -131,6 +171,13 @@ module Geordi
131
171
  token
132
172
  end
133
173
 
174
+ def get_linear_team_ids
175
+ local_team_ids = normalize_team_ids(@local_settings['linear_team_ids'])
176
+ global_team_ids = normalize_team_ids(@global_settings['linear_team_ids'])
177
+
178
+ local_team_ids | global_team_ids
179
+ end
180
+
134
181
  def normalize_team_ids(team_ids)
135
182
  case team_ids
136
183
  when Array
data/lib/geordi/util.rb CHANGED
@@ -94,13 +94,18 @@ module Geordi
94
94
  elsif gem_major_version('rails') == 3
95
95
  "#{binstub_or_fallback('rails')} console #{environment}"
96
96
  else
97
- use_multiline = if irb_version >= Gem::Version.new('1.2') && ruby_version < Gem::Version.new('3.0')
97
+
98
+ irb_flags_from_config, source = Settings.new.irb_flags
99
+ irb_flags = ""
100
+
101
+ if irb_version >= Gem::Version.new('1.2') && ruby_version < Gem::Version.new('3.0') && (source != :local)
98
102
  Interaction.note 'Using --nomultiline switch for faster pasting'
99
- '--nomultiline'
103
+ irb_flags << '--nomultiline '
100
104
  end
101
105
 
102
- irb_flags = [use_multiline, Settings.new.irb_flags].join(' ').strip
103
- irb_flags.prepend('-- ') unless irb_flags.empty?
106
+ irb_flags << irb_flags_from_config if irb_flags_from_config
107
+
108
+ irb_flags.prepend('-- ').strip! unless irb_flags.empty?
104
109
 
105
110
  "#{binstub_or_fallback('rails')} console -e #{environment} #{irb_flags}"
106
111
  end
@@ -114,22 +119,6 @@ module Geordi
114
119
  end
115
120
  end
116
121
 
117
- def current_branch
118
- if testing?
119
- git_default_branch
120
- else
121
- `git rev-parse --abbrev-ref HEAD`.strip
122
- end
123
- end
124
-
125
- def staged_changes?
126
- if testing?
127
- ENV['GEORDI_TESTING_STAGED_CHANGES'] == 'true'
128
- else
129
- statuses = `git status --porcelain`.split("\n")
130
- statuses.any? { |l| /^[A-Z]/i =~ l }
131
- end
132
- end
133
122
 
134
123
  def deploy_targets
135
124
  Dir['config/deploy/*'].map do |f|
@@ -216,18 +205,6 @@ module Geordi
216
205
  def rspec_path?(path)
217
206
  %r{(^|\/)spec|_spec\.rb($|:)}.match?(path)
218
207
  end
219
-
220
- def git_default_branch
221
- default_branch = if testing?
222
- ENV['GEORDI_TESTING_DEFAULT_BRANCH']
223
- else
224
- head_symref = `git ls-remote --symref origin HEAD`
225
- head_symref[%r{\Aref: refs/heads/(\S+)\sHEAD}, 1]
226
- end
227
-
228
- default_branch || 'master'
229
- end
230
-
231
208
  end
232
209
  end
233
210
  end
@@ -1,3 +1,3 @@
1
1
  module Geordi
2
- VERSION = '12.3.0'.freeze
2
+ VERSION = '12.5.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: 12.3.0
4
+ version: 12.5.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Henning Koch
8
- autorequire:
8
+ autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2025-08-04 00:00:00.000000000 Z
11
+ date: 2025-09-22 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: thor
@@ -102,9 +102,10 @@ files:
102
102
  - lib/geordi/cucumber.rb
103
103
  - lib/geordi/db_cleaner.rb
104
104
  - lib/geordi/dump_loader.rb
105
- - lib/geordi/gitlinear.rb
105
+ - lib/geordi/git.rb
106
106
  - lib/geordi/hint.rb
107
107
  - lib/geordi/interaction.rb
108
+ - lib/geordi/linear_client.rb
108
109
  - lib/geordi/remote.rb
109
110
  - lib/geordi/settings.rb
110
111
  - lib/geordi/util.rb
@@ -123,7 +124,7 @@ metadata:
123
124
  bug_tracker_uri: https://github.com/makandra/geordi/issues
124
125
  changelog_uri: https://github.com/makandra/geordi/blob/master/CHANGELOG.md
125
126
  rubygems_mfa_required: 'true'
126
- post_install_message:
127
+ post_install_message:
127
128
  rdoc_options: []
128
129
  require_paths:
129
130
  - lib
@@ -139,7 +140,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
139
140
  version: '0'
140
141
  requirements: []
141
142
  rubygems_version: 3.1.6
142
- signing_key:
143
+ signing_key:
143
144
  specification_version: 4
144
145
  summary: Collection of command line tools we use in our daily work with Ruby, Rails
145
146
  and Linux at makandra.