Skip to content

Commit 732c8c1

Browse files
committed
wip
1 parent 6bed519 commit 732c8c1

File tree

3 files changed

+340
-12
lines changed

3 files changed

+340
-12
lines changed

.release_tool.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -249,19 +249,19 @@ release_notes_inclusion_policy = ["tickets", "pull-requests"]
249249
# Uses Python regex with capturing group (group 1 is extracted)
250250
# Looks for sections like "## Description" or "## Summary" in ticket text
251251
# The tool gracefully handles tickets without description sections
252-
# Default: r'(?:## Description|## Summary)\\n(.*?)(?=\\n##|\\Z)'
252+
# Default: r'(?:## Description|## Summary)\n(.*?)(?=\n##|\Z)'
253253
# Set to empty string "" to disable description extraction
254-
# NOTE: In TOML, backslashes must be doubled: \\n becomes \\\\n, \\Z becomes \\\\Z
255-
description_section_regex = "(?:## Description|## Summary)\\\\n(.*?)(?=\\\\n##|\\\\Z)"
254+
# NOTE: In TOML, backslashes must be doubled: \n becomes \\n, \Z becomes \\Z
255+
description_section_regex = "(?:## Description|## Summary)\\n(.*?)(?=\\n##|\\Z)"
256256

257257
# migration_section_regex: Regex to extract migration notes from ticket body
258258
# Useful for database migrations, breaking changes, upgrade steps
259259
# Looks for sections like "## Migration" or "## Migration Notes"
260260
# The tool gracefully handles tickets without migration sections
261-
# Default: r'(?:## Migration|## Migration Notes)\\n(.*?)(?=\\n##|\\Z)'
261+
# Default: r'(?:## Migration|## Migration Notes)\n(.*?)(?=\n##|\Z)'
262262
# Set to empty string "" to disable migration notes extraction
263-
# NOTE: In TOML, backslashes must be doubled: \\n becomes \\\\n, \\Z becomes \\\\Z
264-
migration_section_regex = "(?:## Migration|## Migration Notes)\\\\n(.*?)(?=\\\\n##|\\\\Z)"
263+
# NOTE: In TOML, backslashes must be doubled: \n becomes \\n, \Z becomes \\Z
264+
migration_section_regex = "(?:## Migration|## Migration Notes)\\n(.*?)(?=\\n##|\\Z)"
265265

266266
# =============================================================================
267267
# Version Comparison and Gap Detection Policy

src/release_tool/config_template.toml

Lines changed: 6 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -275,19 +275,19 @@ release_notes_inclusion_policy = ["tickets", "pull-requests"]
275275
# Uses Python regex with capturing group (group 1 is extracted)
276276
# Looks for sections like "## Description" or "## Summary" in ticket text
277277
# The tool gracefully handles tickets without description sections
278-
# Default: r'(?:## Description|## Summary)\\n(.*?)(?=\\n##|\\Z)'
278+
# Default: r'(?:## Description|## Summary)\n(.*?)(?=\n##|\Z)'
279279
# Set to empty string "" to disable description extraction
280-
# NOTE: In TOML, backslashes must be doubled: \\n becomes \\\\n, \\Z becomes \\\\Z
281-
description_section_regex = "(?:## Description|## Summary)\\\\n(.*?)(?=\\\\n##|\\\\Z)"
280+
# NOTE: In TOML, backslashes must be doubled: \n becomes \\n, \Z becomes \\Z
281+
description_section_regex = "(?:## Description|## Summary)\\n(.*?)(?=\\n##|\\Z)"
282282

283283
# migration_section_regex: Regex to extract migration notes from ticket body
284284
# Useful for database migrations, breaking changes, upgrade steps
285285
# Looks for sections like "## Migration" or "## Migration Notes"
286286
# The tool gracefully handles tickets without migration sections
287-
# Default: r'(?:## Migration|## Migration Notes)\\n(.*?)(?=\\n##|\\Z)'
287+
# Default: r'(?:## Migration|## Migration Notes)\n(.*?)(?=\n##|\Z)'
288288
# Set to empty string "" to disable migration notes extraction
289-
# NOTE: In TOML, backslashes must be doubled: \\n becomes \\\\n, \\Z becomes \\\\Z
290-
migration_section_regex = "(?:## Migration|## Migration Notes)\\\\n(.*?)(?=\\\\n##|\\\\Z)"
289+
# NOTE: In TOML, backslashes must be doubled: \n becomes \\n, \Z becomes \\Z
290+
migration_section_regex = "(?:## Migration|## Migration Notes)\\n(.*?)(?=\\n##|\\Z)"
291291

292292
# =============================================================================
293293
# Version Comparison and Gap Detection Policy

tests/test_policies.py

