Today it can be confusing to understand why an issue is listed as closed. In the case where it has been moved we rightly add a note in the issue state and a link to where the issue was moved. We should do the same when an issue is closed because it was promoted to an Epic.
An example of the confusion this can cause is here: #2519 (comment 263836383). Further up is the system note that it was promoted, but without further action by the user doing the promoting (like editing the description manually) it can be quite easy to miss.
Thanks @kencjohnston and @joshlambert! This seems fairly trivial with a high value add. In addition to this, I've also been toying with the idea of following the lead on MRs and having a sticky header at the top of an issue, but taking it one step further and making the issue title / status and breadcrumbs sticky as well.
This would alleviate the problem of clicking into a deep link within an issue only to realize at some point it is no longer open. Do you think this would be valuable?
@joshlambert looks like it. Crazy how time flies when you are having fun (original issue - #9898 (closed)). Unfortunately it appears as though it only applies to issues promoted after this change was implemented which is kind of a bummer (!18839 (merged)).
It seems like there ought to be a way to add this to older issues given we're already linking to the newly created epic via a system note Do you think it's worth exploring/investigating?
Should be straightforward enough. Using the REST API to view notes on the given issue you can see the system note generated during promotion. We can identify promoted issues via system notes matching the text promoted to epic.
Maybe we should fix this using a migration and keep the database consistent? I used @johnhope's suggestion to identify promoted issues via system note.
Current we have 467 issues promoted to epic with promoted_to_epic_id = null. I used the query below to fetch data in production.
Collapsed query.
SELECT COUNT(*) FROM notes INNER JOIN issues on issues.id = notes.noteable_id WHERE notes.noteable_type = 'Issue' AND notes.system = true AND notes.note like 'promoted to epic%' AND issues.promoted_to_epic_id IS NULL count ------- 467(1 row)
We could use the following query to fetch issue_id, epic_iid and namespace_id and iterate over that in a background migration. It took ~9 seconds to run in production:
Collapsed query.
SELECT noteable_id, (REGEXP_MATCHES(notes.note, '&(\d+)'))[1] as epic_iid, projects.namespace_id FROM notes INNER JOIN projects ON notes.project_id = projects.id WHERE notes.noteable_type = 'Issue' AND notes.system = true AND notes.note like 'promoted to epic%'--------------------------------------------------------------------------------------------------------------------------------------------------------- Nested Loop (cost=811.28..44689.89 rows=5007 width=40) (actual time=7112.299..9230.455 rows=596 loops=1) -> Bitmap Heap Scan on notes (cost=810.84..27703.64 rows=5007 width=115) (actual time=7112.247..9216.622 rows=596 loops=1) Recheck Cond: (note ~~ 'promoted to epic%'::text) Rows Removed by Index Recheck: 2005 Filter: (system AND ((noteable_type)::text = 'Issue'::text)) Rows Removed by Filter: 69 Heap Blocks: exact=2574 -> Bitmap Index Scan on index_notes_on_note_trigram (cost=0.00..809.59 rows=16946 width=0) (actual time=7105.008..7105.008 rows=2676 loops=1) Index Cond: (note ~~ 'promoted to epic%'::text) -> Index Scan using projects_pkey on projects (cost=0.43..3.38 rows=1 width=8) (actual time=0.013..0.014 rows=1 loops=596) Index Cond: (id = notes.project_id) Planning time: 2.521 ms Execution time: 9230.598 ms
WITHpromotion_notesAS(SELECTnoteable_id,noteaspromotion_note,projects.namespace_idasepic_group_idFROMnotesINNERJOINprojectsONnotes.project_id=projects.idINNERJOINissuesONnotes.noteable_id=issues.idWHEREnotes.noteable_type='Issue'ANDnotes.systemISTRUEANDnotes.notelike'promoted to epic%'ANDissues.promoted_to_epic_idISNULL),promoted_epicsAS(SELECTepics.idaspromoted_epic_id,promotion_notes.noteable_idasissue_idFROMepicsINNERJOINpromotion_notesonepics.group_id=promotion_notes.epic_group_idWHEREconcat('promoted to epic &',epics.iid)=promotion_notes.promotion_note)SELECTCOUNT(*)frompromoted_epicscount-------0(1row)
There are no promoted issues with null promoted_to_epic.