checkoff 0.5.5 → 0.10.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. checksums.yaml +4 -4
  2. data/.circleci/config.yml +119 -12
  3. data/.envrc +2 -0
  4. data/.git-hooks/pre_commit/circle_ci.rb +21 -0
  5. data/.gitignore +50 -1
  6. data/.overcommit.yml +53 -0
  7. data/.rubocop.yml +64 -168
  8. data/.yamllint.yml +8 -0
  9. data/CODE_OF_CONDUCT.md +120 -36
  10. data/DEVELOPMENT.md +28 -0
  11. data/Gemfile.lock +130 -0
  12. data/{LICENSE.txt → LICENSE} +7 -6
  13. data/Makefile +48 -18
  14. data/README.md +15 -2
  15. data/Rakefile +2 -9
  16. data/bin/bump +29 -0
  17. data/bin/checkoff +29 -0
  18. data/bin/overcommit +29 -0
  19. data/bin/rake +29 -0
  20. data/checkoff.gemspec +9 -5
  21. data/coverage/.last_run.json +2 -1
  22. data/docs/cookiecutter_input.json +13 -0
  23. data/fix.sh +299 -0
  24. data/lib/checkoff/cli.rb +9 -1
  25. data/lib/checkoff/config_loader.rb +43 -3
  26. data/lib/checkoff/projects.rb +5 -5
  27. data/lib/checkoff/sections.rb +48 -11
  28. data/lib/checkoff/tasks.rb +1 -1
  29. data/lib/checkoff/version.rb +1 -1
  30. data/lib/checkoff/workspaces.rb +4 -3
  31. data/metrics/bigfiles_high_water_mark +1 -1
  32. data/metrics/rubocop_high_water_mark +1 -1
  33. data/{lib/tasks → rakelib}/ci.rake +0 -0
  34. data/{lib/tasks → rakelib}/clear_metrics.rake +1 -1
  35. data/{lib/tasks → rakelib}/default.rake +0 -0
  36. data/rakelib/gem_tasks.rake +3 -0
  37. data/{lib/tasks → rakelib}/localtest.rake +1 -1
  38. data/rakelib/overcommit.rake +6 -0
  39. data/rakelib/quality.rake +4 -0
  40. data/{lib/tasks → rakelib}/test.rake +0 -0
  41. data/rakelib/undercover.rake +8 -0
  42. data/requirements_dev.txt +2 -0
  43. metadata +93 -25
  44. data/.ruby-version +0 -1
  45. data/.travis.yml +0 -22
  46. data/lib/tasks/feature.rake +0 -9
  47. data/lib/tasks/quality.rake +0 -9
  48. data/lib/tasks/spec.rake +0 -9
data/lib/checkoff/cli.rb CHANGED
@@ -12,6 +12,7 @@ require_relative 'sections'
12
12
 
13
13
  module Checkoff
14
14
  # Provide ability for CLI to pull Asana items
15
+ # rubocop:disable Metrics/ClassLength
15
16
  class CLI
16
17
  attr_reader :sections, :stderr
17
18
 
@@ -86,6 +87,7 @@ module Checkoff
86
87
  subargs.task_name = args[2]
87
88
  end
88
89
 
90
+ # rubocop:disable Metrics/MethodLength
89
91
  def parse_args(args)
90
92
  mode = args[0]
91
93
  subargs = OpenStruct.new
@@ -99,6 +101,7 @@ module Checkoff
99
101
  end
100
102
  [mode, subargs]
101
103
  end
104
+ # rubocop:enable Metrics/MethodLength
102
105
 
103
106
  def output_help
104
107
  stderr.puts 'View tasks:'
@@ -110,7 +113,11 @@ module Checkoff
110
113
  end
111
114
 
112
115
  def view(workspace_name, project_name, section_name)
