plan_my_stuff 0.1.0 → 1.0.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.
Files changed (113) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +595 -0
  3. data/CONFIGURATION.md +487 -0
  4. data/README.md +612 -88
  5. data/app/controllers/plan_my_stuff/application_controller.rb +27 -5
  6. data/app/controllers/plan_my_stuff/comments_controller.rb +50 -19
  7. data/app/controllers/plan_my_stuff/issues/approvals_controller.rb +127 -0
  8. data/app/controllers/plan_my_stuff/issues/closures_controller.rb +53 -0
  9. data/app/controllers/plan_my_stuff/issues/links_controller.rb +129 -0
  10. data/app/controllers/plan_my_stuff/issues/takes_controller.rb +161 -0
  11. data/app/controllers/plan_my_stuff/issues/testings_controller.rb +82 -0
  12. data/app/controllers/plan_my_stuff/issues/viewers_controller.rb +62 -0
  13. data/app/controllers/plan_my_stuff/issues/waitings_controller.rb +55 -0
  14. data/app/controllers/plan_my_stuff/issues_controller.rb +53 -70
  15. data/app/controllers/plan_my_stuff/labels_controller.rb +32 -10
  16. data/app/controllers/plan_my_stuff/project_items/assignments_controller.rb +88 -0
  17. data/app/controllers/plan_my_stuff/project_items/statuses_controller.rb +44 -0
  18. data/app/controllers/plan_my_stuff/project_items_controller.rb +32 -69
  19. data/app/controllers/plan_my_stuff/projects_controller.rb +81 -3
  20. data/app/controllers/plan_my_stuff/testing_project_items/results_controller.rb +67 -0
  21. data/app/controllers/plan_my_stuff/testing_project_items_controller.rb +49 -0
  22. data/app/controllers/plan_my_stuff/testing_projects_controller.rb +121 -0
  23. data/app/controllers/plan_my_stuff/webhooks/aws_controller.rb +202 -0
  24. data/app/controllers/plan_my_stuff/webhooks/github_controller.rb +371 -0
  25. data/app/jobs/plan_my_stuff/application_job.rb +8 -0
  26. data/app/jobs/plan_my_stuff/reminders_sweep_job.rb +75 -0
  27. data/app/views/plan_my_stuff/comments/edit.html.erb +1 -3
  28. data/app/views/plan_my_stuff/comments/partials/_form.html.erb +8 -0
  29. data/app/views/plan_my_stuff/issues/edit.html.erb +2 -4
  30. data/app/views/plan_my_stuff/issues/index.html.erb +5 -5
  31. data/app/views/plan_my_stuff/issues/new.html.erb +2 -4
  32. data/app/views/plan_my_stuff/issues/partials/_approvals.html.erb +108 -0
  33. data/app/views/plan_my_stuff/issues/partials/_form.html.erb +11 -6
  34. data/app/views/plan_my_stuff/issues/partials/_labels.html.erb +4 -3
  35. data/app/views/plan_my_stuff/issues/partials/_links.html.erb +113 -0
  36. data/app/views/plan_my_stuff/issues/partials/_viewers.html.erb +4 -3
  37. data/app/views/plan_my_stuff/issues/show.html.erb +67 -6
  38. data/app/views/plan_my_stuff/partials/_flash.html.erb +3 -0
  39. data/app/views/plan_my_stuff/projects/edit.html.erb +5 -0
  40. data/app/views/plan_my_stuff/projects/index.html.erb +18 -2
  41. data/app/views/plan_my_stuff/projects/new.html.erb +5 -0
  42. data/app/views/plan_my_stuff/projects/partials/_form.html.erb +30 -0
  43. data/app/views/plan_my_stuff/projects/show.html.erb +30 -11
  44. data/app/views/plan_my_stuff/testing_project_items/new.html.erb +10 -0
  45. data/app/views/plan_my_stuff/testing_project_items/results/new.html.erb +20 -0
  46. data/app/views/plan_my_stuff/testing_projects/edit.html.erb +5 -0
  47. data/app/views/plan_my_stuff/testing_projects/new.html.erb +5 -0
  48. data/app/views/plan_my_stuff/testing_projects/partials/_form.html.erb +40 -0
  49. data/app/views/plan_my_stuff/testing_projects/partials/_item.html.erb +52 -0
  50. data/app/views/plan_my_stuff/testing_projects/partials/items/_form.html.erb +36 -0
  51. data/app/views/plan_my_stuff/testing_projects/show.html.erb +65 -0
  52. data/config/routes.rb +43 -15
  53. data/lib/generators/plan_my_stuff/install/templates/initializer.rb +302 -20
  54. data/lib/plan_my_stuff/application_record.rb +158 -1
  55. data/lib/plan_my_stuff/approval.rb +88 -0
  56. data/lib/plan_my_stuff/archive/sweep.rb +85 -0
  57. data/lib/plan_my_stuff/archive.rb +12 -0
  58. data/lib/plan_my_stuff/attachment.rb +83 -0
  59. data/lib/plan_my_stuff/attachment_uploader.rb +245 -0
  60. data/lib/plan_my_stuff/aws_sns_simulator.rb +116 -0
  61. data/lib/plan_my_stuff/base_metadata.rb +25 -28
  62. data/lib/plan_my_stuff/base_project.rb +502 -0
  63. data/lib/plan_my_stuff/base_project_extractions/graphql_hydration.rb +186 -0
  64. data/lib/plan_my_stuff/base_project_item.rb +588 -0
  65. data/lib/plan_my_stuff/base_project_metadata.rb +16 -0
  66. data/lib/plan_my_stuff/cache.rb +197 -0
  67. data/lib/plan_my_stuff/client.rb +139 -64
  68. data/lib/plan_my_stuff/comment.rb +225 -100
  69. data/lib/plan_my_stuff/comment_metadata.rb +68 -5
  70. data/lib/plan_my_stuff/configuration.rb +459 -28
  71. data/lib/plan_my_stuff/custom_fields.rb +96 -12
  72. data/lib/plan_my_stuff/engine.rb +14 -2
  73. data/lib/plan_my_stuff/errors.rb +65 -5
  74. data/lib/plan_my_stuff/graphql/queries.rb +454 -0
  75. data/lib/plan_my_stuff/issue.rb +1097 -166
  76. data/lib/plan_my_stuff/issue_extractions/approvals.rb +370 -0
  77. data/lib/plan_my_stuff/issue_extractions/links.rb +525 -0
  78. data/lib/plan_my_stuff/issue_extractions/viewers.rb +75 -0
  79. data/lib/plan_my_stuff/issue_extractions/waiting.rb +171 -0
  80. data/lib/plan_my_stuff/issue_field.rb +126 -0
  81. data/lib/plan_my_stuff/issue_field_translation.rb +67 -0
  82. data/lib/plan_my_stuff/issue_field_value_set.rb +68 -0
  83. data/lib/plan_my_stuff/issue_metadata.rb +132 -21
  84. data/lib/plan_my_stuff/label.rb +100 -13
  85. data/lib/plan_my_stuff/link.rb +144 -0
  86. data/lib/plan_my_stuff/markdown.rb +13 -7
  87. data/lib/plan_my_stuff/metadata_parser.rb +51 -12
  88. data/lib/plan_my_stuff/notifications.rb +148 -0
  89. data/lib/plan_my_stuff/pipeline/completed_sweep.rb +46 -0
  90. data/lib/plan_my_stuff/pipeline/issue_linker.rb +62 -0
  91. data/lib/plan_my_stuff/pipeline/status.rb +40 -0
  92. data/lib/plan_my_stuff/pipeline/testing.rb +23 -0
  93. data/lib/plan_my_stuff/pipeline.rb +310 -0
  94. data/lib/plan_my_stuff/project.rb +63 -465
  95. data/lib/plan_my_stuff/project_item.rb +3 -409
  96. data/lib/plan_my_stuff/project_item_metadata.rb +55 -0
  97. data/lib/plan_my_stuff/project_metadata.rb +47 -0
  98. data/lib/plan_my_stuff/reminders/closer.rb +70 -0
  99. data/lib/plan_my_stuff/reminders/fire.rb +129 -0
  100. data/lib/plan_my_stuff/reminders/sweep.rb +54 -0
  101. data/lib/plan_my_stuff/reminders.rb +12 -0
  102. data/lib/plan_my_stuff/repo.rb +145 -0
  103. data/lib/plan_my_stuff/test_helpers.rb +265 -25
  104. data/lib/plan_my_stuff/testing_project.rb +292 -0
  105. data/lib/plan_my_stuff/testing_project_item.rb +218 -0
  106. data/lib/plan_my_stuff/testing_project_metadata.rb +94 -0
  107. data/lib/plan_my_stuff/user_resolver.rb +24 -3
  108. data/lib/plan_my_stuff/verifier.rb +10 -0
  109. data/lib/plan_my_stuff/version.rb +2 -2
  110. data/lib/plan_my_stuff/webhook_replayer.rb +292 -0
  111. data/lib/plan_my_stuff.rb +55 -20
  112. data/lib/tasks/plan_my_stuff.rake +331 -0
  113. metadata +99 -4
