issue-db 1.2.0 → 1.3.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: 740a1a64de7f9ab45fd7932682999972139392b4c8d8f48105cde4f49e9474b1
4
- data.tar.gz: 6219653cb7f2303482e039f906ae788e4d9f9a89131df12e750ef35cc0f8a976
3
+ metadata.gz: 8c165f569fbb2648a4c73a0bf4007f67e6da2ff2dae28132af424d9eb9844575
4
+ data.tar.gz: 224bc98fcc029e417e620e4d8efaa79030fdf1a767558d126ab22145fec66ac7
5
5
  SHA512:
6
- metadata.gz: 743c37f2ca18320e7ce68f4c5f88b2275a19c93f2239d10c7a1b5102ffb089444fb79ca947efef6dd47792bfeeafe82ead49812e92605f2ccafa0b351ce9ac43
7
- data.tar.gz: cb0eb761ee2dfa89c3fc0f8d096957269905052778756a09fc755c3a5975264c690e725e38f707e4a10be618e8255f6b2cde26d1755d4ddac3d06c919e7499b7
6
+ metadata.gz: e870e848ab6765beb5470008c38c03c9d0133fa22c9bd74a93888bbef7e59fb001dc18f9311106bdc8e43c4ef442eeb8f9f18fd8e18df37fcc7f9e9e5e4a1ad2
7
+ data.tar.gz: 5932e25bbc9bd9f1bf68638d38b61675ac5a497c4cc2fc2e59e121bdc838a3c2759d45a32c3e47014c8e88f6000116d33227379336fc3fbe1370107a4613402f
data/README.md CHANGED
@@ -88,6 +88,23 @@ record = db.create("order_number_123", { location: "London", items: [ "cookies",
88
88
  # more on this in another section of the README below
89
89
  options = { body_before: "some markdown text before the data", body_after: "some markdown text after the data" }
90
90
  record = db.create("order_number_123", { location: "London", items: [ "cookies", "espresso" ] }, options)
91
+
92
+ # with the `labels` option to add additional GitHub labels to the issue (in addition to the library-managed label)
93
+ options = { labels: ["priority:high", "customer:premium"] }
94
+ record = db.create("order_number_123", { location: "London", items: [ "cookies", "espresso" ] }, options)
95
+
96
+ # with the `assignees` option to assign GitHub users to the issue
97
+ options = { assignees: ["alice", "bob"] }
98
+ record = db.create("order_number_123", { location: "London", items: [ "cookies", "espresso" ] }, options)
99
+
100
+ # with multiple options combined
101
+ options = {
102
+ labels: ["priority:high", "customer:premium"],
103
+ assignees: ["alice", "bob"],
104
+ body_before: "some markdown text before the data",
105
+ body_after: "some markdown text after the data"
106
+ }
107
+ record = db.create("order_number_123", { location: "London", items: [ "cookies", "espresso" ] }, options)
91
108
  ```
92
109
 
93
110
  Notes:
@@ -125,6 +142,23 @@ record = db.update("order_number_123", { location: "London", items: [ "cookies",
125
142
  # more on this in another section of the README below
126
143
  options = { body_before: "# Order 123\n\nData:", body_after: "Please do not edit the body of this issue" }
127
144
  record = db.update("order_number_123", { location: "London", items: [ "cookies", "espresso", "chips" ] }, options)
145
+
146
+ # with the `labels` option to add additional GitHub labels to the issue (in addition to the library-managed label)
147
+ options = { labels: ["status:processed", "priority:low"] }
148
+ record = db.update("order_number_123", { location: "London", items: [ "cookies", "espresso", "chips" ] }, options)
149
+
150
+ # with the `assignees` option to assign GitHub users to the issue
151
+ options = { assignees: ["charlie", "diana"] }
152
+ record = db.update("order_number_123", { location: "London", items: [ "cookies", "espresso", "chips" ] }, options)
153
+
154
+ # with multiple options combined
155
+ options = {
156
+ labels: ["status:processed", "priority:low"],
157
+ assignees: ["charlie", "diana"],
158
+ body_before: "# Order 123\n\nData:",
159
+ body_after: "Please do not edit the body of this issue"
160
+ }
161
+ record = db.update("order_number_123", { location: "London", items: [ "cookies", "espresso", "chips" ] }, options)
128
162
  ```
129
163
 
130
164
  ### `db.delete(key, options = {})`
@@ -136,6 +170,21 @@ Example:
136
170
 
137
171
  ```ruby
138
172
  record = db.delete("order_number_123")
173
+
174
+ # with the `labels` option to add additional GitHub labels to the issue before closing it
175
+ options = { labels: ["archived", "completed"] }
176
+ record = db.delete("order_number_123", options)
177
+
178
+ # with the `assignees` option to assign GitHub users to the issue before closing it
179
+ options = { assignees: ["alice"] }
180
+ record = db.delete("order_number_123", options)
181
+
182
+ # with multiple options combined
183
+ options = {
184
+ labels: ["archived", "completed"],
185
+ assignees: ["alice"]
186
+ }
187
+ record = db.delete("order_number_123", options)
139
188
  ```
140
189
 
141
190
  ### `db.list_keys(options = {})`
@@ -195,6 +244,153 @@ This section will go into detail around how you can configure the `issue-db` gem
195
244
  | `GH_APP_ALGO` | The algo to use for your GitHub App if providing a private key | `RS256` |
196
245
  | `ISSUE_DB_GITHUB_TOKEN` | The GitHub personal access token to use for authenticating with the GitHub API. You can also use a GitHub app or pass in your own authenticated Octokit.rb instance | `nil` |
197
246
 
247
+ ## Labels 🏷️
248
+
249
+ The `issue-db` gem uses GitHub issue labels for organization and management. Here's how labels work:
250
+
251
+ ### Library-Managed Label
252
+
253
+ The gem automatically applies a library-managed label (default: `issue-db`) to all issues it creates. This label:
254
+
255
+ - **Cannot be modified or removed** by users (the gem will always ensure it's present)
256
+ - Is used to identify which issues in the repository are managed by the `issue-db` gem
257
+ - Can be customized by setting the `ISSUE_DB_LABEL` environment variable or passing the `label` parameter to `IssueDB.new()`
258
+
259
+ ### Additional Custom Labels
260
+
261
+ You can add your own custom labels to issues when creating, updating, or deleting records by using the `labels` option:
262
+
263
+ ```ruby
264
+ # Add custom labels when creating a record
265
+ options = { labels: ["priority:high", "customer:premium", "region:europe"] }
266
+ record = db.create("order_123", { product: "laptop" }, options)
267
+
268
+ # Add custom labels when updating a record
269
+ options = { labels: ["status:processed", "priority:low"] }
270
+ record = db.update("order_123", { product: "laptop", status: "shipped" }, options)
271
+
272
+ # Add custom labels before deleting (closing) a record
273
+ options = { labels: ["archived", "completed", "Q4-2024"] }
274
+ record = db.delete("order_123", options)
275
+ ```
276
+
277
+ **Important Notes:**
278
+
279
+ - Custom labels are **added in addition** to the library-managed label, not instead of it
280
+ - If you accidentally include the library-managed label in your custom labels array, it will be automatically filtered out to prevent duplicates
281
+ - Custom labels follow GitHub's label naming conventions and restrictions
282
+ - Labels help with organization, filtering, and automation workflows in GitHub
283
+
284
+ ### Label Preservation
285
+
286
+ When performing update or delete operations, the gem preserves existing labels by default:
287
+
288
+ ```ruby
289
+ # Create a record with custom labels
290
+ options = { labels: ["priority:high", "customer:premium"] }
291
+ record = db.create("order_123", { product: "laptop" }, options)
292
+ # Result: Issue has labels ["issue-db", "priority:high", "customer:premium"]
293
+
294
+ # Update the record WITHOUT specifying labels - existing labels are preserved
295
+ record = db.update("order_123", { product: "laptop", status: "shipped" })
296
+ # Result: Issue STILL has labels ["issue-db", "priority:high", "customer:premium"]
297
+
298
+ # Update the record WITH new labels - replaces all labels (except library-managed)
299
+ options = { labels: ["status:processed", "priority:low"] }
300
+ record = db.update("order_123", { product: "laptop", status: "delivered" }, options)
301
+ # Result: Issue now has labels ["issue-db", "status:processed", "priority:low"]
302
+ ```
303
+
304
+ **Key Behavior:**
305
+
306
+ - **Labels specified** = Replace all labels with library-managed label + specified labels
307
+ - **No labels specified** = Preserve existing labels exactly as they are
308
+
309
+ ### Example with Multiple Options
310
+
311
+ You can combine labels with other options:
312
+
313
+ ```ruby
314
+ options = {
315
+ labels: ["priority:high", "customer:vip"],
316
+ body_before: "## Order Details\n\nCustomer: VIP\n\n",
317
+ body_after: "\n\n---\n*This order requires special handling*"
318
+ }
319
+ record = db.create("vip_order_456", { items: ["premium_service"] }, options)
320
+ ```
321
+
322
+ ## Assignees 👥
323
+
324
+ The `issue-db` gem supports GitHub issue assignees for task ownership and responsibility tracking. Here's how assignees work:
325
+
326
+ ### Basic Assignee Usage
327
+
328
+ You can assign GitHub users to issues when creating, updating, or deleting records by using the `assignees` option:
329
+
330
+ ```ruby
331
+ # Assign users when creating a record
332
+ options = { assignees: ["alice", "bob"] }
333
+ record = db.create("task_123", { type: "code_review" }, options)
334
+
335
+ # Assign users when updating a record
336
+ options = { assignees: ["charlie", "diana"] }
337
+ record = db.update("task_123", { type: "code_review", status: "in_progress" }, options)
338
+
339
+ # Assign users before deleting (closing) a record
340
+ options = { assignees: ["alice"] }
341
+ record = db.delete("task_123", options)
342
+ ```
343
+
344
+ ### Assignee Preservation
345
+
346
+ Just like labels, the gem preserves existing assignees by default when no assignees are specified:
347
+
348
+ ```ruby
349
+ # Create a record with assignees
350
+ options = { assignees: ["alice", "bob"] }
351
+ record = db.create("task_123", { type: "code_review" }, options)
352
+ # Result: Issue is assigned to alice and bob
353
+
354
+ # Update the record WITHOUT specifying assignees - existing assignees are preserved
355
+ record = db.update("task_123", { type: "code_review", status: "in_progress" })
356
+ # Result: Issue is STILL assigned to alice and bob
357
+
358
+ # Update the record WITH new assignees - replaces all assignees
359
+ options = { assignees: ["charlie"] }
360
+ record = db.update("task_123", { type: "code_review", status: "completed" }, options)
361
+ # Result: Issue is now only assigned to charlie
362
+ ```
363
+
364
+ **Key Behavior:**
365
+
366
+ - **Assignees specified** = Replace all assignees with the specified assignees
367
+ - **No assignees specified** = Preserve existing assignees exactly as they are
368
+ - **Empty array specified** = Remove all assignees from the issue
369
+
370
+ ### Combining Labels and Assignees
371
+
372
+ You can use both labels and assignees together for comprehensive issue management:
373
+
374
+ ```ruby
375
+ options = {
376
+ labels: ["priority:high", "type:bug", "team:backend"],
377
+ assignees: ["alice", "bob"],
378
+ body_before: "## Bug Report\n\nPriority: High\nTeam: Backend\n\n",
379
+ body_after: "\n\n---\n*Assigned to backend team leads*"
380
+ }
381
+ record = db.create("bug_456", {
382
+ error: "Database timeout",
383
+ severity: "critical"
384
+ }, options)
385
+ ```
386
+
387
+ **Important Notes:**
388
+
389
+ - Assignees must be valid GitHub usernames with access to the repository
390
+ - You can assign up to 10 users to a single issue (GitHub's limit)
391
+ - Invalid or inaccessible usernames will cause the API call to fail
392
+ - Assignees help with responsibility tracking, notifications, and project management workflows
393
+
198
394
  ## Authentication 🔒
199
395
 
200
396
  The `issue-db` gem uses the [`Octokit.rb`](https://github.com/octokit/octokit.rb) library under the hood for interactions with the GitHub API. You have four options for authentication when using the `issue-db` gem:
@@ -31,11 +31,11 @@ module IssueDB
31
31
  # This will return the newly created issue as a Record object (parsed)
32
32
  # :param: key [String] the key (issue title) to create
33
33
  # :param: data [Hash] the data to use for the issue body
34
- # :param: options [Hash] a hash of options containing extra data such as body_before and body_after
34
+ # :param: options [Hash] a hash of options containing extra data such as body_before, body_after, labels, and assignees
35
35
  # :return: The newly created issue as a Record object
36
36
  # usage example:
37
37
  # data = { color: "blue", cool: true, popularity: 100, tags: ["tag1", "tag2"] }
38
- # options = { body_before: "some text before the data", body_after: "some text after the data", include_closed: true }
38
+ # options = { body_before: "some text before the data", body_after: "some text after the data", include_closed: true, labels: ["priority:high", "bug"], assignees: ["username1", "username2"] }
39
39
  # db.create("event123", {cool: true, data: "here"}, options)
40
40
  def create(key, data, options = {})
41
41
  @log.debug("attempting to create: #{key}")
@@ -49,8 +49,24 @@ module IssueDB
49
49
 
50
50
  body = generate(data, body_before: options[:body_before], body_after: options[:body_after])
51
51
 
52
+ # Prepare labels array - always include the library-managed label for create operations
53
+ labels = [@label]
54
+ if options[:labels] && options[:labels].is_a?(Array)
55
+ # Add user-provided labels but ensure the library label is not duplicated
56
+ user_labels = options[:labels].reject { |label| label == @label }
57
+ labels.concat(user_labels)
58
+ end
59
+
60
+ # Prepare API options hash
61
+ api_options = { labels: labels }
62
+
63
+ # Add assignees if provided
64
+ if options[:assignees] && options[:assignees].is_a?(Array)
65
+ api_options[:assignees] = options[:assignees]
66
+ end
67
+
52
68
  # if we make it here, no existing issues were found so we can safely create one
53
- issue = @client.create_issue(@repo.full_name, key, body, { labels: @label })
69
+ issue = @client.create_issue(@repo.full_name, key, body, api_options)
54
70
 
55
71
  # ensure the cache is initialized before appending and handle race conditions
56
72
  current_issues = issues
@@ -78,11 +94,11 @@ module IssueDB
78
94
  # This will return the updated issue as a Record object (parsed)
79
95
  # :param: key [String] the key (issue title) to update
80
96
  # :param: data [Hash] the data to use for the issue body
81
- # :param: options [Hash] a hash of options containing extra data such as body_before and body_after
97
+ # :param: options [Hash] a hash of options containing extra data such as body_before, body_after, labels, and assignees
82
98
  # :return: The updated issue as a Record object
83
99
  # usage example:
84
100
  # data = { color: "blue", cool: true, popularity: 100, tags: ["tag1", "tag2"] }
85
- # options = { body_before: "some text before the data", body_after: "some text after the data", include_closed: true }
101
+ # options = { body_before: "some text before the data", body_after: "some text after the data", include_closed: true, labels: ["priority:high", "bug"], assignees: ["username1", "username2"] }
86
102
  # db.update("event123", {cool: true, data: "here"}, options)
87
103
  def update(key, data, options = {})
88
104
  @log.debug("attempting to update: #{key}")
@@ -90,7 +106,25 @@ module IssueDB
90
106
 
91
107
  body = generate(data, body_before: options[:body_before], body_after: options[:body_after])
92
108
 
93
- updated_issue = @client.update_issue(@repo.full_name, issue.number, key, body)
109
+ # Prepare the API call options
110
+ api_options = {}
111
+
112
+ # Only modify labels if the user explicitly provides them
113
+ if options[:labels] && options[:labels].is_a?(Array)
114
+ # Prepare labels array - always include the library-managed label
115
+ labels = [@label]
116
+ # Add user-provided labels but ensure the library label is not duplicated
117
+ user_labels = options[:labels].reject { |label| label == @label }
118
+ labels.concat(user_labels)
119
+ api_options[:labels] = labels
120
+ end
121
+
122
+ # Only modify assignees if the user explicitly provides them
123
+ if options[:assignees] && options[:assignees].is_a?(Array)
124
+ api_options[:assignees] = options[:assignees]
125
+ end
126
+
127
+ updated_issue = @client.update_issue(@repo.full_name, issue.number, key, body, api_options)
94
128
 
95
129
  # update the issue in the cache using the reference we have
96
130
  index = @issues.index(issue)
@@ -108,15 +142,34 @@ module IssueDB
108
142
 
109
143
  # Delete an issue/record from the database - in this context, "delete" means to close the issue as "completed"
110
144
  # :param: key [String] the key (issue title) to delete
111
- # :param: options [Hash] a hash of options to pass through to the search method
145
+ # :param: options [Hash] a hash of options to pass through to the search method and control labels and assignees
112
146
  # :return: The deleted issue as a Record object (parsed) - it may contain useful data
113
147
  def delete(key, options = {})
114
148
  @log.debug("attempting to delete: #{key}")
115
149
  issue = find_issue_by_key(key, options)
116
150
 
117
- deleted_issue = @client.close_issue(@repo.full_name, issue.number)
151
+ # For delete operations, only update labels and assignees if the user explicitly provides them
152
+ if (options[:labels] && options[:labels].is_a?(Array)) || (options[:assignees] && options[:assignees].is_a?(Array))
153
+ update_options = {}
154
+
155
+ if options[:labels] && options[:labels].is_a?(Array)
156
+ # Prepare labels array - always include the library-managed label
157
+ labels = [@label]
158
+ # Add user-provided labels but ensure the library label is not duplicated
159
+ user_labels = options[:labels].reject { |label| label == @label }
160
+ labels.concat(user_labels)
161
+ update_options[:labels] = labels
162
+ end
163
+
164
+ if options[:assignees] && options[:assignees].is_a?(Array)
165
+ update_options[:assignees] = options[:assignees]
166
+ end
167
+
168
+ # Update the issue with new labels and/or assignees before closing
169
+ @client.update_issue(@repo.full_name, issue.number, update_options)
170
+ end
118
171
 
119
- # update the issue in the cache using the reference we have
172
+ deleted_issue = @client.close_issue(@repo.full_name, issue.number) # update the issue in the cache using the reference we have
120
173
  index = @issues.index(issue)
121
174
  if index
122
175
  @issues[index] = deleted_issue
@@ -126,6 +179,7 @@ module IssueDB
126
179
  update_issue_cache!
127
180
  end
128
181
 
182
+ @log.debug("issue deleted: #{key}")
129
183
  # return the deleted issue as a Record object as it may contain useful data
130
184
  return Record.new(deleted_issue)
131
185
  end
data/lib/version.rb CHANGED
@@ -2,6 +2,6 @@
2
2
 
3
3
  module IssueDB
4
4
  module Version
5
- VERSION = "1.2.0"
5
+ VERSION = "1.3.0"
6
6
  end
7
7
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: issue-db
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.3.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - runwaylab