113
- project_name = project_name[1..-1].to_sym if project_name.start_with? ':'
116
+ if project_name.nil?
117
+ stderr.puts 'Please specify a project name'
118
+ exit(1)
119
+ end
120
+ project_name = project_name[1..].to_sym if project_name.start_with? ':'
114
121
  if section_name.nil?
115
122
  run_on_project(workspace_name, project_name)
116
123
  else
@@ -131,4 +138,5 @@ module Checkoff
131
138
  end
132
139
  end
133
140
  end
141
+ # rubocop:enable Metrics/ClassLength
134
142
  end
@@ -4,12 +4,52 @@ require 'yaml'
4
4
  require 'active_support/core_ext/hash'
5
5
 
6
6
  module Checkoff
7
+ # Use the provided config from a YAML file, and fall back to env
8
+ # variable if it's not populated for a key'
9
+ class EnvFallbackConfigLoader
10
+ def initialize(config, sym, yaml_filename)
11
+ @config = config
12
+ @envvar_prefix = sym.upcase
13
+ @yaml_filename = yaml_filename
14
+ end
15
+
16
+ def envvar_name(key)
17
+ "#{@envvar_prefix}__#{key.upcase}"
18
+ end
19
+
20
+ def [](key)
21
+ config_value = @config[key]
22
+ return config_value unless config_value.nil?
23
+
24
+ ENV[envvar_name(key)]
25
+ end
26
+
27
+ def fetch(key)
28
+ out = self[key]
29
+ return out unless out.nil?
30
+
31
+ raise KeyError,
32
+ "Please configure either the #{key} key in #{@yaml_filename} or set #{envvar_name(key)}"
33
+ end
34
+ end
35
+
7
36
  # Load configuration file
8
37
  class ConfigLoader
9
- def self.load(sym)
38
+ def self.yaml_filename(sym)
10
39
  file = "#{sym}.yml"
11
- YAML.load_file(File.expand_path("~/.#{file}"))
12
- .with_indifferent_access
40
+ File.expand_path("~/.#{file}")
41
+ end
42
+
43
+ def self.load_yaml_file(sym)
44
+ filename = yaml_filename(sym)
45
+ return {} unless File.exist?(filename)
46
+
47
+ YAML.load_file(filename).with_indifferent_access
48
+ end
49
+
50
+ def self.load(sym)
51
+ yaml_result = load_yaml_file(sym)
52
+ EnvFallbackConfigLoader.new(yaml_result, sym, yaml_filename(sym))
13
53
  end
14
54
  end
15
55
  end
@@ -49,10 +49,10 @@ module Checkoff
49
49
  end
50
50
 
51
51
  def my_tasks(workspace_name)
52
- my_tasks = @config[:my_tasks]
53
- gid = @config[:my_tasks][workspace_name] unless my_tasks.nil?
54
- raise "Please define [:my_tasks][#{workspace_name}] in config file" if my_tasks.nil? || gid.nil?
55
-
52
+ workspace = @workspaces.workspace_by_name(workspace_name)
53
+ result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
54
+ workspace: workspace.gid)
55
+ gid = result.gid
56
56
  projects.find_by_id(gid)
57
57
  end
58
58
 
@@ -77,7 +77,7 @@ module Checkoff
77
77
  per_page: 100,
78
78
  options: {
79
79
  fields: %w[name completed_at due_at due_on assignee_status tags
80
- memberships.project.gid memberships.section.name],
80
+ memberships.project.gid memberships.section.name dependencies],
81
81
  },
82
82
  }
83
83
  end
@@ -4,6 +4,7 @@ require 'forwardable'
4
4
 
5
5
  module Checkoff
6
6
  # Query different sections of Asana projects
7
+ # rubocop:disable Metrics/ClassLength
7
8
  class Sections
8
9
  MINUTE = 60
9
10
  LONG_CACHE_TIME = MINUTE * 15
@@ -11,12 +12,14 @@ module Checkoff
11
12
 
12
13
  extend Forwardable
13
14
 
