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
@@ -0,0 +1,65 @@
1
+ <h1><%= @project.title %></h1>
2
+
3
+ <p>
4
+ <%= link_to('Edit', plan_my_stuff.edit_testing_project_path(@project.number)) %>
5
+ <% if @support_user && @project.url.present? %>
6
+ <%= link_to('View on GitHub', @project.url, target: '_blank', rel: 'noopener') %>
7
+ <% end %>
8
+ </p>
9
+
10
+ <% if @support_user && @project.metadata.subject_urls.present? %>
11
+ <p>
12
+ <strong>Subject URLs:</strong>
13
+ <% @project.metadata.subject_urls.each do |url| %>
14
+ <%= link_to(url, url, target: '_blank', rel: 'noopener') %>
15
+ <% end %>
16
+ </p>
17
+ <% end %>
18
+
19
+ <% if @project.metadata.due_date.present? %>
20
+ <p><strong>Due:</strong> <%= @project.metadata.due_date %></p>
21
+ <% end %>
22
+
23
+ <% if @statuses.present? %>
24
+ <table>
25
+ <thead>
26
+ <tr>
27
+ <% @statuses.each do |status| %>
28
+ <th><%= status %></th>
29
+ <% end %>
30
+ </tr>
31
+ </thead>
32
+ <tbody>
33
+ <tr>
34
+ <% @statuses.each do |status| %>
35
+ <td>
36
+ <% items = @items_by_status[status] || [] %>
37
+ <% items.each do |item| %>
38
+ <%= render({
39
+ partial: 'plan_my_stuff/testing_projects/partials/item',
40
+ locals: { item: item, project: @project, statuses: @statuses },
41
+ }) %>
42
+ <% end %>
43
+ <% if items.empty? %>
44
+ <em>No items</em>
45
+ <% end %>
46
+ </td>
47
+ <% end %>
48
+ </tr>
49
+ </tbody>
50
+ </table>
51
+ <% else %>
52
+ <p>No statuses configured for this project.</p>
53
+ <% end %>
54
+
55
+ <h2>Add Item</h2>
56
+
57
+ <p><%= link_to('Add item with details', plan_my_stuff.new_testing_project_item_path(@project.number)) %></p>
58
+
59
+ <%= form_with(url: plan_my_stuff.testing_project_items_path(@project.number), method: :post, local: true) do |form| %>
60
+ <%= form.label(:title, 'Title') %>
61
+ <%= form.text_field(:title, required: true) %>
62
+ <%= form.label(:body, 'Body') %>
63
+ <%= form.text_area(:body) %>
64
+ <%= form.submit('Add Item') %>
65
+ <% end %>
data/config/routes.rb CHANGED
@@ -1,24 +1,52 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  PlanMyStuff::Engine.routes.draw do
4
- resources :issues, except: %i[destroy] do
5
- member do
6
- patch :close
7
- patch :reopen
8
- post :add_viewers
9
- delete :remove_viewer
4
+ config = PlanMyStuff.configuration
5
+ mount_groups = config.mount_groups
6
+
7
+ if mount_groups.fetch(:issues, true)
8
+ resources :issues, except: %i[destroy], controller: config.controller_for(:issues) do
9
+ resource :closure, only: %i[create destroy], controller: config.controller_for(:'issues/closures')
10
+ resource :waiting, only: %i[create destroy], controller: config.controller_for(:'issues/waitings')
11
+ resources :viewers, only: %i[create destroy], controller: config.controller_for(:'issues/viewers')
12
+ if config.pipeline_enabled
13
+ resource(
14
+ :take,
15
+ only: %i[create destroy],
16
+ controller: config.controller_for(:'issues/takes'),
17
+ )
18
+ resource(
19
+ :testing,
20
+ only: %i[create destroy],
21
+ controller: config.controller_for(:'issues/testings'),
22
+ )
23
+ end
24
+ resources :comments, only: %i[create edit update], controller: config.controller_for(:comments)
25
+ resources :labels, only: %i[create destroy], controller: config.controller_for(:labels)
26
+ resources :links, only: %i[create destroy], controller: config.controller_for(:'issues/links')
27
+ resources :approvals, only: %i[create update destroy], controller: config.controller_for(:'issues/approvals')
28
+ end
29
+ end
30
+
31
+ if mount_groups.fetch(:webhooks, true) && config.pipeline_enabled
32
+ namespace :webhooks do
33
+ resource :github, only: %i[create], controller: config.controller_for(:'webhooks/github')
34
+ resource :aws, only: %i[create], controller: config.controller_for(:'webhooks/aws')
10
35
  end