@@ -1,6 +1,337 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  namespace :plan_my_stuff do
4
+ desc 'Run the plan_my_stuff install generator (alias for `rails g plan_my_stuff:install`)'
5
+ task install: :environment do
6
+ require 'rails/generators'
7
+
8
+ require 'generators/plan_my_stuff/install/install_generator'
9
+ PlanMyStuff::Generators::InstallGenerator.start([])
10
+ end
11
+
12
+ desc 'Copy plan_my_stuff view templates (alias for `rails g plan_my_stuff:views`)'
13
+ task views: :environment do
14
+ require 'rails/generators'
15
+
16
+ require 'generators/plan_my_stuff/views/views_generator'
17
+ PlanMyStuff::Generators::ViewsGenerator.start([])
18
+ end
19
+
20
+ namespace :webhooks do
21
+ desc 'Create an organization webhook (URL=... [EVENTS=ev1,ev2]). ' \
22
+ 'Raises PlanMyStuff::ConfigurationError if config.webhook_secret is blank.'
23
+ task create_org: :environment do
24
+ url = ENV.fetch('URL') { raise(ArgumentError, 'URL env var is required') }
25
+ default_events = %w[projects_v2 projects_v2_item projects_v2_status_update]
26
+ events = ENV['EVENTS'].present? ? ENV['EVENTS'].split(',').map(&:strip).compact_blank : default_events
27
+ config = PlanMyStuff.configuration
28
+ if config.webhook_secret.blank?
29
+ raise(PlanMyStuff::ConfigurationError, 'PlanMyStuff.configuration.webhook_secret is blank')
30
+ end
31
+
32
+ hook = PlanMyStuff.client.rest(
33
+ :create_org_hook,
34
+ config.organization,
35
+ { url: url, content_type: 'json', secret: config.webhook_secret, insecure_ssl: '0' },
36
+ { events: events, active: true },
37
+ )
38
+
39
+ puts("Created org hook #{hook.id} on #{config.organization} -> #{url}")
40
+ puts("Events: #{events.join(', ')}")
41
+ end
42
+
43
+ desc 'Create a repo webhook (REPO=owner/name URL=... [EVENTS=ev1,ev2]). ' \
44
+ 'Raises PlanMyStuff::ConfigurationError if config.webhook_secret is blank.'
45
+ task create_repo: :environment do
46
+ url = ENV.fetch('URL') { raise(ArgumentError, 'URL env var is required') }
47
+ repo = ENV.fetch('REPO') do
48
+ PlanMyStuff.client.resolve_repo!
49
+ rescue PlanMyStuff::ConfigurationError, ArgumentError
50
+ raise(ArgumentError, 'REPO env var is required or configured (e.g. BrandsInsurance/PlanMyStuff)')
51
+ end
52
+ default_events = %w[pull_request issues]
53
+ events = ENV['EVENTS'].present? ? ENV['EVENTS'].split(',').map(&:strip).compact_blank : default_events
54
+ config = PlanMyStuff.configuration
55
+ if config.webhook_secret.blank?
56
+ raise(PlanMyStuff::ConfigurationError, 'PlanMyStuff.configuration.webhook_secret is blank')
57
+ end
58
+
59
+ hook = PlanMyStuff.client.rest(
60
+ :create_hook,
61
+ repo,
62
+ 'web',
63
+ { url: url, content_type: 'json', secret: config.webhook_secret, insecure_ssl: '0' },
64
+ { events: events, active: true },
65
+ )
66
+
67
+ puts("Created repo hook #{hook.id} on #{repo} -> #{url}")
68
+ puts("Events: #{events.join(', ')}")
69
+ end
70
+
71
+ desc 'Replay recent deliveries to a local endpoint (ENDPOINT_URL=... WEBHOOK_URL=... ' \
72
+ '[SCOPE=org|repo] [REPO=owner/name] [PROCESSED_FILE=...] [NON_INTERACTIVE=1])'
73
+ task replay: :environment do
74
+ require 'plan_my_stuff/webhook_replayer'
75
+
76
+ endpoint_url = ENV.fetch('ENDPOINT_URL') { raise(ArgumentError, 'ENDPOINT_URL env var is required') }
77
+ webhook_url = ENV.fetch('WEBHOOK_URL') { raise(ArgumentError, 'WEBHOOK_URL env var is required') }
78
+ scope = ENV.fetch('SCOPE', 'org').to_sym
79
+ repo = ENV.fetch('REPO', nil)
80
+ processed_file =
81
+ ENV.fetch('PROCESSED_FILE') do
82
+ Rails.root.join('tmp', 'plan_my_stuff', "webhook_replay_#{scope}_processed.txt").to_s
83
+ end
84
+ interactive = ENV['NON_INTERACTIVE'].to_s.strip.empty?
85
+
86
+ PlanMyStuff::WebhookReplayer.fetch_and_replay(
87
+ endpoint_url: endpoint_url,
88
+ webhook_url: webhook_url,
89
+ scope: scope,
90
+ repo: repo,
91
+ processed_file: processed_file,
92
+ interactive: interactive,
93
+ )
94
+ end
95
+
96
+ desc 'Continuously poll org + repo hooks and auto-replay new deliveries ' \
97
+ '(ENDPOINT_URL=... [ORG_WEBHOOK_URL=...] [REPO_WEBHOOK_URL=... REPO=owner/name] [INTERVAL=30]). ' \
98
+ 'At least one of ORG_WEBHOOK_URL or REPO_WEBHOOK_URL is required (raises ArgumentError if both absent).'
99
+ task listen: :environment do
100
+ require 'plan_my_stuff/webhook_replayer'
101
+
102
+ endpoint_url = ENV.fetch('ENDPOINT_URL') { raise(ArgumentError, 'ENDPOINT_URL env var is required') }
103
+ org_webhook_url = ENV.fetch('ORG_WEBHOOK_URL', nil)
104
+ repo_webhook_url = ENV.fetch('REPO_WEBHOOK_URL', nil)
105
+ repo = ENV.fetch('REPO', nil)
106
+ interval = ENV.fetch('INTERVAL', '30').to_i
107
+
108
+ targets = []
109
+ targets << { scope: :org, webhook_url: org_webhook_url } if org_webhook_url.present?
110
+ if repo_webhook_url.present?
111
+ raise(ArgumentError, 'REPO env var required when REPO_WEBHOOK_URL is set') if repo.blank?
112
+
113
+ targets << { scope: :repo, webhook_url: repo_webhook_url, repo: repo }
114
+ end
115
+ raise(ArgumentError, 'Set at least one of ORG_WEBHOOK_URL or REPO_WEBHOOK_URL') if targets.empty?
116
+
117
+ processed_file_for = -> (target) {
118
+ Rails.root.join('tmp', 'plan_my_stuff', "webhook_listen_#{target[:scope]}_processed.txt").to_s
119
+ }
120
+
121
+ PlanMyStuff::WebhookReplayer.listen(
122
+ targets: targets,
123
+ endpoint_url: endpoint_url,
124
+ processed_file_for: processed_file_for,
125
+ interval: interval,
126
+ )
127
+ end
128
+
129
+ desc 'Simulate an AWS SNS deployment webhook to a local endpoint ' \
130
+ '(ENDPOINT_URL=... [EVENT=SERVICE_DEPLOYMENT_COMPLETED])'
131
+ task simulate_aws: :environment do
132
+ require 'plan_my_stuff/aws_sns_simulator'
133
+
134
+ endpoint_url = ENV.fetch('ENDPOINT_URL') { raise(ArgumentError, 'ENDPOINT_URL env var is required') }
135
+ event_name = ENV.fetch('EVENT', PlanMyStuff::AwsSnsSimulator::DEFAULT_EVENT)
136
+
137
+ PlanMyStuff::AwsSnsSimulator.post!(
138
+ endpoint_url: endpoint_url,
139
+ event_name: event_name,
140
+ )
141
+ end
142
+ end
143
+
144
+ namespace :reminders do
145
+ desc 'Enqueue a RemindersSweepJob per configured repo ' \
146
+ '([REPO=<key>] to target a single repo). ' \
147
+ 'Raises PlanMyStuff::ConfigurationError when no repos are configured (config.repos empty and REPO unset).'
148
+ task sweep: :environment do
149
+ config = PlanMyStuff.configuration
150
+ repo_keys =
151
+ if ENV['REPO'].present?
152
+ [ENV['REPO'].to_sym]
153
+ else
154
+ config.repos.keys
155
+ end
156
+
157
+ if repo_keys.empty?
158
+ raise(PlanMyStuff::ConfigurationError, 'No repos configured (set config.repos or pass REPO=<key>)')
159
+ end
160
+
161
+ repo_keys.each do |key|
162
+ PlanMyStuff::RemindersSweepJob.requeue(key)
163
+ puts("Scheduled RemindersSweepJob for #{key.inspect} " \
164
+ "(#{config.repos[key] || '<unknown>'}) at #{PlanMyStuff::RemindersSweepJob.next_run.iso8601}")
165
+ end
166
+ end
167
+ end
168
+
169
+ namespace :testing do
170
+ desc 'Create a TestingProject template in the configured organization. ' \
171
+ 'Bootstraps all testing custom fields. Set the returned project number as ' \
172
+ 'config.testing_template_project_number, then configure its board view ' \
173
+ 'and "Column by: Test Status" manually in the GitHub UI. ' \
174
+ 'Options: TITLE="My Template" (default: "PMS Testing Template")'
175
+ task create_template: :environment do
176
+ title = ENV.fetch('TITLE', '[TEMPLATE] Testing Feature')
177
+
178
+ puts("Creating testing project template '#{title}' in " \
179
+ "#{PlanMyStuff.configuration.organization}...")
180
+
181
+ project = PlanMyStuff::TestingProject.create!(title: title)
182
+
183
+ puts
184
+ puts("\e[32mTemplate created successfully.\e[0m")
185
+ puts(" Title: #{project.title}")
186
+ puts(" Number: #{project.number}")
187
+ puts(" URL: #{project.url}")
188
+ puts
189
+ puts('Next steps:')
190
+ puts(' 1. Open the project in GitHub and set the board view to "Column by: Test Status"')
191
+ puts(' 2. Add to your PlanMyStuff initializer:')
192
+ puts(" config.testing_template_project_number = #{project.number}")
193
+ end
194
+ end
195
+
196
+ desc 'Print all GitHub primary rate-limit buckets for the configured token. ' \
197
+ 'Note: secondary/abuse limits and content-creation limits are NOT surfaced by GitHub ' \
198
+ 'until you hit them, so a healthy report here does not guarantee writes will succeed.'
199
+ task rate_limit: :environment do
200
+ data = PlanMyStuff.client.rest(:get, '/rate_limit').to_h
201
+ resources = data[:resources] || {}
202
+
203
+ width = resources.keys.map { |k| k.to_s.length }.max || 0
204
+ resources.sort.each do |name, bucket|
205
+ next if bucket[:used].zero?
206
+
207
+ reset_at = Time.at(bucket[:reset]).utc
208
+ reset_in = [bucket[:reset] - Time.now.to_i, 0].max
209
+ puts(format(
210
+ '%-*s used=%-5d remaining=%-5d limit=%-5d resets_at=%s (in %ds)',
211
+ width,
212
+ name,
213
+ bucket[:used],
214
+ bucket[:remaining],
215
+ bucket[:limit],
216
+ reset_at.iso8601,
217
+ reset_in,
218
+ ))
219
+ end
220
+ end
221
+
222
+ namespace :debug do
223
+ desc 'Probe GitHub issue body size limit by creating issues of increasing size, then closing them. ' \
224
+ 'Options: REPO=owner/name (default: config.default_repo) START=60000 MAX=70000 STEP=1000 ' \
225
+ 'LABEL=body-size-probe KEEP=0 (set KEEP=1 to leave issues open for inspection)'
226
+ task body_size_limit: :environment do
227
+ client = PlanMyStuff.client
228
+ repo = client.resolve_repo!(ENV.fetch('REPO', nil))
229
+ start = Integer(ENV.fetch('START', '60000'))
230
+ max = Integer(ENV.fetch('MAX', '70000'))
231
+ step = Integer(ENV.fetch('STEP', '1000'))
232
+ keep = ENV.fetch('KEEP', '0') == '1'
233
+ label = 'pms-body-size-probe'
234
+
235
+ puts("Probing #{repo} body limit: sizes #{start}..#{max} step #{step} " \
236
+ "label=#{label.inspect} (KEEP=#{keep ? '1' : '0'})")
237
+ puts
238
+
239
+ PlanMyStuff::Label.ensure!(repo: repo, name: label) if label.present?
240
+
241
+ created_numbers = []
242
+
243
+ start.step(max, step) do |size|
244
+ header = "<details><summary>#{size} chars</summary>\n\n"
245
+ footer = "\n\n</details>"
246
+ inner = 'a' * [size - header.length - footer.length, 0].max
247
+ body = "#{header}#{inner}#{footer}"
248
+ title = "[PMS body-size probe] #{size} chars"
249
+ print(format('size=%-8d ', size))
250
+
251
+ begin
252
+ options = label.present? ? { labels: [label] } : {}
253
+ result = client.rest(:create_issue, repo, title, body, **options)
254
+ number = result.respond_to?(:number) ? result.number : result[:number]
255
+ created_numbers << number
256
+ puts("OK ##{number}")
257
+ rescue Octokit::Error => e
258
+ puts("FAIL #{e.class}: #{e.message.to_s[0, 200]}")
259
+ break
260
+ end
261
+ end
262
+
263
+ puts
264
+ if keep || created_numbers.empty?
265
+ puts("Created #{created_numbers.size} issue(s); leaving open (KEEP=1).") if keep
266
+ else
267
+ puts("Closing #{created_numbers.size} probe issue(s)...")
268
+ created_numbers.each do |n|
269
+ PlanMyStuff::Issue.update!(number: n, repo: repo, state: 'closed')
270
+ rescue Octokit::Error => e
271
+ puts(" ##{n}: close failed (#{e.class})")
272
+ end
273
+ end
274
+ end
275
+
276
+ desc 'Probe GitHub issue comment body size limit by posting comments of increasing size on a single ' \
277
+ 'bootstrap issue, then closing it. ' \
278
+ 'Options: START=60000 MAX=70000 STEP=1000 KEEP=0 (set KEEP=1 to leave the bootstrap issue open)'
279
+ task comment_size_limit: :environment do
280
+ client = PlanMyStuff.client
281
+ repo = client.resolve_repo!
282
+ start = Integer(ENV.fetch('START', '60000'))
283
+ max = Integer(ENV.fetch('MAX', '70000'))
284
+ step = Integer(ENV.fetch('STEP', '1000'))
285
+ keep = ENV.fetch('KEEP', '0') == '1'
286
+ label = 'pms-comment-size-probe'
287
+
288
+ puts("Probing #{repo} comment body limit: sizes #{start}..#{max} step #{step} " \
289
+ "label=#{label.inspect} (KEEP=#{keep ? '1' : '0'})")
290
+ puts
291
+
292
+ PlanMyStuff::Label.ensure!(repo: repo, name: label)
293
+
294
+ issue = client.rest(
295
+ :create_issue,
296
+ repo,
297
+ '[PMS comment-size probe] bootstrap',
298
+ 'Bootstrap issue for comment-size probing.',
299
+ labels: [label],
300
+ )
301
+ issue_number = issue.respond_to?(:number) ? issue.number : issue[:number]
302
+ puts("Bootstrap issue: ##{issue_number}")
303
+ puts
304
+
305
+ start.step(max, step) do |size|
306
+ header = "<details><summary>#{size} chars</summary>\n\n"
307
+ footer = "\n\n</details>"
308
+ inner = 'a' * [size - header.length - footer.length, 0].max
309
+ body = "#{header}#{inner}#{footer}"
310
+ print(format('size=%-8d ', size))
311
+
312
+ begin
313
+ client.rest(:add_comment, repo, issue_number, body)
314
+ puts('OK')
315
+ rescue Octokit::Error => e
316
+ puts("FAIL #{e.class}: #{e.message.to_s[0, 200]}")
317
+ break
318
+ end
319
+ end
320
+
321
+ puts
322
+ if keep
323
+ puts("Leaving bootstrap issue ##{issue_number} open (KEEP=1).")
324
+ else
325
+ puts("Closing bootstrap issue ##{issue_number}...")
326
+ begin
327
+ PlanMyStuff::Issue.update!(number: issue_number, repo: repo, state: 'closed')
328
+ rescue Octokit::Error => e
329
+ puts(" close failed (#{e.class})")
330
+ end
331
+ end
332
+ end
333
+ end
334
+
4
335
  desc 'Verify PlanMyStuff configuration: token, org, repos, and project access'