14
- attr_reader :projects, :time
15
+ attr_reader :projects, :workspaces, :time
15
16
 
16
17
  def initialize(config: Checkoff::ConfigLoader.load(:asana),
17
18
  projects: Checkoff::Projects.new(config: config),
19
+ workspaces: Checkoff::Workspaces.new(config: config),
18
20
  time: Time)
19
21
  @projects = projects
22
+ @workspaces = workspaces
20
23
  @time = time
21
24
  end
22
25
 
@@ -41,6 +44,12 @@ module Checkoff
41
44
  end
42
45
  cache_method :by_section, LONG_CACHE_TIME
43
46
 
47
+ def legacy_tasks_by_section_for_project(project)
48
+ raw_tasks = projects.tasks_from_project(project)
49
+ active_tasks = projects.active_tasks(raw_tasks)
50
+ legacy_by_section(active_tasks)
51
+ end
52
+
44
53
  def tasks_by_section_for_project(project)
45
54
  raw_tasks = projects.tasks_from_project(project)
46
55
  active_tasks = projects.active_tasks(raw_tasks)
@@ -68,12 +77,12 @@ module Checkoff
68
77
  by_section
69
78
  end
70
79
 
71
- def tasks_by_section_for_project_and_assignee_status(project,
72
- assignee_status)
80
+ def legacy_tasks_by_section_for_project_and_assignee_status(project,
81
+ assignee_status)
73
82
  raw_tasks = projects.tasks_from_project(project)
74
83
  by_assignee_status =
75
84
  projects.active_tasks(raw_tasks)
76
- .group_by(&:assignee_status)
85
+ .group_by(&:assignee_status)
77
86
  active_tasks = by_assignee_status[assignee_status]
78
87
  legacy_by_section(active_tasks)
79
88
  end
@@ -87,15 +96,35 @@ module Checkoff
87
96
  project
88
97
  end
89
98
 
99
+ def verify_legacy_user_task_list!(workspace_name)
100
+ return unless user_task_list_migrated_to_real_sections?(workspace_name)
101
+
102
+ raise NotImplementedError, 'Section-based user task lists not yet supported'
103
+ end
104
+
105
+ ASSIGNEE_STATUS_BY_PROJECT_NAME = {
106
+ my_tasks_new: 'inbox',
107
+ my_tasks_today: 'today',
108
+ my_tasks_upcoming: 'upcoming',
109
+ }.freeze
110
+
111
+ def user_task_list_by_section(workspace_name, project_name)
112
+ verify_legacy_user_task_list!(workspace_name)
113
+
114
+ project = projects.project(workspace_name, project_name)
115
+ if project_name == :my_tasks
116
+ legacy_tasks_by_section_for_project(project)
117
+ else
118
+ legacy_tasks_by_section_for_project_and_assignee_status(project,
119
+ ASSIGNEE_STATUS_BY_PROJECT_NAME.fetch(project_name))
120
+ end
121
+ end
122
+
90
123
  def tasks_by_section(workspace_name, project_name)
91
124
  project = project_or_raise(workspace_name, project_name)
92
125
  case project_name
93
- when :my_tasks_new
94
- tasks_by_section_for_project_and_assignee_status(project, 'inbox')
95
- when :my_tasks_today
96
- tasks_by_section_for_project_and_assignee_status(project, 'today')
97
- when :my_tasks_upcoming
98
- tasks_by_section_for_project_and_assignee_status(project, 'upcoming')
126
+ when :my_tasks, :my_tasks_new, :my_tasks_today, :my_tasks_upcoming
127
+ user_task_list_by_section(workspace_name, project_name)
99
128
  else
100
129
  tasks_by_section_for_project(project)
101
130
  end
@@ -104,7 +133,7 @@ module Checkoff
104
133
 
105
134
  # XXX: Rename to section_tasks
106
135
  def tasks(workspace_name, project_name, section_name)