11
- resources :comments, only: %i[create edit update]
12
- post 'labels', to: 'labels#add_to_issue', as: :add_label
13
- delete 'labels/:name', to: 'labels#remove_from_issue', as: :remove_label
14
36
  end
15
37
 
16
- resources :projects, only: %i[index show] do
17
- resources :items, only: %i[create], controller: 'project_items' do
18
- member do
19
- patch :move
20
- patch :assign
21
- patch :unassign
38
+ if mount_groups.fetch(:projects, true)
39
+ resources :projects, except: %i[destroy], controller: config.controller_for(:projects) do
40
+ resources :items, only: %i[create destroy], controller: config.controller_for(:project_items) do
41
+ resource :status, only: %i[update], controller: config.controller_for(:'project_items/statuses')
42
+ resource :assignment, only: %i[update destroy], controller: config.controller_for(:'project_items/assignments')
43
+ end
44
+ end
45
+
46
+ resources :testing_projects, except: %i[destroy index], controller: config.controller_for(:testing_projects) do
47
+ resources :items, only: %i[new create], controller: config.controller_for(:testing_project_items) do
48
+ resource :status, only: %i[update], controller: config.controller_for(:'project_items/statuses')
49
+ resource :result, only: %i[new create], controller: config.controller_for(:'testing_project_items/results')
22
50
  end
23
51
  end
24
52
  end
@@ -1,12 +1,16 @@
1
1
  # frozen_string_literal: true
2
2
 
3
- PMS.configure do |config|
3
+ PlanMyStuff.configure do |config|
4
4
  # --------------------------------------------------------------------------
5
5
  # Authentication (required)
6
6
  # --------------------------------------------------------------------------
7
7
  # GitHub PAT with `repo` and `project` scopes. All API calls use this token.
8
8
  config.access_token = Rails.application.credentials.dig(:plan_my_stuff, :github_token)
9
9
 
10
+ # Classic PAT (requires `repo` scope) for the Issues Import API (golden-comet-preview).
11
+ # Fine-grained tokens are not supported by that endpoint.
12
+ # config.import_access_token = Rails.application.credentials.dig(:plan_my_stuff, :github_import_token)
13
+
10
14
  # --------------------------------------------------------------------------
11
15
  # Organization (required)
12
16
  # --------------------------------------------------------------------------
@@ -20,12 +24,34 @@ PMS.configure do |config|
20
24
  # config.repos[:underwriter] = 'YourOrganization/Underwriter'
21
25
  # config.default_repo = :element
22
26
 
27
+ # Human-readable prefix used as the `Issue#to_param` so URLs read
28
+ # `/issues/Element-1234` instead of `/issues/1234`. Missing keys fall back
29
+ # to `key.to_s.titleize`; only repos whose key doesn't titleize cleanly
30
+ # need an entry.
31
+ # config.repo_nicknames = { safety: 'Compliance' }
32
+
33
+ # --------------------------------------------------------------------------
34
+ # Attachments
35
+ # --------------------------------------------------------------------------
36
+ # Bare repo name (under `organization`) that stores uploaded attachment
37
+ # binaries. The repo must already exist; the uploader does not create it.
38
+ # Attachments commit onto `config.main_branch` under
39
+ # `<repo_key_or_name>/issue-<number>/<uuid>.<ext>`.
40
+ # config.attachment_repo = 'pms-attachments'
41
+
23
42
  # --------------------------------------------------------------------------
24
43
  # Projects
25
44
  # --------------------------------------------------------------------------
26
45
  # Default GitHub Projects V2 number for add_to_project calls.
27
46
  # config.default_project_number = 14
28
47
 
48
+ # Template project to clone when creating new Testing Projects.
49
+ # When set, TestingProject.create! copies this project (preserving its
50
+ # custom fields and board layout) instead of bootstrapping fields from scratch.
51
+ # The template should be a pre-configured testing project with the correct
52
+ # fields and board view already set up in the GitHub UI.
53
+ # config.testing_template_project_number = 42
54
+
29
55
  # --------------------------------------------------------------------------