Lines changed: 328 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -800,3 +800,331 @@ def test_ticket_key_without_hash_prefix(self, test_config):
800800
assert note.url == note.ticket_url
801801
assert note.short_link == "#8624"
802802
assert note.short_repo_link == "sequentech/meta#8624"
803+
804+
805+
class TestDescriptionAndMigrationExtraction:
806+
"""Tests for extracting description and migration notes from ticket bodies."""
807+
808+
def test_extract_description_section(self, test_config):
809+
"""Test extraction of Description section from ticket body."""
810+
ticket_body = """## Description
811+
This is the description text.
812+
It can span multiple lines.
813+
814+
## Other Section
815+
Something else here."""
816+
817+
ticket = Ticket(
818+
repo_id=1,
819+
number=123,
820+
key="123",
821+
title="Test ticket",
822+
state="closed",
823+
body=ticket_body,
824+
url="https://github.com/test/repo/issues/123"
825+
)
826+
827+
change = ConsolidatedChange(
828+
type="ticket",
829+
ticket_key="123",
830+
prs=[],
831+
commits=[]
832+
)
833+
834+
generator = ReleaseNoteGenerator(test_config)
835+
note = generator.create_release_note(change, ticket)
836+
837+
assert note.description is not None
838+
assert "This is the description text" in note.description
839+
assert "It can span multiple lines" in note.description
840+
assert "Other Section" not in note.description
841+
842+
def test_extract_summary_section(self, test_config):
843+
"""Test extraction of Summary section (alternative to Description)."""
844+
ticket_body = """## Summary
845+
This is a summary of the changes.
846+
847+
## Details
848+
More information here."""
849+
850+
ticket = Ticket(
851+
repo_id=1,
852+
number=124,
853+
key="124",
854+
title="Test ticket",
855+
state="closed",
856+
body=ticket_body,
857+
url="https://github.com/test/repo/issues/124"
858+
)
859+
860+
change = ConsolidatedChange(
861+
type="ticket",
862+
ticket_key="124",
863+
prs=[],
864+
commits=[]
865+
)
866+
867+
generator = ReleaseNoteGenerator(test_config)
868+
note = generator.create_release_note(change, ticket)
869+
870+
assert note.description is not None
871+
assert "This is a summary of the changes" in note.description
872+
assert "Details" not in note.description
873+
874+
def test_extract_migration_notes_section(self, test_config):
875+
"""Test extraction of Migration section from ticket body."""
876+
ticket_body = """## Description
877+
Some description.
878+
879+
## Migration
880+
Run the following command:
881+
```
882+
python manage.py migrate
883+
```
884+
885+
## Other Section
886+
Something else."""
887+
888+
ticket = Ticket(
889+
repo_id=1,
890+
number=125,
891+
key="125",
892+
title="Test ticket",
893+
state="closed",
894+
body=ticket_body,
895+
url="https://github.com/test/repo/issues/125"
896+
)
897+
898+
change = ConsolidatedChange(
899+
type="ticket",
900+
ticket_key="125",
901+
prs=[],
902+
commits=[]
903+
)
904+
905+
generator = ReleaseNoteGenerator(test_config)
906+
note = generator.create_release_note(change, ticket)
907+
908+
assert note.migration_notes is not None
909+
assert "Run the following command" in note.migration_notes
910+
assert "python manage.py migrate" in note.migration_notes
911+
assert "Other Section" not in note.migration_notes
912+
913+
def test_extract_migration_notes_alternative_heading(self, test_config):
914+
"""Test extraction of Migration Notes section (alternative heading)."""
915+
ticket_body = """## Description
916+
Some description.
917+
918+
## Migration Notes
919+
Database schema changes required:
920+
- Add new column
921+
- Update indexes
922+
923+
## Testing
924+
Test steps."""
925+
926+
ticket = Ticket(
927+
repo_id=1,
928+
number=126,
929+
key="126",
930+
title="Test ticket",
931+
state="closed",
932+
body=ticket_body,
933+
url="https://github.com/test/repo/issues/126"
934+
)
935+
936+
change = ConsolidatedChange(
937+
type="ticket",
938+
ticket_key="126",
939+
prs=[],
940+
commits=[]
941+
)
942+
943+
generator = ReleaseNoteGenerator(test_config)
944+
note = generator.create_release_note(change, ticket)
945+
946+
assert note.migration_notes is not None
947+
assert "Database schema changes required" in note.migration_notes
948+
assert "Add new column" in note.migration_notes
949+
assert "Testing" not in note.migration_notes
950+
951+
def test_no_description_section(self, test_config):
952+
"""Test ticket without Description section returns None."""
953+
ticket_body = """## Other Section
954+
Some content.
955+
956+
## Another Section
957+
More content."""
958+
959+
ticket = Ticket(
960+
repo_id=1,
961+
number=127,
962+
key="127",
963+
title="Test ticket",
964+
state="closed",
965+
body=ticket_body,
966+
url="https://github.com/test/repo/issues/127"
967+
)
968+
969+
change = ConsolidatedChange(
970+
type="ticket",
971+
ticket_key="127",
972+
prs=[],
973+
commits=[]
974+
)
975+
976+
generator = ReleaseNoteGenerator(test_config)
977+
note = generator.create_release_note(change, ticket)
978+
979+
assert note.description is None
980+
981+
def test_no_migration_section(self, test_config):
982+
"""Test ticket without Migration section returns None."""
983+
ticket_body = """## Description
984+
Some description.
985+
986+
## Other Section
987+
Some content."""
988+
989+
ticket = Ticket(
990+
repo_id=1,
991+
number=128,
992+
key="128",
993+
title="Test ticket",
994+
state="closed",
995+
body=ticket_body,
996+
url="https://github.com/test/repo/issues/128"
997+
)
998+
999+
change = ConsolidatedChange(
1000+
type="ticket",
1001+
ticket_key="128",
1002+
prs=[],
1003+
commits=[]
1004+
)
1005+
1006+
generator = ReleaseNoteGenerator(test_config)
1007+
note = generator.create_release_note(change, ticket)
1008+
1009+
assert note.migration_notes is None
1010+
1011+
def test_case_insensitive_section_matching(self, test_config):
1012+
"""Test that section matching is case-insensitive."""
1013+
ticket_body = """## description
1014+
Lowercase description heading.
1015+
1016+
## MIGRATION
1017+
Uppercase migration heading."""
1018+
1019+
ticket = Ticket(
1020+
repo_id=1,
1021+
number=129,
1022+
key="129",
1023+
title="Test ticket",
1024+
state="closed",
1025+
body=ticket_body,
1026+
url="https://github.com/test/repo/issues/129"
1027+
)
1028+
1029+
change = ConsolidatedChange(
1030+
type="ticket",
1031+
ticket_key="129",
1032+
prs=[],
1033+
commits=[]
1034+
)
1035+
1036+
generator = ReleaseNoteGenerator(test_config)
1037+
note = generator.create_release_note(change, ticket)
1038+
1039+
assert note.description is not None
1040+
assert "Lowercase description heading" in note.description
1041+
assert note.migration_notes is not None
1042+
assert "Uppercase migration heading" in note.migration_notes
1043+
1044+
def test_extract_both_description_and_migration(self, test_config):
1045+
"""Test extracting both description and migration notes from same ticket."""
1046+
ticket_body = """## Description
1047+
This feature adds new functionality.
1048+
1049+
## Migration
1050+
Run: python manage.py migrate
1051+
1052+
## Testing
1053+
Test instructions."""
1054+
1055+
ticket = Ticket(
1056+
repo_id=1,
1057+
number=130,
1058+
key="130",
1059+
title="Test ticket",
1060+
state="closed",
1061+
body=ticket_body,
1062+
url="https://github.com/test/repo/issues/130"
1063+
)
1064+
1065+
change = ConsolidatedChange(
1066+
type="ticket",
1067+
ticket_key="130",
1068+
prs=[],
1069+
commits=[]
1070+
)
1071+
1072+
generator = ReleaseNoteGenerator(test_config)
1073+
note = generator.create_release_note(change, ticket)
1074+
1075+
assert note.description is not None
1076+
assert "This feature adds new functionality" in note.description
1077+
assert note.migration_notes is not None
1078+
assert "Run: python manage.py migrate" in note.migration_notes
1079+
assert "Testing" not in note.description
1080+
assert "Testing" not in note.migration_notes
1081+
1082+
def test_empty_ticket_body(self, test_config):
1083+
"""Test ticket with empty body."""
1084+
ticket = Ticket(
1085+
repo_id=1,
1086+
number=131,
1087+
key="131",
1088+
title="Test ticket",
1089+
state="closed",
1090+
body="",
1091+
url="https://github.com/test/repo/issues/131"
1092+
)
1093+
1094+
change = ConsolidatedChange(
1095+
type="ticket",
1096+
ticket_key="131",
1097+
prs=[],
1098+
commits=[]
1099+
)
1100+
1101+
generator = ReleaseNoteGenerator(test_config)
1102+
note = generator.create_release_note(change, ticket)
1103+
1104+
assert note.description is None
1105+
assert note.migration_notes is None
1106+
1107+
def test_none_ticket_body(self, test_config):
1108+
"""Test ticket with None body."""
1109+
ticket = Ticket(
1110+
repo_id=1,
1111+
number=132,
1112+
key="132",
1113+
title="Test ticket",
1114+
state="closed",
1115+
body=None,
1116+
url="https://github.com/test/repo/issues/132"
1117+
)
1118+
1119+
change = ConsolidatedChange(
1120+
type="ticket",
1121+
ticket_key="132",
1122+
prs=[],
1123+
commits=[]
1124+
)
1125+
1126+
generator = ReleaseNoteGenerator(test_config)
1127+
note = generator.create_release_note(change, ticket)
1128+
1129+
assert note.description is None
1130+
assert note.migration_notes is None

0 commit comments

Comments
 (0)