107
- tasks_by_section(workspace_name, project_name)[section_name]
136
+ tasks_by_section(workspace_name, project_name).fetch(section_name, [])
108
137
  end
109
138
  cache_method :tasks, SHORT_CACHE_TIME
110
139
 
@@ -120,6 +149,13 @@ module Checkoff
120
149
  end
121
150
  cache_method :section_task_names, SHORT_CACHE_TIME
122
151
 
152
+ def user_task_list_migrated_to_real_sections?(workspace_name)
153
+ workspace = workspaces.workspace_by_name(workspace_name)
154
+ result = client.user_task_lists.get_user_task_list_for_user(user_gid: 'me',
155
+ workspace: workspace.gid)
156
+ result.migration_status != 'not_migrated'
157
+ end
158
+
123
159
  def task_due?(task)
124
160
  if task.due_at
125
161
  Time.parse(task.due_at) <= time.now
@@ -143,4 +179,5 @@ module Checkoff
143
179
  end
144
180
  cache_method :project_task_names, SHORT_CACHE_TIME
145
181
  end
182
+ # rubocop:enable Metrics/ClassLength
146
183
  end
@@ -50,7 +50,7 @@ module Checkoff
50
50
  end
51
51
 
52
52
  def default_assignee_gid
53
- @config[:default_assignee_gid]
53
+ @config.fetch(:default_assignee_gid)
54
54
  end
55
55
  end
56
56
  end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module Checkoff
4
- VERSION = '0.5.5'
4
+ VERSION = '0.10.0'
5
5
  end
@@ -11,19 +11,20 @@ module Checkoff
11
11
 
12
12
  def client
13
13
  @client ||= @asana_client.new do |c|
14
- c.authentication :access_token, @config[:personal_access_token]
14
+ c.authentication :access_token, @config.fetch(:personal_access_token)
15
15
  c.default_headers 'asana-enable' => 'string_ids,new_sections'
16
+ c.default_headers 'asana-disable' => 'new_user_task_lists'
16
17
  end
17
18
  end
18
19
 
19
20
  def default_workspace_gid
20
- @config[:default_workspace_gid]
21
+ @config.fetch(:default_workspace_gid)
21
22
  end
22
23
 
23
24
  def workspace_by_name(workspace_name)
24
25
  client.workspaces.find_all.find do |workspace|
25
26
  workspace.name == workspace_name
26
- end || raise("Could not find workspace #{workspace_name}")
27
+ end || raise("Could not find workspace named [#{workspace_name}]")
27
28
  end
28
29
  end
29
30
  end
@@ -1 +1 @@
1
- 414
1
+ 488
@@ -1 +1 @@
1
- 4
1
+ 6
File without changes
@@ -3,6 +3,6 @@
3
3
  desc 'Rebaseline quality thresholds to last commit'
4
4
  task :clear_metrics do |_t|
5
5
  ret =
6
- system('git checkout coverage/.last_run.json metrics/*_high_water_mark')
6
+ system('git checkout coverage/.last_run.json')
7
7
  raise unless ret
8
8
  end
File without changes
@@ -0,0 +1,3 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'bundler/gem_tasks'
@@ -1,4 +1,4 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  desc 'Standard build when running on a workstation'
4
- task localtest: %i[clear_metrics test quality]
4
+ task localtest: %i[clear_metrics test undercover quality]
@@ -0,0 +1,6 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Run overcommit on current code'
4
+ task :overcommit do
5
+ sh 'overcommit --run'
6
+ end
@@ -0,0 +1,4 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Run quality checks'
4
+ task quality: :overcommit
File without changes
@@ -0,0 +1,8 @@
1
+ # frozen_string_literal: true
2
+
3
+ desc 'Ensure PR changes are fully covered by tests'
4
+ task :undercover do |_t|
5
+ ret =
6
+ system('undercover --compare origin/main')
7
+ raise unless ret
8
+ end
@@ -0,0 +1,2 @@
1
+ pip==21.0.1
2
+ yamllint==1.26.0
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: checkoff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.10.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Vince Broz
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2020-12-30 00:00:00.000000000 Z
11
+ date: 2021-05-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -66,6 +66,20 @@ dependencies:
66
66
  - - ">="