30
56
  # App identity
31
57
  # --------------------------------------------------------------------------
@@ -49,6 +75,14 @@ PMS.configure do |config|
49
75
  # config.support_method = :support?
50
76
  # config.support_method = ->(user) { user.role.in?(%w[support admin]) }
51
77
 
78
+ # Map of app user id (whatever `user_id_method` returns) to GitHub login.
79
+ # Used by the "Take" UI flow to assign the GitHub user when a support
80
+ # user claims an issue.
81
+ # config.github_login_for = {
82
+ # 1 => 'some_username',
83
+ # 2 => 'octocat',
84
+ # }
85
+
52
86
  # --------------------------------------------------------------------------
53
87
  # Engine authentication
54
88
  # --------------------------------------------------------------------------
@@ -65,31 +99,45 @@ PMS.configure do |config|
65
99
  # :commonmarker (default) or :redcarpet -- the chosen gem must be in your Gemfile.
66
100
  # config.markdown_renderer = :commonmarker
67
101
 
102
+ # Default options passed to the markdown renderer. Per-call options merge on top.
103
+ # For :commonmarker -- passed as `options:` to `Commonmarker.to_html`:
104
+ # config.markdown_options = { render: { hardbreaks: true } }
105
+ # For :redcarpet -- :render_options and :renderer are extracted for the HTML
106
+ # renderer; remaining keys are extensions passed to `Redcarpet::Markdown.new`:
107
+ # config.markdown_options = {
108
+ # render_options: { hard_wrap: true, no_styles: true },
109
+ # autolink: true,
110
+ # }
111
+
68
112
  # --------------------------------------------------------------------------
69
113
  # URL prefix
70
114
  # --------------------------------------------------------------------------
71
- # Prefix for building user-facing ticket URLs in your app.
72
- # config.issues_url_prefix = 'https://myapp.example.com/tickets/'
115
+ # Prefix for building user-facing ticket URLs in your app. The issue
116
+ # number is appended to form `Issue#user_link`, which the gem also
117
+ # writes as the visible body of the GitHub issue.
118
+ # Recommended default:
119
+ # url_options = Rails.application.routes.default_url_options
120
+ # config.issues_url_prefix =
121
+ # "#{url_options[:protocol] || 'http'}://#{url_options[:host]}/issues"
73
122
 
74
123
  # --------------------------------------------------------------------------
75
- # Request gateway
124
+ # Notification actor
76
125
  # --------------------------------------------------------------------------
77
- # Proc returning boolean, or nil (always send). When it returns false the
78
- # request is deferred to a background job instead of hitting GitHub.
79
- # config.should_send_request = -> { !MaintenanceMode.active? }
80
-
81
- # Map of action type to job class name for deferred requests.
82
- # config.job_classes = {
83
- # create_ticket: 'PmsCreateTicketJob',
84
- # post_comment: 'PmsPostCommentJob',
85
- # update_status: 'PmsUpdateStatusJob'
86
- # }
126
+ # Fallback actor for notification events (*.plan_my_stuff) when a caller
127
+ # does not pass an explicit user: kwarg. Proc/lambda called at event time.
128
+ # config.current_user = -> { Current.user }
87
129
 
88
- # Custom notifier for deferred requests (proc/class), or nil to use
89
- # the built-in DeferredMailer.
90
- # config.deferred_notifier = nil
91
- # config.deferred_email_from = 'noreply@example.com'
92
- # config.deferred_email_to = 'it-support@example.com'
130
+ # --------------------------------------------------------------------------
131
+ # Controller rescue hook
132
+ # --------------------------------------------------------------------------
133
+ # Invoked from every gem controller rescue block, after the gem logs the
134
+ # error class/message and stack trace via Rails.logger.error and just
135
+ # before the user-facing flash + redirect/render. Forwards the rescued
136
+ # exception so consuming apps can fire monitoring even though the rescue
137
+ # swallows the error. Receives the rescued exception; return value is
138
+ # ignored. Defaults to nil (no-op).
139
+ #
140
+ # config.controller_rescue = ->(error) { MonitoringService.notice_error(error) }
93
141
 
