Edit: the exclusion rule below is wrong, as Michael pointed out: only the bugfix releases of the previous branch must be excluded. See the corrected rule and code.
The excludes can be computed, so the RM never has to think about them. The rule is: exclude every version released before the cycle’s first release (RC1, or the final when there’s no RC). An issue carrying such a version already shipped in a bugfix release. Version names and release dates are all it needs, and the Release Notes app already has both (each ReleaseNotes.Code.ReleaseNoteClass object holds version, date and released). So the RN template can generate the link from the version alone, as Thomas suggested, with no call to JIRA. Excluding every version released in the 6 months before RC1 is enough, and excluding a version that no issue carries is harmless.
I checked it against 17.5.0, 18.6.0, 18.7.0 and 18.8.0: it returns exactly the issues not already shipped (65, 87, 82 and 87). For example, for 18.8.0: full list of issues fixed in XWiki 18.8.0.
It also shows the manual approach isn’t working. The 18.7.0 and 18.8.0 dashboards have no excludes and count 24 and 27 issues that had already shipped in 18.4.x/17.10.x, about 25% too many. The 17.5.0 filter excluded 17.4.1 but missed 16.10.9 and 17.4.0.
As Simon said, bugfix releases don’t need this: their RN already lists fixVersion = X.Y.Z.
Here’s a proposal for the RN template, replacing the dashboard line. (Superseded by the corrected code.) It reuses the variables the template already sets and needs script rights only. It should be reviewed before being used: it was generated with Claude and hasn’t been tested on a wiki yet (only its logic was checked, against the live RN data and JIRA):
Velocity code (superseded, implements the wrong rule)
{{velocity}}
#set ($rnClass = 'ReleaseNotes.Code.ReleaseNoteClass')
#if ($isBugFix)
#set ($cycle = [$version])
#set ($start = $rnObject.getValue('date'))
#else
## The cycle: this version's RCs and its final (18.8.0-rc-1, 18.8.0), whatever exists of them.
#set ($cycleQuery = $services.query.xwql("select obj.version, obj.date from Document doc, doc.object($rnClass) obj where obj.product = :product and (obj.version = :final or obj.version like :pre)"))
#set ($cycleQuery = $cycleQuery.bindValue('product', $product).bindValue('final', $versionPrefix))
#set ($cycleQuery = $cycleQuery.bindValue('pre').literal("${versionPrefix}-").anyChars().query())
#set ($cycle = [])
#set ($start = $NULL)
#foreach ($row in $cycleQuery.execute())
#set ($discard = $cycle.add($row[0]))
#if ($row[1] && (!$start || $row[1].before($start)))
#set ($start = $row[1])
#end
#end
#end
#if (!$start)
#set ($start = $datetool.date)
#end
## 6 months before the cycle's first release
#set ($from = $datetool.toDate($mathtool.sub($start.time, 15811200000)))
#set ($excluded = $services.query.xwql("select obj.version from Document doc, doc.object($rnClass) obj where obj.product = :product and obj.released = 1 and obj.date < :start and obj.date >= :from order by obj.version").bindValue('product', $product).bindValue('start', $start).bindValue('from', $from).execute())
#set ($jql = "component != ""Development Issues only"" AND project in (XCOMMONS, XRENDERING, XWIKI) AND fixVersion in ($stringtool.join($cycle, ', '))")
#if (!$isBugFix && !$excluded.isEmpty())
#set ($jql = "$jql AND fixVersion not in ($stringtool.join($excluded, ', '))")
#end
[[Full list of issues fixed in $product $versionPrefix>>https://jira.xwiki.org/issues/?jql=$escapetool.url($jql)]].
{{/velocity}}
I’d suggest packaging this as a wiki macro (e.g. {{jiraFixedIssuesFor version="$version"/}}), like {{jiraBlockersFor}}, so the template keeps a single line and the rule lives in one place. It’s also a good candidate for the async macro, with caching. The result changes only when a release note’s date or released flag changes, and it’s frozen once the cycle is released. Caching it keyed on the version means the queries run once, not on every view of every release note page.
WDYT?
Thx