5
336
  task verify: :environment do
6
337
  require 'plan_my_stuff/verifier'
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: plan_my_stuff
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 1.0.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Brands Insurance
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2026-04-01 00:00:00.000000000 Z
11
+ date: 2026-05-29 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -51,32 +51,75 @@ executables: []
51
51
  extensions: []
52
52
  extra_rdoc_files: []
53
53
  files:
54
+ - CHANGELOG.md
55
+ - CONFIGURATION.md
54
56
  - LICENSE
55
57
  - README.md
56
58
  - app/controllers/plan_my_stuff/application_controller.rb
57
59
  - app/controllers/plan_my_stuff/comments_controller.rb
60
+ - app/controllers/plan_my_stuff/issues/approvals_controller.rb
61
+ - app/controllers/plan_my_stuff/issues/closures_controller.rb
62
+ - app/controllers/plan_my_stuff/issues/links_controller.rb
63
+ - app/controllers/plan_my_stuff/issues/takes_controller.rb
64
+ - app/controllers/plan_my_stuff/issues/testings_controller.rb
65
+ - app/controllers/plan_my_stuff/issues/viewers_controller.rb
66
+ - app/controllers/plan_my_stuff/issues/waitings_controller.rb
58
67
  - app/controllers/plan_my_stuff/issues_controller.rb