67
67
  - !ruby/object:Gem::Version
68
68
  version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bump
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
69
83
  - !ruby/object:Gem::Dependency
70
84
  name: bundler
71
85
  requirement: !ruby/object:Gem::Requirement
@@ -109,19 +123,19 @@ dependencies:
109
123
  - !ruby/object:Gem::Version
110
124
  version: '0'
111
125
  - !ruby/object:Gem::Dependency
112
- name: quality
126
+ name: overcommit
113
127
  requirement: !ruby/object:Gem::Requirement
114
128
  requirements:
115
- - - "~>"
129
+ - - ">="
116
130
  - !ruby/object:Gem::Version
117
- version: '38'
131
+ version: '0'
118
132
  type: :development
119
133
  prerelease: false
120
134
  version_requirements: !ruby/object:Gem::Requirement
121
135
  requirements:
122
- - - "~>"
136
+ - - ">="
123
137
  - !ruby/object:Gem::Version
124
- version: '38'
138
+ version: '0'
125
139
  - !ruby/object:Gem::Dependency
126
140
  name: rake
127
141
  requirement: !ruby/object:Gem::Requirement
@@ -137,19 +151,47 @@ dependencies:
137
151
  - !ruby/object:Gem::Version
138
152
  version: '13.0'
139
153
  - !ruby/object:Gem::Dependency
140
- name: rspec
154
+ name: rubocop
155
+ requirement: !ruby/object:Gem::Requirement
156
+ requirements:
157
+ - - ">="
158
+ - !ruby/object:Gem::Version
159
+ version: '0'
160
+ type: :development
161
+ prerelease: false
162
+ version_requirements: !ruby/object:Gem::Requirement
163
+ requirements:
164
+ - - ">="
165
+ - !ruby/object:Gem::Version
166
+ version: '0'
167
+ - !ruby/object:Gem::Dependency
168
+ name: rubocop-minitest
169
+ requirement: !ruby/object:Gem::Requirement
170
+ requirements:
171
+ - - ">="
172
+ - !ruby/object:Gem::Version
173
+ version: '0'
174
+ type: :development
175
+ prerelease: false
176
+ version_requirements: !ruby/object:Gem::Requirement
177
+ requirements:
178
+ - - ">="
179
+ - !ruby/object:Gem::Version
180
+ version: '0'
181
+ - !ruby/object:Gem::Dependency
182
+ name: rubocop-rake
141
183
  requirement: !ruby/object:Gem::Requirement
142
184
  requirements:
143
185
  - - ">="
144
186
  - !ruby/object:Gem::Version
145
- version: '3.4'
187
+ version: '0'
146
188
  type: :development
147
189
  prerelease: false
148
190
  version_requirements: !ruby/object:Gem::Requirement
149
191
  requirements:
150
192
  - - ">="
151
193
  - !ruby/object:Gem::Version
152
- version: '3.4'
194
+ version: '0'
153
195
  - !ruby/object:Gem::Dependency
154
196
  name: simplecov
155
197
  requirement: !ruby/object:Gem::Requirement
@@ -164,6 +206,20 @@ dependencies:
164
206
  - - ">="
165
207
  - !ruby/object:Gem::Version
166
208
  version: '0'
209
+ - !ruby/object:Gem::Dependency
210
+ name: undercover
211
+ requirement: !ruby/object:Gem::Requirement
212
+ requirements:
213
+ - - ">="
214
+ - !ruby/object:Gem::Version
215
+ version: '0'
216
+ type: :development
217
+ prerelease: false
218
+ version_requirements: !ruby/object:Gem::Requirement
219
+ requirements:
220
+ - - ">="
221
+ - !ruby/object:Gem::Version
222
+ version: '0'
167
223
  description:
