Module: Rubyists::Linear::CLI::WhatFor

Includes:
Projects
Included in:
SubCommands
Defined in:
lib/linear/cli/what_for.rb

Overview

Module for the _for methods

Constant Summary collapse

PR_TYPES =

TODO: Make this configurable

{
  fix: 'Bug fixes',
  feat: 'New feature work',
  chore: 'Chores and maintenance',
  eyes: 'Observability, metrics',
  test: 'Testing code',
  perf: 'Performance related work',
  refactor: 'Code refactoring',
  docs: 'Documentation Updates',
  sec: 'Security-related, including dependency updates',
  style: 'Style updates',
  ci: 'Continuous integration related',
  db: 'Database-Related (migrations, models, etc)'
}.freeze
PR_TYPE_SELECTIONS =
PR_TYPES.invert
ALLOWED_PR_TYPES =
/#{PR_TYPES.keys.join("|")}/

Instance Method Summary collapse

Methods included from Projects

#ask_for_projects, #project_for, #project_scores

Instance Method Details

#ask_or_edit(thing, question) ⇒ Object

Raises:



70
71
72
73
74
75
76
77
78
79
80
# File 'lib/linear/cli/what_for.rb', line 70

def ask_or_edit(thing, question)
  return thing if thing && thing != '-'

  answer = prompt.ask("#{question}: ('-' to open an editor)", default: '-')
  return answer unless answer == '-'

  answer = editor_for [question.downcase, '.md']
  raise SmellsBad, "No content provided for #{question}" if answer.empty?

  answer
end

#cancelled_state_for(thingy) ⇒ Object



54
55
56
57
58
59
60
# File 'lib/linear/cli/what_for.rb', line 54

def cancelled_state_for(thingy)
  states = thingy.cancelled_states
  return states.first if states.size == 1

  selection = prompt.select('Choose a cancelled state', states.to_h { |s| [s.name, s.id] })
  Rubyists::Linear::WorkflowState.find selection
end

#comment_for(issue, comment) ⇒ Object



39
40
41
# File 'lib/linear/cli/what_for.rb', line 39

def comment_for(issue, comment)
  ask_or_edit comment, "Comment for #{issue.identifier} - #{issue.title}"
end

#completed_state_for(thingy) ⇒ Object



62
63
64
65
66
67
68
# File 'lib/linear/cli/what_for.rb', line 62

def completed_state_for(thingy)
  states = thingy.completed_states
  return states.first if states.size == 1

  selection = prompt.select('Choose a completed state', states.to_h { |s| [s.name, s.id] })
  Rubyists::Linear::WorkflowState.find selection
end

#description_for(description = nil) ⇒ Object



82
83
84
# File 'lib/linear/cli/what_for.rb', line 82

def description_for(description = nil)
  ask_or_edit description, 'Description'
end

#editor_for(prefix) ⇒ Object



30
31
32
33
34
35
36
37
# File 'lib/linear/cli/what_for.rb', line 30

def editor_for(prefix)
  file = Tempfile.open(prefix, Rubyists::Linear.tmpdir)
  TTY::Editor.open(file.path)
  file.close
  File.readlines(file.path).map(&:chomp).join('\\n')
ensure
  file&.close
end

#labels_for(team, labels = nil) ⇒ Object



132
133
134
135
136
# File 'lib/linear/cli/what_for.rb', line 132

def labels_for(team, labels = nil)
  return Rubyists::Linear::Label.find_all_by_name(labels.map(&:strip)) if labels

  prompt.multi_select('Labels:', team.labels.to_h { |t| [t.name, t] })
end

#pr_description_for(issue) ⇒ Object



101
102
103
104
105
106
107
108
109
110
111
112
113
# File 'lib/linear/cli/what_for.rb', line 101

def pr_description_for(issue)
  tmpfile = Tempfile.new([issue.identifier, '.md'], Rubyists::Linear.tmpdir)
  # TODO: Look up templates
  proposed = "# Context\n\n#{issue.description}\n\n## Issue\n\n#{issue.identifier}\n\n# Solution\n\n# Testing\n\n# Notes\n\n" # rubocop:disable Layout/LineLength
  tmpfile.write(proposed) && tmpfile.close
  desc = TTY::Editor.open(tmpfile.path)
  return tmpfile if desc

  File.open(tmpfile.path, 'w+') do |file|
    file.puts prompt.ask("Description for PR for #{issue.identifier} - #{issue.title}", default: proposed)
  end
  tmpfile
end

#pr_scope_for(title) ⇒ Object



122
123
124
125
126
127
128
129
130
# File 'lib/linear/cli/what_for.rb', line 122

def pr_scope_for(title)
  proposed_scope = title.match(/^\w+\(([^)]+)\)/)
  return proposed_scope[1].downcase if proposed_scope

  scope = prompt.ask('What is the scope of this PR?', default: 'none')
  return nil if scope.empty? && scope == 'none'

  scope
end

#pr_title_for(issue) ⇒ Object



92
93
94
95
96
97
98
99
# File 'lib/linear/cli/what_for.rb', line 92

def pr_title_for(issue)
  proposed = [pr_type_for(issue)]
  proposed_scope = pr_scope_for(issue.title)
  proposed << "(#{proposed_scope})" if proposed_scope
  summary = issue.title.sub(/(?:#{ALLOWED_PR_TYPES})(\([^)]+\))? /, '')
  proposed << ": #{issue.identifier} - #{summary}"
  prompt.ask("Title for PR for #{issue.identifier} - #{summary}", default: proposed.join)
end

#pr_type_for(issue) ⇒ Object



115
116
117
118
119
120
# File 'lib/linear/cli/what_for.rb', line 115

def pr_type_for(issue)
  proposed_type = issue.title.match(/^(#{ALLOWED_PR_TYPES})/i)
  return proposed_type[1].downcase if proposed_type

  prompt.select('What type of PR is this?', PR_TYPE_SELECTIONS)
end

#reason_for(reason = nil, four: nil) ⇒ Object



49
50
51
52
# File 'lib/linear/cli/what_for.rb', line 49

def reason_for(reason = nil, four: nil)
  question = four ? "Reason for #{TTY::Markdown.parse(four)}" : 'Reason'
  ask_or_edit reason, question
end

#team_for(key = nil) ⇒ Object



43
44
45
46
47
# File 'lib/linear/cli/what_for.rb', line 43

def team_for(key = nil)
  return Rubyists::Linear::Team.find(key) if key

  ask_for_team
end

#title_for(title = nil) ⇒ Object



86
87
88
89
90
# File 'lib/linear/cli/what_for.rb', line 86

def title_for(title = nil)
  return title if title

  prompt.ask('Title:')
end