59
68
  - app/controllers/plan_my_stuff/labels_controller.rb
69
+ - app/controllers/plan_my_stuff/project_items/assignments_controller.rb
70
+ - app/controllers/plan_my_stuff/project_items/statuses_controller.rb
60
71
  - app/controllers/plan_my_stuff/project_items_controller.rb
61
72
  - app/controllers/plan_my_stuff/projects_controller.rb
73
+ - app/controllers/plan_my_stuff/testing_project_items/results_controller.rb
74
+ - app/controllers/plan_my_stuff/testing_project_items_controller.rb
75
+ - app/controllers/plan_my_stuff/testing_projects_controller.rb
76
+ - app/controllers/plan_my_stuff/webhooks/aws_controller.rb
77
+ - app/controllers/plan_my_stuff/webhooks/github_controller.rb
78
+ - app/jobs/plan_my_stuff/application_job.rb
79
+ - app/jobs/plan_my_stuff/reminders_sweep_job.rb
62
80
  - app/views/plan_my_stuff/comments/edit.html.erb
63
81
  - app/views/plan_my_stuff/comments/partials/_form.html.erb
64
82
  - app/views/plan_my_stuff/issues/edit.html.erb
65
83
  - app/views/plan_my_stuff/issues/index.html.erb
66
84
  - app/views/plan_my_stuff/issues/new.html.erb