94
142
  # --------------------------------------------------------------------------
95
143
  # Custom fields
@@ -100,7 +148,241 @@ PMS.configure do |config|
100
148
  # Supported types: :string, :integer, :boolean, :array, :hash
101
149
  # required: true means the key must be present (value can be nil, [], etc.)
102
150
  #
151
+ # Shared fields (available on both issues and comments):
103
152
  config.custom_fields = {
104
- notification_recipients: { type: :array, required: true },
153
+ # notification_recipients: { type: :array, required: true },
105
154
  }
155
+
156
+ # Issue-only fields (merged on top of shared, context wins on conflicts):
157
+ # config.issue_custom_fields = {
158
+ # ticket_type: { type: :string, required: true },
159
+ # }
160
+
161
+ # Comment-only fields (merged on top of shared, context wins on conflicts):
162
+ # config.comment_custom_fields = {}
163
+
164
+ # Project-only fields (merged on top of shared, context wins on conflicts):
165
+ # config.project_custom_fields = {}
166
+
167
+ # Testing-project-only fields (merged on top of shared, context wins on conflicts):
168
+ # config.testing_custom_fields = {}
169
+
170
+ # --------------------------------------------------------------------------
171
+ # Issue types (org-side renames)
172
+ # --------------------------------------------------------------------------
173
+ # The gem speaks in canonical type names: 'Bug', 'Feature',
174
+ # 'IT Issue / Hardware', 'Other', 'Performance', 'Question', 'Task'.
175
+ # Symbol shortcuts (`:bug`, `:feature`, `:it_issue`, `:other`, `:performance`,
176
+ # `:question`, `:task`) resolve to those canonical names automatically.
177
+ # If your GitHub org uses different names for any of them, map the
178
+ # canonical name to your org's name here and PMS will translate on
179
+ # write. Missing keys pass through unchanged.
180
+ #
181
+ # config.issue_types = {
182
+ # 'Bug' => 'User Bug',
183
+ # 'Feature' => 'Enhancement',
184
+ # }
185
+
186
+ # --------------------------------------------------------------------------
187
+ # Issue Fields (public preview)
188
+ # --------------------------------------------------------------------------
189
+ # GitHub Issue Fields is a per-org public preview. Default is true (opt-out).
190
+ # Flip to false if your org has not been admitted to the preview - read paths
191
+ # return an empty set and write paths raise IssueFieldsNotEnabledError instead
192
+ # of letting a raw GraphQL error bubble.
193
+ #
194
+ # config.issue_fields_enabled = false
195
+
196
+ # Rename the native Issue Fields the gem refers to internally to whatever your
197
+ # org actually calls them. Keys are the gem's canonical names; values are your
198
+ # org's field names. Applied on reads, writes, and filters; unmapped names pass
199
+ # through unchanged.
200
+ # config.issue_field_names = {
201
+ # 'Issue Status' => 'Status',
202
+ # 'Priority' => 'Prio',
203
+ # }
204
+
205
+ # Rename single-select option labels, nested by canonical field name. Keys are
206
+ # the gem's canonical values; values are your org's option labels. Unmapped
207
+ # fields / values pass through unchanged.
208
+ # config.issue_field_values = {
209
+ # 'Issue Status' => {
210
+ # 'Submitted' => 'Triaged',
211
+ # 'Waiting on Reply' => 'Awaiting Customer',
212
+ # 'Open' => 'Open',
213
+ # 'Reopened' => 'Reopened',
214
+ # },
215
+ # }
216
+
217
+ # --------------------------------------------------------------------------
218
+ # Boot behavior
219
+ # --------------------------------------------------------------------------
220
+ # Eager-load the engine's controllers in after_initialize so that
221
+ # `defined?(PlanMyStuff::SomeController)` resolves without first referencing
222
+ # the constant. Opt in if the host app probes engine controllers via
223
+ # `defined?` in dev mode.
224
+ #
225
+ # config.eager_load_controllers_on_boot = true
226
+
227
+ # --------------------------------------------------------------------------
228
+ # Release pipeline
229
+ # --------------------------------------------------------------------------
230
+ # config.pipeline_enabled = true
231
+ # config.pipeline_project_number = 14
232
+
233
+ # GitHub webhook HMAC-SHA256 secret (required when webhook routes are mounted).
234
+ # config.webhook_secret = Rails.application.credentials.dig(:plan_my_stuff, :webhook_secret)
235
+
236
+ # Configurable display aliases for pipeline statuses.
237
+ # config.pipeline_statuses = {
238
+ # 'Submitted' => 'Triaged',
239
+ # 'Completed' => 'Done',
240
+ # }
241
+
242
+ # Display name for the Testing single-select field on the pipeline project.
243
+ # config.pipeline_testing_field_name = 'Testing'
244
+
245
+ # Display labels for the canonical Testing field option keys.
246
+ # config.pipeline_testing_values = {
247
+ # active: 'Testing',
248
+ # inactive: 'Not testing',
249
+ # }
250
+
251
+ # Whether the pipeline sweep removes aged-out Completed items.
252
+ # config.pipeline_completion_purge_enabled = true
253
+
254
+ # Hours after a project item's last update at which the sweep removes it
255
+ # from the pipeline if its status is Completed.
256
+ # config.pipeline_completion_ttl_hours = 24
257
+
258
+ # config.main_branch = 'main'
259
+ # config.production_branch = 'production'
260
+
261
+ # --------------------------------------------------------------------------
262
+ # Follow-up reminders
263
+ # --------------------------------------------------------------------------
264
+ # Daily sweep that fires `issue_reminder_due.plan_my_stuff` events on
265
+ # waiting issues and auto-closes issues that exceed the inactivity
266
+ # ceiling. Consuming app is responsible for the initial enqueue:
267
+ #
268
+ # # Enqueue one sweep job per configured repo:
269
+ # rake plan_my_stuff:reminders:sweep
270
+ #
271
+ # # Or a single repo:
272
+ # rake plan_my_stuff:reminders:sweep REPO=element
273
+ #
274
+ # Each job self-requeues after perform (default: 6:30am ET next day);
275
+ # running the rake task once on app boot is enough. Override
276
+ # `RemindersSweepJob.next_run` on a subclass to change the cadence.
277
+ #
278
+ # config.reminders_enabled = true
279
+ #
280
+ # Days-since-waiting at which reminders fire. Per-issue override via
281
+ # `issue.metadata.reminder_days = [...]`.
282
+ # config.reminder_days = [1, 3, 7, 10, 14, 18]
283
+ #
284
+ # Days of continuous waiting before the sweep auto-closes the issue
285
+ # (emits `issue.closed_inactive` instead of the regular `issue.closed`).
286
+ # config.inactivity_close_days = 30
287
+ #
288
+ # Label names the gem creates and manages on waiting/inactive issues.
289
+ # config.waiting_on_user_label = 'waiting-on-user'
290
+ # config.waiting_on_approval_label = 'waiting-on-approval'
291
+ # config.user_inactive_label = 'user-inactive'
292
+
293
+ # --------------------------------------------------------------------------
294
+ # Auto-archiving of aged-closed issues
295
+ # --------------------------------------------------------------------------
296
+ # Piggybacks the RemindersSweepJob. For every issue closed more than
297
+ # `archive_closed_after_days` ago (based on GitHub's `closed_at`) the
298
+ # sweep: adds the configured label, locks the conversation, removes
299
+ # the issue from every Projects V2 board, stamps `metadata.archived_at`,
300
+ # and emits `issue_archived.plan_my_stuff` with `reason: :aged_closed`.
301
+ # Issues auto-closed by the inactivity sweep (closed_by_inactivity) are
302
+ # excluded. Non-PMS issues are excluded.
303
+ #
304
+ # config.archiving_enabled = true
305
+ # config.archive_closed_after_days = 90
306
+ # config.archived_label = 'archived'
307
+
308
+ # --------------------------------------------------------------------------
309
+ # AWS webhook (ECS deployments via SNS)
310
+ # --------------------------------------------------------------------------
311
+ # Expected SNS topic ARN for webhook validation.
312
+ # config.sns_topic_arn = 'arn:aws:sns:us-east-1:123456:ecs-deploy-topic'
313
+
314
+ # ECS service identifier - suffix matched against resource ARNs in events.
315
+ # config.aws_service_identifier = 'myapp-production-web'
316
+
317
+ # Commit hash of the currently deploying build. Prefix-matched against
318
+ # issue metadata commit_sha on SERVICE_DEPLOYMENT_COMPLETED events.
319
+ # config.production_commit_sha = Rails.configuration.x.image_tag
320
+
321
+ # Toggle for processing AWS webhook events (default: Rails.env.production?).
322
+ # config.process_aws_webhooks = Rails.env.production?
323
+
324
+ # SNS signature verifier (default: Aws::SNS::MessageVerifier). Must respond
325
+ # to authenticate!(raw_body). Provide aws-sdk-sns in your Gemfile.
326
+ # config.sns_verifier_class = Aws::SNS::MessageVerifier
327
+ # config.sns_verifier_error = Aws::SNS::MessageVerifier::VerificationError
328
+
329
+ # --------------------------------------------------------------------------
330
+ # Caching
331
+ # --------------------------------------------------------------------------
332
+ # ETag-based HTTP caching of GitHub reads via Rails.cache. Defaults to true.
333
+ # config.cache_enabled = true
334
+
335
+ # Opaque version string baked into every PMS cache key. Bump it to invalidate
336
+ # all cached entries from the consuming app's side (e.g. after a deploy).
337
+ # config.cache_version = Rails.configuration.x.image_tag
338
+
339
+ # --------------------------------------------------------------------------
340
+ # Route mounting
341
+ # --------------------------------------------------------------------------
342
+ # Per-group toggles for which engine route groups to mount.
343
+ # config.mount_groups = { webhooks: true, issues: true, projects: true }
344
+
345
+ # --------------------------------------------------------------------------
346
+ # Controller overrides (Devise-style)
347
+ # --------------------------------------------------------------------------
348
+ # Swap in your own controller for any mounted route group. Typical use is
349
+ # to subclass the gem controller and slot the subclass in here:
350
+ #
351
+ # # app/controllers/issues_controller.rb
352
+ # class IssuesController < PlanMyStuff::IssuesController
353
+ # before_action :require_admin
354
+ # end
355
+ #
356
+ # # config/initializers/plan_my_stuff.rb
357
+ # config.controllers = { issues: 'issues' }
358
+ #
359
+ # Unset keys fall back to the gem default.
360
+ #
361
+ # Controllable keys (with gem defaults):
362
+ # :issues => 'plan_my_stuff/issues'
363
+ # :comments => 'plan_my_stuff/comments'
364
+ # :labels => 'plan_my_stuff/labels'
365
+ # :projects => 'plan_my_stuff/projects'
366
+ # :project_items => 'plan_my_stuff/project_items'
367
+ # :testing_projects => 'plan_my_stuff/testing_projects'
368
+ # :testing_project_items => 'plan_my_stuff/testing_project_items'
369
+ # :'issues/closures' => 'plan_my_stuff/issues/closures'
370
+ # :'issues/viewers' => 'plan_my_stuff/issues/viewers'
371
+ # :'issues/takes' => 'plan_my_stuff/issues/takes'
372
+ # :'issues/testings' => 'plan_my_stuff/issues/testings'
373
+ # :'issues/waitings' => 'plan_my_stuff/issues/waitings'
374
+ # :'issues/links' => 'plan_my_stuff/issues/links'
375
+ # :'issues/approvals' => 'plan_my_stuff/issues/approvals'
376
+ # :'project_items/statuses' => 'plan_my_stuff/project_items/statuses'
377
+ # :'project_items/assignments' => 'plan_my_stuff/project_items/assignments'
378
+ # :'testing_project_items/results' => 'plan_my_stuff/testing_project_items/results'
379
+ # :'webhooks/github' => 'plan_my_stuff/webhooks/github'
380
+ # :'webhooks/aws' => 'plan_my_stuff/webhooks/aws'
381
+
382
+ # --------------------------------------------------------------------------
383
+ # Parent controller
384
+ # --------------------------------------------------------------------------
385
+ # Parent class for PlanMyStuff::ApplicationController. Override when the
386
+ # consuming app does not use the `ApplicationController` name.
387
+ # config.parent_controller = 'MyOtherApplicationController'
106
388
  end