-
Notifications
You must be signed in to change notification settings - Fork 0
Expand file tree
/
Copy path.release_tool.toml
More file actions
1212 lines (1125 loc) · 53.3 KB
/
.release_tool.toml
File metadata and controls
1212 lines (1125 loc) · 53.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
config_version = "1.10"
# =============================================================================
# Release Tool Configuration
# =============================================================================
# This file controls how the release tool generates release notes by managing:
# - Repository information and GitHub integration
# - Issue extraction and consolidation policies
# - Version comparison and gap detection
# - Release note categorization and formatting
# - Output destinations (file, GitHub release, PR)
# =============================================================================
# Repository Configuration
# =============================================================================
[repository]
# code_repos (REQUIRED): List of GitHub repositories containing code
# Each repository must have a 'link' (owner/repo format) and an 'alias' for referencing
# The first repository in the list is the primary repository
# The alias is used in templates: {{code_repo.alias.link}} or {{code_repo.alias.slug}}
[[repository.code_repos]]
link = "sequentech/release-tool"
alias = "release-tool"
[[repository.code_repos]]
link = "sequentech/release-bot"
alias = "release-bot"
# issue_repos: List of repositories where issues/tickets are tracked
# Each repository must have a 'link' (owner/repo format) and an 'alias' for referencing
# If empty, uses code_repos for issues as well
# This is useful when issues are tracked in different repos than the code
# The alias is used in templates: {{issue_repo.alias.link}} or {{issue_repo.alias.slug}}
# Default: [] (uses code_repos)
# =============================================================================
# GitHub API Configuration
# =============================================================================
[github]
# GITHUB_TOKEN environment variable is REQUIRED for API authentication
# The token needs the following permissions:
# - repo (for accessing repositories, PRs, issues)
# - write:packages (if creating releases)
# How to create: https://github.com/settings/tokens
# Set it as an environment variable: export GITHUB_TOKEN="ghp_..."
# api_url: GitHub API base URL
# Change this only if using GitHub Enterprise Server
# Default: "https://api.github.com"
# For GitHub Enterprise: "https://github.yourcompany.com/api/v3"
api_url = "https://api.github.com"
# =============================================================================
# Database Configuration
# =============================================================================
[database]
# path: Location of the SQLite database file for caching GitHub data
# The database stores PRs, commits, issues, and releases to minimize API calls
# Relative paths are relative to the current working directory
# Default: "release_tool.db"
path = "release_tool.db"
# =============================================================================
# Pull Configuration
# =============================================================================
[pull]
# cutoff_date: Only fetch issues/PRs created after this date (ISO format: YYYY-MM-DD)
# This limits historical data fetching and speeds up initial sync
# Example: "2024-01-01" to only fetch data from 2024 onwards
# Default: null (fetch all historical data)
cutoff_date = "2025-01-01"
# parallel_workers: Number of parallel workers for GitHub API calls
# Higher values = faster sync, but may hit rate limits more quickly
# Recommended: 5-20 depending on your API rate limit
# Default: 10
parallel_workers = 10
# NOTE: Code repositories are always cloned to .release_tool_cache/{repo_alias}
# This path is no longer configurable to ensure consistency
# clone_method: Method for cloning repositories
# Options:
# - "https": Clone using HTTPS with GitHub token authentication
# Recommended for GitHub Actions with GITHUB_TOKEN
# - "ssh": Clone using SSH (git@github.com:owner/repo.git)
# Requires SSH keys to be configured
# - "auto": Try HTTPS first, fallback to SSH if it fails (DEFAULT)
# Provides flexibility across different environments
# Default: "auto"
# Use case:
# - GitHub Actions: Use "https" or "auto" (default)
# - Local development with SSH keys: Use "ssh" or "auto"
# - GitHub Enterprise: Use "https" with custom clone_url_template
clone_method = "auto"
# clone_url_template: Custom clone URL template for non-standard Git hosting
# Use {repo_full_name} as a placeholder for the repository (e.g., "owner/repo")
# This is useful for GitHub Enterprise or custom Git servers
# If not specified, uses standard GitHub URLs based on clone_method
# Default: null (uses github.com)
# Examples:
# - GitHub Enterprise: "https://github.enterprise.com/{repo_full_name}.git"
# - Custom Git server: "https://git.company.com/{repo_full_name}.git"
# - SSH on custom port: "ssh://git@gitlab.company.com:2222/{repo_full_name}.git"
# clone_url_template = "https://github.enterprise.com/{repo_full_name}.git"
# show_progress: Show progress updates during pull
# When true, displays messages like "pulling 13 / 156 issues (10% done)"
# Default: true
show_progress = true
# =============================================================================
# Issue Extraction and Consolidation Policy
# =============================================================================
# patterns: Ordered list of issue extraction patterns
# Each pattern is associated with a specific extraction strategy (where to look)
# and uses Python regex with NAMED CAPTURE GROUPS (use "issue" group for the ID)
#
# Patterns are tried in ORDER (by the "order" field). First match wins.
# Lower order numbers = higher priority. You can reorder by changing the numbers.
# TIP: Put more specific/reliable patterns first (lower order), generic ones last
#
# Available strategies:
# - "branch_name": Extract from PR branch name (e.g., feat/meta-123/main)
# - "pr_body": Extract from PR description text
# - "pr_title": Extract from PR title
# - "commit_message": Extract from commit message text
#
# Pattern structure:
# [[issue_policy.patterns]]
# order = 1 # Priority (lower = tried first)
# strategy = "branch_name" # Where to look
# pattern = "regex_here" # What to match (use (?P<issue>\\d+) for ID)
# description = "explanation" # Optional: what this pattern matches
# ORDER 1: Branch name (most reliable, structured format)
# Matches: feat/meta-123/main, fix/repo-456.whatever/develop
# Format: <type>/<repo>-<issue_number>[.optional]/<target_branch>
[[issue_policy.patterns]]
order = 1
strategy = "branch_name"
pattern = "/(?P<repo>\\w+)-(?P<issue>\\d+)"
description = "Branch name format: type/repo-123/target"
# ORDER 2: Parent issue URL in PR body (backup policy)
# Matches: "Parent issue: https://github.com/owner/repo/issues/999"
# Use this when the branch name doesn't follow convention
[[issue_policy.patterns]]
order = 2
strategy = "pr_body"
pattern = "(Parent issue:.*?/issues/|sequentech/meta#)(?P<issue>\\d+)"
description = "Parent issue URL in PR description"
# ORDER 3: GitHub issue reference in PR title
# Matches: "#123" in the PR title
[[issue_policy.patterns]]
order = 3
strategy = "pr_title"
pattern = "#(?P<issue>\\d+)"
description = "GitHub issue reference (#123) in PR title"
[issue_policy]
# no_issue_action: What to do when a commit/PR has no associated issue
# Valid values:
# - "ignore": Silently skip the warning, include in release notes
# - "warn": Print a warning but continue (RECOMMENDED for most teams)
# - "error": Stop the release note generation with an error
# Default: "warn"
# Use "error" for strict issue tracking, "warn" for flexibility
no_issue_action = "warn"
# unclosed_issue_action: What to do with issues that are still open
# Valid values:
# - "ignore": Include open issues in release notes without warning
# - "warn": Print a warning but include them (RECOMMENDED)
# - "error": Stop if any issues are still open
# Default: "warn"
unclosed_issue_action = "warn"
# partial_issue_action: What to do when a issue is extracted but not found in DB
# or found in a different repository than expected
#
# "Partial matches" occur when:
# - Issue key extracted from branch/PR, but issue not in database (common causes):
# * Issue created before your pull cutoff date
# * Issue doesn't exist (typo in branch name)
# * Sync hasn't been run yet
# - Issue found in database, but in wrong repository:
# * Branch says "meta-8624" but issue is in "step" repo
# * Mismatch between issue_repos config and actual issue location
#
# Valid values:
# - "ignore": Silently skip partial matches, continue with next pattern
# - "warn": Print detailed warnings with potential causes and issue links (RECOMMENDED)
# - "error": Stop release note generation if any partial matches found
#
# Default: "warn"
# When in warn mode, the tool will list:
# - Which issues had partial matches
# - Whether they were not found or in different repo
# - Potential reasons (cutoff date, typo, wrong repo, etc.)
# - Links to issues if found in different repos
partial_issue_action = "warn"
# inter_release_duplicate_action: What to do when a issue appears in multiple releases
# This checks if issues in the new release already exist in semantically-earlier releases
# (e.g., creating 9.3.1 and finding issues that already appeared in 9.2.0)
#
# This happens when a single issue includes multiple PRs that land in different releases,
# or when backporting fixes to older release branches.
#
# Valid values:
# - "ignore": Exclude duplicate issues from the new release (keep only in earlier release)
# - "warn": Include duplicates but show a warning (RECOMMENDED)
# - "error": Stop release note generation if duplicates are found
#
# Default: "warn"
# Use "ignore" if you want strict deduplication (one issue per release across history)
# Use "warn" for visibility into which issues span multiple releases
inter_release_duplicate_action = "warn"
# consolidation_enabled: Group multiple commits by their parent issue
# When true: Commits with the same issue (e.g., ISSUE-123) are grouped
# into a single release note entry
# When false: Each commit appears as a separate entry in release notes
# Default: true
# RECOMMENDED: true (makes release notes more concise and readable)
consolidation_enabled = true
# release_notes_inclusion_policy: Control which types of changes appear in release notes
# This is a list of change types to include. Valid values: "issues", "pull-requests", "commits"
#
# Change Types:
# - "issues": Include commits/PRs that are associated with a issue/issue
# - "pull-requests": Include PRs even if they don't have an associated issue
# - "commits": Include direct commits that have no PR and no issue
#
# How it works:
# - Changes are categorized by type: "issue", "pr", or "commit"
# - Only changes whose type is in this list will appear in release notes
# - Excluded changes won't appear in any category (including "Other")
# - Excluded changes won't be counted in statistics
# - Excluded changes are completely absent from the final output
#
# Common configurations:
# 1. Default ["issues", "pull-requests"] (RECOMMENDED):
# ✅ Include: Commits/PRs with issues
# ✅ Include: PRs without issues
# ❌ EXCLUDE: Standalone commits (no PR, no issue)
# Use case: Standard workflow where all significant work goes through PRs
#
# 2. Issues only ["issues"]:
# ✅ Include: Commits/PRs with issues
# ❌ EXCLUDE: PRs without issues
# ❌ EXCLUDE: Standalone commits
# Use case: Strict issue tracking, ignore untracked work
#
# 3. Everything ["issues", "pull-requests", "commits"]:
# ✅ Include: All changes regardless of type
# Use case: Small teams, less formal process, want complete history
#
# 4. PRs only ["pull-requests"]:
# ✅ Include: PRs without issues
# ❌ EXCLUDE: Commits/PRs with issues
# ❌ EXCLUDE: Standalone commits
# Use case: Unusual - only show untracked PRs
#
# Default: ["issues", "pull-requests"]
# This default works well for most teams using PR-based workflows
release_notes_inclusion_policy = ["issues", "pull-requests"]
# description_section_regex: Regex to extract description from issue body
# Uses Python regex with capturing group (group 1 is extracted)
# Matches headers with any level (1-6 hashes) containing "Description" or "Summary"
# Stops at major section headers (1-2 hashes like "# Tasks", "## Other")
# Continues past nested sub-sections (### or more hashes)
# The tool gracefully handles issues without description sections
# Default: r'(?:#{1,6} (?:Description|Summary).*?)\n(.*?)(?=\n#{1,2} |\Z)'
# Set to empty string "" to disable description extraction
# NOTE: In TOML, backslashes must be doubled: \n becomes \\n, \Z becomes \\Z
description_section_regex = "(?:#{1,6} (?:Description|Summary).*?)\\n(.*?)(?=\\n#{1,2} |\\Z)"
# migration_section_regex: Regex to extract migration notes from issue body
# Useful for database migrations, breaking changes, upgrade steps
# Matches headers with any level (1-6 hashes) containing "Migration" or "Migration Notes"
# Stops at major section headers (1-2 hashes like "# Tasks", "## Other")
# Continues past nested sub-sections (### or more hashes)
# The tool gracefully handles issues without migration sections
# Default: r'(?:#{1,6} (?:Migration|Migration Notes).*?)\n(.*?)(?=\n#{1,2} |\Z)'
# Set to empty string "" to disable migration notes extraction
# NOTE: In TOML, backslashes must be doubled: \n becomes \\n, \Z becomes \\Z
migration_section_regex = "(?:#{1,6} (?:Migration|Migration Notes).*?)\\n(.*?)(?=\\n#{1,2} |\\Z)"
# =============================================================================
# Version Comparison and Gap Detection Policy
# =============================================================================
[version_policy]
# gap_detection: Check for missing versions between releases
# Detects gaps like 1.0.0 → 1.2.0 (missing 1.1.0)
# Valid values:
# - "ignore": Don't check for version gaps
# - "warn": Print a warning if gaps detected (RECOMMENDED)
# - "error": Stop the process if gaps are detected
# Default: "warn"
gap_detection = "warn"
# tag_prefix: Prefix used for version tags in Git
# The tool will look for tags like "v1.0.0" if prefix is "v"
# Common values: "v", "release-", "" (empty for no prefix)
# Default: "v"
tag_prefix = "v"
# =============================================================================
# Branch Management Policy
# =============================================================================
# Controls how release branches are created and managed
[branch_policy]
# release_branch_template: Template for release branch names (Jinja2 syntax)
# Use {{major}}, {{minor}}, {{patch}} as placeholders
# Examples:
# - "release/{{major}}.{{minor}}" → "release/9.1"
# - "rel-{{major}}.{{minor}}.x" → "rel-9.1.x"
# - "v{{major}}.{{minor}}" → "v9.1"
# Default: "release/{{major}}.{{minor}}"
release_branch_template = "release/{{major}}.{{minor}}"
# default_branch: The default branch for new major versions
# New major versions (e.g., 9.0.0 when coming from 8.x.x) will branch from this
# Common values: "main", "master", "develop"
# Default: "main"
default_branch = "main"
# create_branches: Automatically create release branches if they don't exist
# When true, the tool will create a new release branch automatically
# When false, you must create branches manually
# Default: true
create_branches = true
# branch_from_previous_release: Branch new minor versions from previous release
# Controls the branching strategy for new minor versions:
# - true: 9.1.0 branches from release/9.0 (if it exists)
# - false: 9.1.0 branches from main (default_branch)
# This enables hotfix workflows where release branches persist
# Default: true
branch_from_previous_release = true
# =============================================================================
# Release Notes Categorization
# =============================================================================
# Categories group release notes by the labels on issues/PRs
# Each category can match multiple labels, and has a display order
#
# Label Matching with Source Prefixes:
# You can specify where labels should match from using prefixes:
# - "pr:label_name" = Only match this label from Pull Requests
# - "issue:label_name" = Only match this label from Issues/Issues
# - "label_name" = Match from EITHER PRs or issues (default)
#
# This is useful when PRs and issues use the same label names differently.
# For example:
# - PRs might use "bug" for any bug-related code change
# - Issues might use "bug" only for confirmed bugs needing fixes
# - You can categorize them separately: ["pr:bug"] vs ["issue:bug"]
#
# Category structure:
# [[release_notes.categories]]
# name = "Display Name" # Shown in the release notes
# labels = ["label1", "pr:label2", "issue:label3"] # With optional prefixes
# order = 1 # Display order (lower numbers appear first)
[release_notes]
# excluded_labels: Skip issues/PRs with these labels from release notes
# Useful for internal changes, CI updates, etc.
# excluded_labels = [
# "skip-changelog",
# "internal",
# "wip",
# "do-not-merge",
# "release",
# ]
excluded_labels = [
"skip-changelog",
"internal",
"wip",
"do-not-merge",
"release",
]
# title_template: Jinja2 template for the release notes title
# Available variables:
# - {{ version }}: The version being released (e.g., "1.2.3")
# Default: "Release {{ version }}"
title_template = "Release {{ version }}"
# entry_template: Jinja2 template for each individual release note entry
# This is a POWERFUL template that lets you customize exactly how each change
# appears in the release notes. You can use Jinja2 syntax including conditionals,
# loops, filters, and all available variables.
#
# IMPORTANT: HTML-like behavior for whitespace and line breaks
# - Multiple spaces/tabs are collapsed into a single space (like HTML)
# - New lines in the template are ignored unless you use <br> or <br/>
# - Use <br> or <br/> for explicit line breaks in the output
# - Use for explicit spaces that won't collapse (e.g., = two spaces)
# - This allows multi-line templates for readability while controlling output
#
# Available variables for each entry:
# - {{ title }} : The title/summary of the change (string)
# Example: "Fix authentication bug in login flow"
# - {{ url }} : Smart link prioritizing issue over PR (string or None)
# Example: "https://github.com/owner/repo/issues/123"
# Priority: issue_url > pr_url
# - {{ issue_url }} : Direct link to the issue/issue (string or None)
# Example: "https://github.com/owner/repo/issues/123"
# - {{ pr_url }} : Direct link to the pull request (string or None)
# Example: "https://github.com/owner/repo/pull/456"
# - {{ short_link }} : Short format link (string or None)
# Example: "#123"
# - {{ short_repo_link }} : Short format with repo name (string or None)
# Example: "owner/repo#123"
# - {{ pr_numbers }} : List of related PR numbers (list of int)
# Example: [123, 124]
# - {{ authors }} : List of author objects (list of dict)
# Each author is a dict with comprehensive information:
# - name: Git author name (e.g., "John Doe")
# - email: Git author email (e.g., "john@example.com")
# - username: GitHub login (e.g., "johndoe")
# - github_id: GitHub user ID (e.g., 12345)
# - display_name: GitHub display name
# - avatar_url: Profile picture URL
# - profile_url: GitHub profile URL
# - company: Company name
# - location: Location
# - bio: Bio text
# - blog: Blog URL
# - user_type: "User", "Bot", or "Organization"
# - identifier: Best identifier (username > name > email)
# - mention: @mention format (e.g., "@johndoe")
# - full_display_name: Best display name
# - {{ description }} : Extracted description text (string or None)
# Example: "This fixes the login flow by..."
# - {{ migration_notes }} : Extracted migration notes (string or None)
# Example: "Run: python manage.py migrate"
# - {{ labels }} : List of label names (list of string)
# Example: ["bug", "critical", "security"]
# - {{ issue_key }} : Issue identifier (string or None)
# Example: "#123" or "JIRA-456"
# - {{ category }} : Assigned category name (string or None)
# Example: "Bug Fixes"
# - {{ commit_shas }} : List of commit SHA hashes (list of string)
# Example: ["a1b2c3d", "e4f5g6h"]
#
# Jinja2 syntax examples:
# - Conditionals: {% if url %}...{% endif %}
# - Loops: {% for author in authors %}@{{ author.username }}{% endfor %}
# - Filters: {{ description|truncate(100) }}
# - Boolean check: {% if pr_numbers %}(#{{ pr_numbers[0] }}){% endif %}
# - Line breaks: Use <br> or <br/> for new lines in output
# - Author fields: {{ author.username }}, {{ author.name }}, {{ author.mention }}
#
# Template examples:
# 1. Minimal (single line):
# entry_template = "- {{ title }}"
#
# 2. With PR link (single line):
# entry_template = "- {{ title }}{% if url %} ([#{{ pr_numbers[0] }}]({{ url }})){% endif %}"
#
# 3. With GitHub @mentions (uses author.mention for smart @username or name):
# entry_template = '''- {{ title }}
# {% if url %}([#{{ pr_numbers[0] }}]({{ url }})){% endif %}
# {% if authors %}<br>by {% for author in authors %}{{ author.mention }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}'''
#
# 4. With author names and emails:
# entry_template = '''- {{ title }}
# {% if authors %}<br>by {% for author in authors %}{{ author.name }} <{{ author.email }}>{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}'''
#
# 5. With author avatars and profile links (for markdown/HTML):
# entry_template = '''- {{ title }}
# {% if authors %}<br>by {% for author in authors %}<a href="{{ author.profile_url }}"><img src="{{ author.avatar_url }}" width="20"/> {{ author.display_name }}</a>{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}'''
#
# 6. Complex multi-line with labels, migration notes, and rich authors:
# entry_template = '''- {{ title }}
# {% if url %}([#{{ pr_numbers[0] }}]({{ url }})){% endif %}
# {% if labels %} `{{ labels|join('` `') }}`{% endif %}
# {% if authors %}<br>Contributors: {% for author in authors %}@{{ author.username or author.name }}{% if author.company %} ({{ author.company }}){% endif %}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}
# {% if migration_notes %}<br> **Migration:** {{ migration_notes }}{% endif %}'''
#
# Default: Multi-line template with title, short_repo_link, and author mentions
# The whitespace will collapse, <br> tags create line breaks
# Uses author.mention which gives @username if available, otherwise falls back to name
# Uses short_repo_link for concise display (e.g., "meta#123" instead of full URL)
entry_template = '''- {{ title }}{% if url and short_repo_link %} ([{{ short_repo_link }}]({{ url }})){% elif url %} ([link]({{ url }})){% endif %}
{% if authors %}
by {% for author in authors %}{{ author.mention }}{% if not loop.last %}, {% endif %}{% endfor %}{% endif %}'''
# release_output_template: MASTER Jinja2 template for GitHub release notes output
# This is an ADVANCED feature that gives you complete control over the release
# notes structure. When set, this template replaces the default category-based
# layout and lets you design your own custom format.
#
# WHEN TO USE:
# - You want a custom layout (e.g., flat list, grouped by type, etc.)
# - You need to iterate over migrations or descriptions across all issues
# - You want full control over the output structure
#
# IMPORTANT: HTML-like behavior for whitespace (same as entry_template)
# - Multiple spaces collapse to single space
# - Line breaks: Use <br> or <br/> for new lines in output
# - Non-breaking spaces: Use for explicit spaces that won't collapse
#
# Available variables:
# - {{ version }} : Version string (e.g., "1.2.3")
# - {{ title }} : Rendered release title (from title_template)
# - {{ year }} : Current year (e.g., "2025")
# - {{ categories }} : List of category dicts with 'name' and 'notes'
# - {{ all_notes }} : Flat list of all note dicts (across categories)
# - {{ render_entry(note) }}: Function to render a note using entry_template
#
# Each note dict contains:
# - title, url (prioritizes issue_url over pr_url), issue_url, pr_url
# - short_link (#123), short_repo_link (meta#123)
# - pr_numbers, commit_shas, labels, issue_key, category
# - description, migration_notes (processed, may be None)
# - authors (list of author dicts with all fields)
#
# Template examples:
#
# 1. Default category-based layout (equivalent to not setting output_template):
# output_template = '''# {{ title }}
#
# {% for category in categories %}
# ## {{ category.name }}
# {% for note in category.notes %}
# {{ render_entry(note) }}
# {% endfor %}
# {% endfor %}'''
#
# 2. Flat list without categories:
# output_template = '''# {{ title }}
#
# {% for note in all_notes %}
# {{ render_entry(note) }}
# {% endfor %}'''
#
# 3. Custom layout with migrations section:
# output_template = '''# {{ title }}
#
# ## Changes
# {% for note in all_notes %}
# {{ render_entry(note) }}
# {% endfor %}
#
# ## Migration Notes
# {% for note in all_notes %}
# {% if note.migration_notes %}
# ### {{ note.title }}
# {{ note.migration_notes }}
# {% endif %}
# {% endfor %}'''
#
# 4. Grouped by issue with full descriptions:
# output_template = '''# {{ title }}
#
# {% for note in all_notes %}
# ## {{ note.title }}
# {% if note.description %}
# {{ note.description }}
# {% endif %}
# {% if note.url %}
# **Pull Request:** [#{{ note.pr_numbers[0] }}]({{ note.url }})
# {% endif %}
# {% if note.authors %}
# **Authors:** {% for author in note.authors %}{{ author.mention }}{% if not loop.last %}, {% endif %}{% endfor %}
# {% endif %}
# {% if note.migration_notes %}
# **Migration:** {{ note.migration_notes }}
# {% endif %}
# {% endfor %}'''
#
# 5. Custom grouping with manual entry rendering:
# output_template = '''# {{ title }}
#
# ## Features & Fixes
# {% for category in categories %}
# {% if category.name in ["Features", "Bug Fixes"] %}
# ### {{ category.name }}
# {% for note in category.notes %}
# {{ render_entry(note) }}
# {% endfor %}
# {% endif %}
# {% endfor %}
#
# ## Other Changes
# {% for category in categories %}
# {% if category.name not in ["Features", "Bug Fixes"] %}
# {% for note in category.notes %}
# - {{ note.title }}{% if note.url %} ([#{{ note.pr_numbers[0] }}]({{ note.url }})){% endif %}
# {% endfor %}
# {% endif %}
# {% endfor %}'''
#
# Default: Comprehensive template with breaking changes, migrations, descriptions, and categorized changes
release_output_template = '''{% set breaking_with_desc = all_notes|selectattr('category', 'equalto', '💥 Breaking Changes')|selectattr('description')|list %}
{% if breaking_with_desc|length > 0 %}
<br>## 💥 Breaking Changes
{% for note in breaking_with_desc %}
<br>### {{ note.title }}
{{ note.description }}
{% if note.url %}See {{ note.url }} for details.{% endif %}
{% endfor %}
{% endif %}
{% set migration_notes = all_notes|selectattr('migration_notes')|list %}
{% if migration_notes|length > 0 %}
<br>## 🔄 Migrations
{% for note in migration_notes %}
<br>### {{ note.title }}
{{ note.migration_notes }}
{% if note.url and note.short_repo_link %}See [{{ note.short_repo_link }}]({{ note.url }}) for details.{% elif note.url %}See {{ note.url }} for details.{% endif %}
{% endfor %}
{% endif %}
{% set non_breaking_with_desc = all_notes|rejectattr('category', 'equalto', '💥 Breaking Changes')|selectattr('description')|list %}
{% if non_breaking_with_desc|length > 0 %}
<br>## 📝 Highlights
{% for note in non_breaking_with_desc %}
<br>### {{ note.title }}
{{ note.description }}
{% if note.url and note.short_repo_link %}See [{{ note.short_repo_link }}]({{ note.url }}) for details.{% elif note.url %}See {{ note.url }} for details.{% endif %}
{% endfor %}
{% endif %}
<br>## 📋 All Changes
{% for category in categories %}
<br>### {{ category.name }}
{% for note in category.notes %}
{% if loop.first %}<br>{% endif %}{{ render_entry(note) }}<br>
{% endfor %}
{% endfor %}'''
# =============================================================================
# Output Configuration
# =============================================================================
[[release_notes.categories]]
name = "💥 Breaking Changes"
labels = ["breaking-change", "breaking"]
order = 1
alias = "breaking"
[[release_notes.categories]]
name = "🚀 Features"
labels = ["feature", "enhancement", "feat"]
order = 2
alias = "features"
[[release_notes.categories]]
name = "🛠 Bug Fixes"
labels = ["bug", "fix", "bugfix", "hotfix"]
order = 3
alias = "bugfixes"
[[release_notes.categories]]
name = "📖 Documentation"
labels = ["docs", "documentation"]
order = 4
alias = "docs"
[[release_notes.categories]]
name = "🛡 Security Updates"
labels = ["security"]
order = 5
alias = "security"
# IMPORTANT: Fallback Category
# The category with alias="other" serves as the fallback for any issues/PRs
# that don't match any other category labels. You can name it whatever you want
# (e.g., "Other", "Miscellaneous", "Other Changes"), but the alias MUST be "other".
#
# - name: Display name for this category (customizable)
# - labels: Should be empty [] to catch unmatched items
# - order: Should be high (e.g., 99) to appear last
# - alias: MUST be "other" for the tool to recognize this as the fallback category
[[release_notes.categories]]
name = "Other"
labels = []
order = 99
alias = "other"
# =============================================================================
# PR Code Generation Templates (Multi-Repository Support)
# =============================================================================
# Configure code generation templates for each code repository.
# Format: [output.pr_code.<alias>] where <alias> matches a repository alias from [[repository.code_repos]]
#
# Each repository can have multiple [[output.pr_code.<alias>.templates]] entries to generate
# different outputs (e.g., Docusaurus docs, CHANGELOG.md, custom formats).
# PR code templates for the "release-tool" repository
[output.pr_code.release-tool]
[[output.pr_code.release-tool.templates]]
# output_template: Jinja2 template for the output file content
# This is a POWERFUL template that gives you complete control over the output
# structure. Use Jinja2 syntax including conditionals, loops, filters, and all
# available variables.
#
# WHEN TO USE:
# - Generate Docusaurus documentation with frontmatter
# - Create custom changelog formats
# - Generate multiple output files from the same release data
# - Wrap release notes with additional metadata or formatting
#
# IMPORTANT: HTML-like behavior for whitespace
# - Multiple spaces/tabs collapse to single space (like HTML)
# - Line breaks: Use <br> or <br/> for explicit line breaks in output
# - Non-breaking spaces: Use for spaces that won't collapse
# - This allows multi-line templates for readability while controlling output
#
# Available variables:
# - {{ version }} : Version string (e.g., "1.2.3")
# - {{ major }} : Major version number (e.g., "1")
# - {{ minor }} : Minor version number (e.g., "2")
# - {{ patch }} : Patch version number (e.g., "3")
# - {{ title }} : Rendered release title (from title_template)
# - {{ year }} : Current year (e.g., "2025")
# - {{ categories }} : List of category dicts with 'name', 'alias', 'notes'
# - {{ all_notes }} : Flat list of all note dicts (across categories)
# - {{ render_entry(note) }} : Function to render a note using entry_template
# - {{ render_release_notes() }} : Function to render the default GitHub release notes
#
# Each note dict in all_notes or category.notes contains:
# - title, url (prioritizes issue_url over pr_url), issue_url, pr_url
# - short_link (#123), short_repo_link (meta#123)
# - pr_numbers, commit_shas, labels, issue_key, category
# - description, migration_notes (processed, may be None)
# - authors (list of author dicts with all fields)
#
# Template examples:
#
# 1. Docusaurus with frontmatter (DEFAULT - wraps GitHub release notes):
# output_template = '''---
# id: release-{{version}}
# title: {{title}}
# ---
# # Release {{version}}
#
# {{ render_release_notes() }}<br>'''
#
# 2. Simple CHANGELOG (just release notes):
# output_template = '''{{ render_release_notes() }}'''
#
# 3. Custom category-based layout:
# output_template = '''# {{ title }}
#
# {% for category in categories %}
# ## {{ category.name }}
# {% for note in category.notes %}
# {{ render_entry(note) }}
# {% endfor %}
# {% endfor %}'''
#
# 4. Flat list without categories:
# output_template = '''# {{ title }}
#
# {% for note in all_notes %}
# {{ render_entry(note) }}
# {% endfor %}'''
#
# Default: Docusaurus template with frontmatter and default release notes
output_template = '''---
id: release-{{version}}
title: {{title}}
---
<!--
SPDX-FileCopyrightText: {{year}} Sequent Tech Inc <legal@sequentech.io>
SPDX-License-Identifier: AGPL-3.0-only
-->
# Release {{version}}
{{ render_release_notes() }}'''
# output_path: Path template for the output file (Jinja2 syntax)
# This file will be created/updated when using the generate and push commands.
#
# Available variables for path substitution:
# - {{version}}: Full version string (e.g., "1.2.3", "2.0.0-rc.1")
# - {{major}}: Major version number only (e.g., "1")
# - {{minor}}: Minor version number only (e.g., "2")
# - {{patch}}: Patch version number only (e.g., "3")
#
# Path template examples:
# - "CHANGELOG.md": Single changelog file (overwrites)
# - "docs/releases/{{version}}.md": Separate file per version
# - "docs/docusaurus/docs/releases/release-{{major}}.{{minor}}/release-{{major}}.{{minor}}.{{patch}}.md": Organized by version
#
# Default example: Docusaurus versioned docs
output_path = "docs/docusaurus/docs/releases/release-{{major}}.{{minor}}/release-{{major}}.{{minor}}.{{patch}}.md"
# release_version_policy: Policy for generating documentation files for release candidates
#
# This controls how documentation files are generated when working with release candidates (RC versions).
# It only affects this template's output_path generation, NOT GitHub release notes.
#
# Valid values:
# - "final-only" (DEFAULT): Generate documentation as if it were the final version
# * RC documentation files use final version name (e.g., 11.0.0.md for 11.0.0-rc.1)
# * Content compares against previous final version (e.g., 10.x.x)
# * Each RC overwrites the same file, building up cumulative changelog
# * Final version generates to the same file with complete changelog
# * GitHub release notes still use standard comparison (RC.1 vs RC.0)
#
# - "include-rcs": Generate separate documentation files for each RC
# * RC documentation files include RC suffix (e.g., 11.0.0-rc.1.md)
# * Content uses standard version comparison (RC.1 vs RC.0 or previous final)
# * Each RC generates a separate file
# * Final version generates to its own file (11.0.0.md)
# * GitHub release notes use standard comparison
#
# Use Cases:
# - "final-only": Best for Docusaurus/versioned docs where you want one file per version
# that gets updated as RCs progress to final. This ensures documentation
# always shows the complete changelog for the upcoming release.
#
# - "include-rcs": Best when you want to document each RC separately, keeping history
# of what changed in each release candidate. Useful for detailed
# release tracking or when RCs are deployed to different environments.
#
# Example with "final-only" for version 11.0.0:
# - Generate 11.0.0-rc.0:
# * Doc file: docs/releases/11.0.0.md (compares 10.5.0 → 11.0.0-rc.0)
# * GitHub release: 11.0.0-rc.0 (compares 10.5.0 → 11.0.0-rc.0)
# - Generate 11.0.0-rc.1:
# * Doc file: docs/releases/11.0.0.md (compares 10.5.0 → 11.0.0-rc.1, OVERWRITES rc.0 docs)
# * GitHub release: 11.0.0-rc.1 (compares 11.0.0-rc.0 → 11.0.0-rc.1)
# - Generate 11.0.0:
# * Doc file: docs/releases/11.0.0.md (compares 10.5.0 → 11.0.0, OVERWRITES rc.1 docs)
# * GitHub release: 11.0.0 (compares 10.5.0 → 11.0.0, complete changelog)
#
# Example with "include-rcs" for version 11.0.0:
# - Generate 11.0.0-rc.0:
# * Doc file: docs/releases/11.0.0-rc.0.md (compares 10.5.0 → 11.0.0-rc.0)
# * GitHub release: 11.0.0-rc.0 (compares 10.5.0 → 11.0.0-rc.0)
# - Generate 11.0.0-rc.1:
# * Doc file: docs/releases/11.0.0-rc.1.md (compares 11.0.0-rc.0 → 11.0.0-rc.1)
# * GitHub release: 11.0.0-rc.1 (compares 11.0.0-rc.0 → 11.0.0-rc.1)
# - Generate 11.0.0:
# * Doc file: docs/releases/11.0.0.md (compares 10.5.0 → 11.0.0)
# * GitHub release: 11.0.0 (compares 10.5.0 → 11.0.0)
#
# Default: "final-only"
release_version_policy = "final-only"
# consolidated_code_repos_aliases: List of code repository aliases to consolidate changes from (OPTIONAL)
# Controls which repositories' changes are included in this template's output.
#
# When null (DEFAULT): Only includes changes from the current repository (the one this template belongs to)
# When a list: Includes changes that touched ANY of the listed repositories (union logic)
#
# Use case: When you have multiple related repositories and want to generate a single
# consolidated changelog that includes changes from all of them.
#
# Examples:
# - null: Only "release-tool" changes appear in release-tool's release notes (DEFAULT)
# - ["release-tool", "release-bot"]: Changes from both repos appear in this output
# - ["release-tool", "release-bot", "meta"]: Changes from all three repos are consolidated
#
# How it works:
# - A change is included if ANY of its commits or PRs belong to one of the listed repos
# - Uses union logic (OR): change touches repo1 OR repo2 OR repo3
# - The repo aliases must match those defined in [[repository.code_repos]]
#
# Default: null (current repo only)
# consolidated_code_repos_aliases = ["release-tool", "release-bot"]
# consolidated_code_repos_aliases = null
# =============================================================================
# PR code templates for the "release-bot" repository
# =============================================================================
[output.pr_code.release-bot]
[[output.pr_code.release-bot.templates]]
# Template for release-bot - simpler than release-tool (no Docusaurus frontmatter)
output_template = '''<!--
SPDX-FileCopyrightText: {{year}} Sequent Tech Inc <legal@sequentech.io>
SPDX-License-Identifier: AGPL-3.0-only
-->
# Release {{version}}
{{ render_release_notes() }}'''
# Output path: One file per version in docs/releases/
output_path = "docs/releases/{{version}}.md"
# Use final-only policy like release-tool
release_version_policy = "final-only"
# Only include changes from release-bot repo (not release-tool)
# consolidated_code_repos_aliases = null
# =============================================================================
# Release Notes Formatting and Content
# =============================================================================
[output]
# draft_output_path: Path template for draft release notes (generate command, Jinja2 syntax)
# This is where 'generate' command saves files by default (when --output not specified)
# Files are saved here for review/editing before pushing to GitHub
#
# Available variables:
# - {{code_repo.current.slug}}: Current code repository slug (e.g., "sequentech-release-tool")
# - {{code_repo.current.link}}: Current code repository link (e.g., "sequentech/release-tool")
# - {{code_repo.<alias>.slug}}: Code repository by alias, sanitized (e.g., "sequentech-release-tool")
# - {{code_repo.<alias>.link}}: Code repository by alias, link format
# - {{version}}: Full version string
# - {{major}}: Major version number
# - {{minor}}: Minor version number
# - {{patch}}: Patch version number
# - {{output_file_type}}: Type of output file:
# - "release": GitHub release notes draft
# - "code-0": First pr_code template output
# - "code-1": Second pr_code template output
# - "code-N": Nth pr_code template output
# - "doc": (deprecated, use code-N instead)
#
# Examples:
# - ".release_tool_cache/draft-releases/{{code_repo.current.slug}}/{{version}}-{{output_file_type}}.md": Organized by repo and version (DEFAULT)
# This will create files like:
# - "1.0.0-release.md" (GitHub release draft)
# - "1.0.0-code-0.md" (first pr_code template)
# - "1.0.0-code-1.md" (second pr_code template)
# - "drafts/{{major}}.{{minor}}.{{patch}}-{{output_file_type}}.md": Simple draft folder
# - "/tmp/releases/{{code_repo.current.slug}}-{{version}}-{{output_file_type}}.md": Temporary location
#
# Default: ".release_tool_cache/draft-releases/{{code_repo.current.slug}}/{{version}}-{{output_file_type}}.md"
draft_output_path = ".release_tool_cache/draft-releases/{{code_repo.current.slug}}/{{version}}-{{output_file_type}}.md"
# assets_path: Path template for downloaded media assets (images, videos, Jinja2 syntax)
# Images and videos referenced in issue descriptions will be downloaded here
# and references will be updated to use local paths in the release notes
# This is useful for Docusaurus and other static site generators
#
# Available variables (same as output_path):
# - {{version}}: Full version string
# - {{major}}: Major version number
# - {{minor}}: Minor version number
# - {{patch}}: Patch version number
#
# Path must be relative to output_path for correct markdown references
# Examples:
# - "docs/releases/assets/{{version}}": Organized by version
# - "static/img/releases/{{major}}.{{minor}}": Shared across patches
# - "assets/{{version}}": Simple structure
#
# Default: "docs/releases/assets/{{version}}"
assets_path = "docs/docusaurus/docs/releases/release-{{major}}.{{minor}}/assets"
# download_media: Download images and videos from issue descriptions
# When true: Downloads media files and updates references to local paths
# When false: Keeps original URLs in release notes
# Default: true
# RECOMMENDED: true for static sites (Docusaurus), false for GitHub releases
download_media = false
# create_github_release: Automatically create a GitHub release
# When true: Uploads release notes to GitHub Releases
# When false: Only generates markdown (no upload)
# Default: true
# CLI override: --release / --no-release
# SECURITY: Requires GitHub token with repo write permissions
create_github_release = true
# create_pr: Automatically create a PR with the release notes file
# When true: Creates a PR to add/update the release notes file
# When false: No PR is created
# Requires: output_path to be configured
# Default: true
# CLI override: --pr / --no-pr
create_pr = true
# release_mode: Default release mode for GitHub releases
# Options:
# - "draft": Releases are created as drafts (unpublished)
# - "published": Releases are published immediately
# Default: "draft"
# CLI override: --release-mode draft|published
# Use case: Set to "draft" if you want to review releases before publishing
release_mode = "draft"
# prerelease: Mark GitHub releases as prereleases
# Options:
# - "auto": Auto-detect from version (e.g., "1.0.0-rc.1" → prerelease, "1.0.0" → stable)
# - true: Always mark as prerelease
# - false: Always mark as stable/final
# Default: "auto"
# CLI override: --prerelease auto|true|false
# Use cases:
# - "auto": Most flexible, automatically handles RCs and stable releases
# - true: If you typically create beta/RC releases and want to be explicit
# - false: For projects that only do stable releases
prerelease = false