Skip to content

Commit 3091c54

Browse files
authored
Implement the sprint-sync script (#4803)
Implement an easy way to sync github sprint items into the Jira sprint. Removed items are dropped, new items are added. Search for the right Jira issue is done using `remoteLinkUrl` but since it does not work always (e.g. when the index it outdated) fallback using the summary is included. Duplicate issues, when detected during the search, are reported as well. Fix #4671. Assisted-by: Claude
1 parent e6c605c commit 3091c54

4 files changed

Lines changed: 390 additions & 25 deletions

File tree

docs/contribute.rst

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -855,6 +855,38 @@ prioritized issues.
855855
.. _backlog: https://github.com/orgs/teemtee/projects/1/views/1
856856

857857

858+
Sync
859+
------------------------------------------------------------------
860+
861+
The ``scripts/sprint-overview`` script lists all issues and pull
862+
requests in a GitHub Project sprint together with their story
863+
points. It can also output the data in YAML format for further
864+
processing:
865+
866+
.. code-block:: bash
867+
868+
./scripts/sprint-overview --sprint 'Sprint 11'
869+
./scripts/sprint-overview --sprint 'Sprint 11' --yaml
870+
871+
The ``scripts/sprint-sync`` script synchronizes a Jira sprint
872+
with GitHub sprint items. It reads the YAML output from
873+
``sprint-overview``, removes Jira items not present in the
874+
GitHub sprint and adds missing items into the Jira sprint:
875+
876+
.. code-block:: bash
877+
878+
./scripts/sprint-overview --sprint 'Sprint 11' --yaml \
879+
| ./scripts/sprint-sync --sprint 'Sprint 11'
880+
881+
Use ``--dry`` to preview changes without modifying the Jira
882+
sprint:
883+
884+
.. code-block:: bash
885+
886+
./scripts/sprint-overview --sprint 'Sprint 11' --yaml \
887+
| ./scripts/sprint-sync --sprint 'Sprint 11' --dry
888+
889+
858890
Release
859891
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
860892

scripts/common.py

Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
"""
2+
Common data structures shared between sprint scripts.
3+
"""
4+
5+
import dataclasses
6+
from typing import Optional
7+
8+
9+
@dataclasses.dataclass
10+
class Item:
11+
"""
12+
A sprint item (issue or pull request).
13+
"""
14+
15+
id: int
16+
type: str
17+
repo: str
18+
status: str
19+
size: Optional[int]
20+
url: str
21+
title: str
22+
23+
def __str__(self) -> str:
24+
size_str = f"[{self.size}]" if self.size is not None else "[-]"
25+
identifier = f"{self.repo}#{self.id}"
26+
return f"{identifier:<10} {size_str:>4} {self.title}"

scripts/sprint-overview

Lines changed: 7 additions & 25 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,8 @@ import jinja2
2626
import requests
2727
from ruamel.yaml import YAML
2828

29+
from common import Item # isort: skip
30+
2931
# https://docs.github.com/en/rest/about-the-rest-api/breaking-changes
3032
GITHUB_API_URL = "https://api.github.com"
3133
GITHUB_API_VERSION = "2026-03-10"
@@ -75,26 +77,6 @@ DISPLAY_TEMPLATE = jinja2.Template(
7577
)
7678

7779

78-
@dataclasses.dataclass
79-
class Item:
80-
"""
81-
A sprint item (issue or pull request).
82-
"""
83-
84-
id: int
85-
type: str
86-
repo: str
87-
status: str
88-
size: Optional[int]
89-
url: str
90-
title: str
91-
92-
def __str__(self) -> str:
93-
size_str = f"[{self.size}]" if self.size is not None else "[-]"
94-
identifier = f"{self.repo}#{self.id}"
95-
return f"{identifier:<10} {size_str:>4} {self.title}"
96-
97-
9880
def github_api_get(
9981
path: str,
10082
params: Optional[dict[str, str]] = None,
@@ -253,14 +235,14 @@ def main(sprint: str, output_yaml: bool) -> None:
253235
"""
254236
items = fetch_sprint_items(sprint)
255237

256-
if not items:
257-
click.echo(f"No items found in sprint '{sprint}'.")
258-
return
259-
260238
if output_yaml:
261239
YAML().dump([dataclasses.asdict(item) for item in items], sys.stdout)
262-
else:
240+
return
241+
242+
if items:
263243
display_items(sprint, items)
244+
else:
245+
click.echo(f"No items found in sprint '{sprint}'.", err=True)
264246

265247

266248
if __name__ == "__main__":

0 commit comments

Comments
 (0)