85
+ - app/views/plan_my_stuff/issues/partials/_approvals.html.erb
67
86
  - app/views/plan_my_stuff/issues/partials/_form.html.erb
68
87
  - app/views/plan_my_stuff/issues/partials/_labels.html.erb
88
+ - app/views/plan_my_stuff/issues/partials/_links.html.erb
69
89
  - app/views/plan_my_stuff/issues/partials/_viewers.html.erb
70
90
  - app/views/plan_my_stuff/issues/show.html.erb
91
+ - app/views/plan_my_stuff/partials/_flash.html.erb
92
+ - app/views/plan_my_stuff/projects/edit.html.erb
71
93
  - app/views/plan_my_stuff/projects/index.html.erb
94
+ - app/views/plan_my_stuff/projects/new.html.erb
95
+ - app/views/plan_my_stuff/projects/partials/_form.html.erb
72
96
  - app/views/plan_my_stuff/projects/show.html.erb
97
+ - app/views/plan_my_stuff/testing_project_items/new.html.erb
98
+ - app/views/plan_my_stuff/testing_project_items/results/new.html.erb
99
+ - app/views/plan_my_stuff/testing_projects/edit.html.erb
100
+ - app/views/plan_my_stuff/testing_projects/new.html.erb
101
+ - app/views/plan_my_stuff/testing_projects/partials/_form.html.erb
102
+ - app/views/plan_my_stuff/testing_projects/partials/_item.html.erb
103
+ - app/views/plan_my_stuff/testing_projects/partials/items/_form.html.erb
104
+ - app/views/plan_my_stuff/testing_projects/show.html.erb
73
105
  - config/routes.rb
