Skip to content

Commit 298fd79

Browse files
Add capability to override default tags on a per-issue basis (#24)
* add: tag removal functionality with - prefix at issue level - Enable removal of default/appended tags using - prefix in issue tags - Process removal tags only at issue level, not in defaults - Remove tags from final tag list after applying default logic - Add comprehensive example file demonstrating removal functionality Fixes #4 * test: add comprehensive tests for tag removal functionality - Test removal of single and multiple tags from defaults - Test removal of tags from appended (+) tags - Test removal of non-existing tags (no-op behavior) - Test combined removal with regular and appended tags - Verify removal only works at issue level, not in defaults All tests pass and cover edge cases for - prefix tag removal feature. * doc: add tag removal example to README - Add example showing - prefix for removing default/appended tags - Demonstrate removal functionality in main IMYML format example - Show how to remove tags like -bug and -enhancement at issue level Completes documentation for tag removal feature from issue #4 Resolves issue #4
1 parent cf5a95f commit 298fd79

4 files changed

Lines changed: 151 additions & 16 deletions

File tree

README.adoc

Lines changed: 12 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -144,7 +144,7 @@ $meta: # optional block for establishing general modes/settings
144144
vrsn: 0.1.0 # milestone/version
145145
user: alice # assigned user
146146
type: Bug # type of issue (must already be registered)
147-
tags: [needs_labels,+posted_by_issuer] # labels
147+
tags: [needs:labels, +posted_by_issuer, +needs:docs] # labels
148148
stub: true # whether to auto-insert stub texts
149149
head: | # header stub text to prepend when indicated
150150
Below the next line is the body...
@@ -169,7 +169,9 @@ issues: # block for listing issues to post to cloud
169169
# ---
170170
# This is the default text that will appear if an issue record is a _stub_ and no `body` field is designated.
171171
# ---
172-
# This issue was automatically generated by issuer.
172+
# This issue was automatically generated by issuer.
173+
- summ: Documentaton issue
174+
tags: [-needs:docs] # skip the default needs:docs label
173175
----
174176

175177
The `$meta` block is entirely optional, but if it is absent, your `issuer` command will need a `--proj` flag to designate the GitHub repo to which your issues should post.
@@ -277,6 +279,14 @@ Defaults to `$meta.defaults.type` or else `null`.
277279
`tags`:::
278280
(Array of Strings)
279281
A listing of specific labels to assign to the issue.
282+
+
283+
Supports special prefix notation for label management:
284+
+
285+
* Regular labels (e.g., `bug`, `priority:high`) are applied based on default tag logic
286+
* Append labels (e.g., `+urgent`) are always applied to all issues
287+
* Removal labels (e.g., `-needs:docs`) remove the specified label from the default/appended labels list
288+
+
289+
Example: `tags: [documentation, +critical, -needs:review]` would add `documentation` and `critical` labels while removing any `needs:review` label from defaults.
280290

281291
`user`:::
282292
(String)

examples/tag-removal-example.yml

Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
# Tag Removal Example - Issue #4 Implementation
2+
#
3+
# This example demonstrates the new tag removal functionality where
4+
# tags prefixed with '-' are removed from the default/appended tags list.
5+
6+
$meta:
7+
proj: myorg/myrepo
8+
defaults:
9+
tags: [+posted-by-issuer, needs:label, +needs:docs]
10+
11+
issues:
12+
- summ: Fix misstatement in the README
13+
body: |
14+
Correct that really bad typo on the first paragraph.
15+
No docs needed for this change since it's a docs change.
16+
tags: [documentation, -needs:docs]
17+
18+
- summ: Issue that will be fixed immediately
19+
tags:
20+
- '-needs:label'
21+
22+
- summ: Complex issue with multiple tag operations
23+
body: |
24+
This issue demonstrates multiple tag operations:
25+
- Adds bug and enhancement tags
26+
- Removes the default needs:label tag
27+
- Removes the appended needs:docs tag
28+
- Keeps the posted-by-issuer tag (append)
29+
tags: [bug, enhancement, -needs:label, -needs:docs]
30+
31+
- summ: Issue with no custom tags
32+
body: |
33+
This issue has no custom tags, so it gets all default tags
34+
including both regular defaults and append tags.
35+
# No tags specified - gets all defaults
36+
37+
- summ: Issue that only removes tags
38+
body: |
39+
This issue only removes tags, so it gets default tags
40+
minus the removed ones.
41+
tags: [-needs:docs]

lib/issuer/issue.rb

Lines changed: 26 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ module Issuer
2323
# Tags support special prefix notation:
2424
# * Regular tags (e.g., +"bug"+) are only applied if the issue has no existing tags
2525
# * Append tags (e.g., +"+urgent"+) are always applied to all issues
26+
# * Removal tags (e.g., +"-needs:docs"+) are removed from the default/appended tags list
2627
#
2728
# == Stub Composition
2829
#
@@ -169,28 +170,32 @@ def self.apply_stub_logic issues, defaults
169170

170171
# Apply tag logic for this issue
171172
#
172-
# Processes existing tags with + prefix as append tags, combines them with
173-
# CLI-provided tags, and determines final tag set based on precedence rules.
173+
# Processes existing tags with + prefix as append tags, - prefix as removal tags,
174+
# combines them with CLI-provided tags, and determines final tag set based on precedence rules.
174175
#
175176
# @param cli_append_tags [Array<String>] Tags to always append from CLI
176177
# @param cli_default_tags [Array<String>] Default tags from CLI (used when no regular tags exist)
177178
# @return [void] Sets @tags instance variable
178179
#
179180
# @example
180-
# # Issue has tags: ['+urgent', 'bug']
181-
# issue.apply_tag_logic(['cli-tag'], ['default-tag'])
182-
# # Result: ['urgent', 'cli-tag', 'bug']
181+
# # Issue has tags: ['+urgent', 'bug', '-needs:docs']
182+
# issue.apply_tag_logic(['cli-tag'], ['default-tag', 'needs:docs'])
183+
# # Result: ['urgent', 'cli-tag', 'bug', 'default-tag'] (needs:docs removed)
183184
def apply_tag_logic cli_append_tags, cli_default_tags
184-
# Parse existing tags for + prefix
185+
# Parse existing tags for + and - prefixes
185186
existing_tags = tags || []
186187
append_tags = []
187188
regular_tags = []
189+
remove_tags = []
188190

189191
existing_tags.each do |tag|
190-
if tag.to_s.start_with?('+')
191-
append_tags << tag.to_s[1..] # Remove + prefix
192+
tag_str = tag.to_s
193+
if tag_str.start_with?('+')
194+
append_tags << tag_str[1..] # Remove + prefix
195+
elsif tag_str.start_with?('-')
196+
remove_tags << tag_str[1..] # Remove - prefix
192197
else
193-
regular_tags << tag.to_s
198+
regular_tags << tag_str
194199
end
195200
end
196201

@@ -199,21 +204,27 @@ def apply_tag_logic cli_append_tags, cli_default_tags
199204
final_tags = append_tags + defaults_append_tags + cli_append_tags
200205

201206
# For regular tags, add issue's own tags, otherwise use default tags
202-
issue_regular_tags = Array(@raw_data['tags']).reject { |tag| tag.to_s.start_with?('+') }
207+
issue_regular_tags = Array(@raw_data['tags']).reject { |tag| tag.to_s.start_with?('+') || tag.to_s.start_with?('-') }
203208

204209
if !issue_regular_tags.empty?
205210
# Issue has its own regular tags, use them
206211
final_tags.concat(issue_regular_tags)
207212
else
208213
# Issue has no regular tags, use defaults from CLI
209214
final_tags.concat(cli_default_tags)
210-
# Also add non-append defaults tags
215+
# Also add non-append defaults tags (- prefix ignored in defaults)
211216
defaults_regular_tags = Array(@defaults['tags']).reject { |tag| tag.to_s.start_with?('+') }
212217
final_tags.concat(defaults_regular_tags)
213218
end
214219

215-
# Set the final tags (removing duplicates)
216-
@tags = final_tags.uniq
220+
# Collect removal tags from issue only (not defaults)
221+
all_remove_tags = remove_tags
222+
223+
# Remove duplicates first, then remove tags specified for removal
224+
final_tags = final_tags.uniq - all_remove_tags
225+
226+
# Set the final tags
227+
@tags = final_tags
217228
end
218229

219230
# Apply stub logic for this issue
@@ -259,7 +270,8 @@ def apply_stub_logic defaults
259270
#
260271
# Separates tags with + prefix (append tags) from regular tags (default tags).
261272
# Tags with + prefix are always applied, while regular tags are only used
262-
# when the issue has no existing regular tags.
273+
# when the issue has no existing regular tags. Tags with - prefix are handled
274+
# in the apply_tag_logic method for removal.
263275
#
264276
# @param tags_string [String] Comma-separated tag string
265277
# @return [Array<Array<String>>] Two-element array: [append_tags, default_tags]

specs/tests/rspec/issue_spec.rb

Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -349,6 +349,78 @@
349349
expect(issue.tags).to contain_exactly('posted-by-issuer', 'cli-default', 'needs-label')
350350
end
351351
end
352+
353+
context 'with removal tags (-prefix)' do
354+
it 'removes tags specified with - prefix from issue tags' do
355+
issue_data = { 'summ' => 'Test', 'tags' => ['bug', '-needs-label'] }
356+
issue = described_class.new(issue_data, defaults)
357+
358+
issue.apply_tag_logic([], [])
359+
360+
expect(issue.tags).to contain_exactly('posted-by-issuer', 'bug')
361+
expect(issue.tags).not_to include('needs-label')
362+
end
363+
364+
it 'removes append tags specified with - prefix' do
365+
issue_data = { 'summ' => 'Test', 'tags' => ['bug', '-posted-by-issuer'] }
366+
issue = described_class.new(issue_data, defaults)
367+
368+
issue.apply_tag_logic([], [])
369+
370+
expect(issue.tags).to contain_exactly('bug')
371+
expect(issue.tags).not_to include('posted-by-issuer')
372+
end
373+
374+
it 'handles multiple removal tags' do
375+
issue_data = { 'summ' => 'Test', 'tags' => ['bug', 'enhancement', '-needs-label', '-posted-by-issuer'] }
376+
issue = described_class.new(issue_data, defaults)
377+
378+
issue.apply_tag_logic([], [])
379+
380+
expect(issue.tags).to contain_exactly('bug', 'enhancement')
381+
expect(issue.tags).not_to include('needs-label', 'posted-by-issuer')
382+
end
383+
384+
it 'handles removal tags when no matching tags exist' do
385+
issue_data = { 'summ' => 'Test', 'tags' => ['bug', '-nonexistent-tag'] }
386+
issue = described_class.new(issue_data, defaults)
387+
388+
issue.apply_tag_logic([], [])
389+
390+
expect(issue.tags).to contain_exactly('posted-by-issuer', 'bug')
391+
end
392+
393+
it 'processes removal tags with CLI tags' do
394+
issue_data = { 'summ' => 'Test', 'tags' => ['bug', '-needs-label'] }
395+
issue = described_class.new(issue_data, defaults)
396+
397+
issue.apply_tag_logic(['cli-urgent'], ['cli-default'])
398+
399+
expect(issue.tags).to contain_exactly('posted-by-issuer', 'cli-urgent', 'bug')
400+
expect(issue.tags).not_to include('needs-label')
401+
end
402+
403+
it 'handles colon-separated tags in removal (needs:docs example)' do
404+
defaults_with_colon = { 'tags' => ['+posted-by-issuer', 'needs:docs', 'needs:label'] }
405+
issue_data = { 'summ' => 'Test', 'tags' => ['documentation', '-needs:docs'] }
406+
issue = described_class.new(issue_data, defaults_with_colon)
407+
408+
issue.apply_tag_logic([], [])
409+
410+
expect(issue.tags).to contain_exactly('posted-by-issuer', 'documentation')
411+
expect(issue.tags).not_to include('needs:docs')
412+
end
413+
414+
it 'handles string format removal tags' do
415+
issue_data = { 'summ' => 'Test', 'tags' => ['-needs-label'] }
416+
issue = described_class.new(issue_data, defaults)
417+
418+
issue.apply_tag_logic([], [])
419+
420+
expect(issue.tags).to contain_exactly('posted-by-issuer')
421+
expect(issue.tags).not_to include('needs-label')
422+
end
423+
end
352424
end
353425

354426
describe '.parse_tag_logic' do

0 commit comments

Comments
 (0)