168
224
  email:
169
225
  - vince@broz.cc
@@ -173,22 +229,32 @@ extensions: []
173
229
  extra_rdoc_files: []
174
230
  files:
175
231
  - ".circleci/config.yml"
232
+ - ".envrc"
233
+ - ".git-hooks/pre_commit/circle_ci.rb"
176
234
  - ".gitignore"
235
+ - ".overcommit.yml"
177
236
  - ".rubocop.yml"
178
- - ".ruby-version"
179
- - ".travis.yml"
237
+ - ".yamllint.yml"
180
238
  - CODE_OF_CONDUCT.md
239
+ - DEVELOPMENT.md
181
240
  - Gemfile
182
- - LICENSE.txt
241
+ - Gemfile.lock
242
+ - LICENSE
183
243
  - Makefile
184
244
  - README.md
185
245
  - Rakefile
246
+ - bin/bump
247
+ - bin/checkoff
186
248
  - bin/console
249
+ - bin/overcommit
250
+ - bin/rake
187
251
  - bin/setup
188
252
  - checkoff.gemspec
189
253
  - coverage/.last_run.json
254
+ - docs/cookiecutter_input.json
190
255
  - docs/example_project.png
191
256
  - exe/checkoff
257
+ - fix.sh
192
258
  - lib/checkoff.rb
193
259
  - lib/checkoff/cli.rb
194
260
  - lib/checkoff/config_loader.rb
@@ -198,14 +264,6 @@ files:
198
264
  - lib/checkoff/tasks.rb
199
265
  - lib/checkoff/version.rb
200
266
  - lib/checkoff/workspaces.rb
201
- - lib/tasks/ci.rake
202
- - lib/tasks/clear_metrics.rake
203
- - lib/tasks/default.rake
204
- - lib/tasks/feature.rake
205
- - lib/tasks/localtest.rake
206
- - lib/tasks/quality.rake
207
- - lib/tasks/spec.rake
208
- - lib/tasks/test.rake
209
267
  - metrics/bigfiles_high_water_mark
210
268
  - metrics/brakeman_high_water_mark
211
269
  - metrics/bundle-audit_high_water_mark
@@ -223,9 +281,19 @@ files:
223
281
  - metrics/rubocop_high_water_mark
224
282
  - metrics/scalastyle_high_water_mark
225
283
  - metrics/shellcheck_high_water_mark
226
- homepage: http://github.com/apiology/checkoff
284
+ - rakelib/ci.rake
285
+ - rakelib/clear_metrics.rake
286
+ - rakelib/default.rake
287
+ - rakelib/gem_tasks.rake
288
+ - rakelib/localtest.rake
289
+ - rakelib/overcommit.rake
290
+ - rakelib/quality.rake
291
+ - rakelib/test.rake
292
+ - rakelib/undercover.rake
293
+ - requirements_dev.txt
294
+ homepage: https://github.com/apiology/checkoff
227
295
  licenses:
228
- - MIT
296
+ - MIT license
229
297
  metadata: {}
230
298
  post_install_message:
231
299
  rdoc_options: []
@@ -235,14 +303,14 @@ required_ruby_version: !ruby/object:Gem::Requirement
235
303
  requirements:
236
304
  - - ">="
237
305
  - !ruby/object:Gem::Version
238
- version: '2.5'
306
+ version: '2.6'
239
307
  required_rubygems_version: !ruby/object:Gem::Requirement
240
308
  requirements:
241
309
  - - ">="
242
310
  - !ruby/object:Gem::Version
243
311
  version: '0'
244
312
  requirements: []
245
- rubygems_version: 3.0.3
313
+ rubygems_version: 3.1.2
246
314
  signing_key:
247
315
  specification_version: 4
248
316
  summary: Command-line and gem client for Asana (unofficial)