74
106
  - lib/generators/plan_my_stuff/install/install_generator.rb
75
107
  - lib/generators/plan_my_stuff/install/templates/initializer.rb
76
108
  - lib/generators/plan_my_stuff/views/views_generator.rb
77
109
  - lib/plan_my_stuff.rb
78
110
  - lib/plan_my_stuff/application_record.rb
111
+ - lib/plan_my_stuff/approval.rb
112
+ - lib/plan_my_stuff/archive.rb
113
+ - lib/plan_my_stuff/archive/sweep.rb
114
+ - lib/plan_my_stuff/attachment.rb
115
+ - lib/plan_my_stuff/attachment_uploader.rb
116
+ - lib/plan_my_stuff/aws_sns_simulator.rb
79
117
  - lib/plan_my_stuff/base_metadata.rb
118
+ - lib/plan_my_stuff/base_project.rb
119
+ - lib/plan_my_stuff/base_project_extractions/graphql_hydration.rb
120
+ - lib/plan_my_stuff/base_project_item.rb
121
+ - lib/plan_my_stuff/base_project_metadata.rb
122
+ - lib/plan_my_stuff/cache.rb
80
123
  - lib/plan_my_stuff/client.rb
81
124
  - lib/plan_my_stuff/comment.rb
82
125
  - lib/plan_my_stuff/comment_metadata.rb
