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 +4 -4
- data/README.md +196 -0
- data/lib/issue_db/database.rb +63 -9
- data/lib/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8c165f569fbb2648a4c73a0bf4007f67e6da2ff2dae28132af424d9eb9844575
|
4
|
+
data.tar.gz: 224bc98fcc029e417e620e4d8efaa79030fdf1a767558d126ab22145fec66ac7
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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:
|
data/lib/issue_db/database.rb
CHANGED
@@ -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
|
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,
|
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
|
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
|
-
|
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
|
-
|
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