@@ -84,17 +127,43 @@ files:
84
127
  - lib/plan_my_stuff/custom_fields.rb
85
128
  - lib/plan_my_stuff/engine.rb
86
129
  - lib/plan_my_stuff/errors.rb
130
+ - lib/plan_my_stuff/graphql/queries.rb
87
131
  - lib/plan_my_stuff/issue.rb
132
+ - lib/plan_my_stuff/issue_extractions/approvals.rb
133
+ - lib/plan_my_stuff/issue_extractions/links.rb
134
+ - lib/plan_my_stuff/issue_extractions/viewers.rb
135
+ - lib/plan_my_stuff/issue_extractions/waiting.rb
136
+ - lib/plan_my_stuff/issue_field.rb
137
+ - lib/plan_my_stuff/issue_field_translation.rb
138
+ - lib/plan_my_stuff/issue_field_value_set.rb
88
139
  - lib/plan_my_stuff/issue_metadata.rb
89
140
  - lib/plan_my_stuff/label.rb
141
+ - lib/plan_my_stuff/link.rb
90
142
  - lib/plan_my_stuff/markdown.rb
91
143
  - lib/plan_my_stuff/metadata_parser.rb
144
+ - lib/plan_my_stuff/notifications.rb
145
+ - lib/plan_my_stuff/pipeline.rb
146
+ - lib/plan_my_stuff/pipeline/completed_sweep.rb
147
+ - lib/plan_my_stuff/pipeline/issue_linker.rb
148
+ - lib/plan_my_stuff/pipeline/status.rb
149
+ - lib/plan_my_stuff/pipeline/testing.rb
92
150
  - lib/plan_my_stuff/project.rb
93
151
  - lib/plan_my_stuff/project_item.rb
152
+ - lib/plan_my_stuff/project_item_metadata.rb
153
+ - lib/plan_my_stuff/project_metadata.rb
154
+ - lib/plan_my_stuff/reminders.rb
155
+ - lib/plan_my_stuff/reminders/closer.rb
156
+ - lib/plan_my_stuff/reminders/fire.rb
157
+ - lib/plan_my_stuff/reminders/sweep.rb
158
+ - lib/plan_my_stuff/repo.rb
94
159
  - lib/plan_my_stuff/test_helpers.rb
160
+ - lib/plan_my_stuff/testing_project.rb
161
+ - lib/plan_my_stuff/testing_project_item.rb
162
+ - lib/plan_my_stuff/testing_project_metadata.rb
95
163
  - lib/plan_my_stuff/user_resolver.rb
96
164
  - lib/plan_my_stuff/verifier.rb
97
165
  - lib/plan_my_stuff/version.rb
166
+ - lib/plan_my_stuff/webhook_replayer.rb
98
167
  - lib/tasks/plan_my_stuff.rake
99
168
  homepage: https://github.com/brandsinsurance/PlanMyStuff/
100
169
  licenses:
@@ -103,8 +172,34 @@ metadata:
103
172
  rubygems_mfa_required: 'true'
104
173
  homepage_uri: https://github.com/brandsinsurance/PlanMyStuff/
105
174
  source_code_uri: https://github.com/brandsinsurance/PlanMyStuff
106
- changelog_uri: https://github.com/brandsinsurance/PlanMyStuff/blob/main/CHANGELOG.adoc
107
- post_install_message:
175
+ changelog_uri: https://github.com/brandsinsurance/PlanMyStuff/blob/main/CHANGELOG.md
176
+ post_install_message: |
177
+ Thanks for installing plan_my_stuff!
178
+
179
+ Getting started:
180
+
181
+ rake plan_my_stuff:install
182
+ Generates config/initializers/plan_my_stuff.rb and mounts the engine
183
+
184
+ rake plan_my_stuff:views
185
+ Copies view templates to app/views/plan_my_stuff/ (optional)
186
+
187
+ Available rake tasks:
188
+
189
+ plan_my_stuff:verify
190
+ Verify token, org, repos, and project access
191
+
192
+ plan_my_stuff:webhooks:create_org URL=... [EVENTS=...]
193
+ plan_my_stuff:webhooks:create_repo URL=... [REPO=owner/name] [EVENTS=...]
194
+ plan_my_stuff:webhooks:replay ENDPOINT_URL=... WEBHOOK_URL=... [SCOPE=org|repo]
195
+ plan_my_stuff:webhooks:listen ENDPOINT_URL=... [ORG_WEBHOOK_URL=...] [REPO_WEBHOOK_URL=... REPO=...]
196
+ plan_my_stuff:webhooks:simulate_aws ENDPOINT_URL=... [EVENT=...]
197
+
198
+ plan_my_stuff:reminders:sweep [REPO=<key>]
199
+
200
+ plan_my_stuff:testing:create_template [TITLE=...]
201
+
202
+ Run `rake -D plan_my_stuff` for full descriptions.
108
203
  rdoc_options: []
109
204
  require_paths:
110
205
  - lib