multicz 1.0.0__tar.gz → 1.2.0__tar.gz

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (76) hide show
  1. {multicz-1.0.0 → multicz-1.2.0}/PKG-INFO +1 -1
  2. {multicz-1.0.0 → multicz-1.2.0}/pyproject.toml +1 -1
  3. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/changelog/bucket.py +41 -5
  4. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/changelog/debian.py +4 -2
  5. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/changelog/markdown.py +13 -2
  6. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/bump.py +1 -0
  7. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/presenters.py +2 -0
  8. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/config/models.py +8 -0
  9. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/writers/debian_changelog.py +2 -1
  10. {multicz-1.0.0 → multicz-1.2.0}/README.md +0 -0
  11. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/__init__.py +0 -0
  12. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/__main__.py +0 -0
  13. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/changelog/__init__.py +0 -0
  14. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/__init__.py +0 -0
  15. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/_shared.py +0 -0
  16. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/__init__.py +0 -0
  17. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/artifacts.py +0 -0
  18. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/changed.py +0 -0
  19. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/changelog.py +0 -0
  20. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/check.py +0 -0
  21. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/config.py +0 -0
  22. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/explain.py +0 -0
  23. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/get.py +0 -0
  24. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/graph.py +0 -0
  25. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/init.py +0 -0
  26. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/plan.py +0 -0
  27. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/release_notes.py +0 -0
  28. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/state.py +0 -0
  29. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/status.py +0 -0
  30. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/commands/validate.py +0 -0
  31. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/cli/results.py +0 -0
  32. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/commits/__init__.py +0 -0
  33. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/commits/git.py +0 -0
  34. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/commits/parse.py +0 -0
  35. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/config/__init__.py +0 -0
  36. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/config/components.py +0 -0
  37. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/config/sources.py +0 -0
  38. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/__init__.py +0 -0
  39. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/_common.py +0 -0
  40. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/cargo.py +0 -0
  41. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/context.py +0 -0
  42. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/debian.py +0 -0
  43. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/go.py +0 -0
  44. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/gradle.py +0 -0
  45. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/helm.py +0 -0
  46. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/node.py +0 -0
  47. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/python.py +0 -0
  48. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/registry.py +0 -0
  49. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/discovery/relations.py +0 -0
  50. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/__init__.py +0 -0
  51. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/_base.py +0 -0
  52. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/_common.py +0 -0
  53. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/json.py +0 -0
  54. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/plain.py +0 -0
  55. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/properties.py +0 -0
  56. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/regex.py +0 -0
  57. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/toml.py +0 -0
  58. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/formats/yaml.py +0 -0
  59. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/planner/__init__.py +0 -0
  60. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/planner/build.py +0 -0
  61. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/planner/plan.py +0 -0
  62. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/planner/reasons.py +0 -0
  63. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/state/__init__.py +0 -0
  64. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/__init__.py +0 -0
  65. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/_base.py +0 -0
  66. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/_cycle.py +0 -0
  67. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/_git.py +0 -0
  68. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/bump_files.py +0 -0
  69. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/changelog.py +0 -0
  70. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/cycles.py +0 -0
  71. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/mirrors.py +0 -0
  72. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/overlap.py +0 -0
  73. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/state.py +0 -0
  74. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/validation/versions.py +0 -0
  75. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/writers/__init__.py +0 -0
  76. {multicz-1.0.0 → multicz-1.2.0}/src/multicz/writers/_base.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: multicz
3
- Version: 1.0.0
3
+ Version: 1.2.0
4
4
  Summary: Multi-component versioning for monorepos: bump apps, charts, and images independently from conventional commits.
5
5
  Keywords: semver,monorepo,helm,conventional-commits,release,versioning
6
6
  Author: Chris
@@ -3,7 +3,7 @@
3
3
 
4
4
  [project]
5
5
  name = "multicz"
6
- version = "1.0.0"
6
+ version = "1.2.0"
7
7
  description = "Multi-component versioning for monorepos: bump apps, charts, and images independently from conventional commits."
8
8
  readme = "README.md"
9
9
  authors = [
@@ -14,10 +14,10 @@ markers).
14
14
 
15
15
  from __future__ import annotations
16
16
 
17
- from collections.abc import Iterable, Sequence
17
+ from collections.abc import Iterable, Mapping, Sequence
18
18
  from dataclasses import dataclass, field
19
19
 
20
- from ..commits import Commit
20
+ from ..commits import BumpRule, Commit
21
21
  from ..config import ChangelogSection, _default_changelog_sections
22
22
 
23
23
 
@@ -51,6 +51,7 @@ def bucket_commits(
51
51
  commits: Iterable[Commit],
52
52
  *,
53
53
  sections: Sequence[ChangelogSection] | None = None,
54
+ bump_rules: Mapping[str, BumpRule] | None = None,
54
55
  breaking_title: str = "Breaking changes",
55
56
  other_title: str = "",
56
57
  ) -> BucketedCommits:
@@ -61,6 +62,13 @@ def bucket_commits(
61
62
  breaking bucket (breaking commits then fall through to whichever
62
63
  section claims their type), ``other_title=""`` drops unmatched
63
64
  commits entirely.
65
+
66
+ ``bump_rules`` (if passed) bridges the bump configuration with the
67
+ rendered sections. Any commit type with a non-``"none"`` rule is
68
+ considered "interesting": if no explicit section claims it, an
69
+ auto-bucket titled ``type.title()`` is created (e.g. ``docs:`` →
70
+ ``"Docs"`` section). Auto-buckets render *after* the explicitly
71
+ declared sections, in alphabetical order for determinism.
64
72
  """
65
73
  sections = list(sections) if sections is not None else _default_changelog_sections()
66
74
  relevant = [c for c in commits if c.is_conventional]
@@ -80,12 +88,31 @@ def bucket_commits(
80
88
  if items:
81
89
  by_section[section.title] = items
82
90
 
91
+ section_claimed = {t.lower() for s in sections for t in s.types}
92
+
93
+ # Auto-buckets for types declared in bump_rules but not claimed by
94
+ # any explicit section. Sorted for stable output.
95
+ rule_claimed: set[str] = set()
96
+ if bump_rules:
97
+ rule_claimed = {
98
+ t.lower()
99
+ for t, kind in bump_rules.items()
100
+ if kind != "none"
101
+ }
102
+ for type_name in sorted(rule_claimed - section_claimed):
103
+ items = [
104
+ c for c in relevant
105
+ if id(c) not in breaking_set and c.type.lower() == type_name
106
+ ]
107
+ if items:
108
+ by_section[type_name.title()] = items
109
+
83
110
  leftovers: list[Commit] = []
84
111
  if other_title:
85
- claimed = {t.lower() for s in sections for t in s.types}
112
+ all_claimed = section_claimed | rule_claimed
86
113
  leftovers = [
87
114
  c for c in relevant
88
- if id(c) not in breaking_set and c.type.lower() not in claimed
115
+ if id(c) not in breaking_set and c.type.lower() not in all_claimed
89
116
  ]
90
117
 
91
118
  return BucketedCommits(
@@ -99,6 +126,7 @@ def filter_commits(
99
126
  commits: Iterable[Commit],
100
127
  *,
101
128
  sections: Sequence[ChangelogSection] | None = None,
129
+ bump_rules: Mapping[str, BumpRule] | None = None,
102
130
  breaking_title: str = "Breaking changes",
103
131
  other_title: str = "",
104
132
  ) -> list[Commit]:
@@ -108,9 +136,17 @@ def filter_commits(
108
136
  list bullets in original commit order). Equivalent to "any commit
109
137
  that ``bucket_commits`` would have placed somewhere", expressed as
110
138
  a single list.
139
+
140
+ Same ``bump_rules`` semantics as :func:`bucket_commits`: a type
141
+ with a non-``"none"`` rule is kept even when no explicit section
142
+ claims it.
111
143
  """
112
144
  sections = list(sections) if sections is not None else _default_changelog_sections()
113
145
  section_types = {t.lower() for s in sections for t in s.types}
146
+ rule_types = {
147
+ t.lower() for t, kind in (bump_rules or {}).items() if kind != "none"
148
+ }
149
+ keep = section_types | rule_types
114
150
  keep_breaking = bool(breaking_title)
115
151
  keep_leftovers = bool(other_title)
116
152
  return [
@@ -118,7 +154,7 @@ def filter_commits(
118
154
  if c.is_conventional
119
155
  and (
120
156
  (keep_breaking and c.breaking)
121
- or c.type.lower() in section_types
157
+ or c.type.lower() in keep
122
158
  or keep_leftovers
123
159
  )
124
160
  ]
@@ -27,12 +27,12 @@ configured Debian revision when writing a new stanza.
27
27
  from __future__ import annotations
28
28
 
29
29
  import re
30
- from collections.abc import Iterable, Sequence
30
+ from collections.abc import Iterable, Mapping, Sequence
31
31
  from dataclasses import dataclass
32
32
  from datetime import UTC, datetime
33
33
  from email.utils import format_datetime
34
34
 
35
- from ..commits import Commit
35
+ from ..commits import BumpRule, Commit
36
36
  from ..config import ChangelogSection
37
37
  from .bucket import filter_commits
38
38
 
@@ -195,6 +195,7 @@ def render_stanza(
195
195
  maintainer: str = "Unknown <unknown@example.com>",
196
196
  when: datetime | None = None,
197
197
  sections: Sequence[ChangelogSection] | None = None,
198
+ bump_rules: Mapping[str, BumpRule] | None = None,
198
199
  breaking_title: str = "Breaking changes",
199
200
  other_title: str = "",
200
201
  ) -> str:
@@ -222,6 +223,7 @@ def render_stanza(
222
223
  filtered = filter_commits(
223
224
  commits,
224
225
  sections=sections,
226
+ bump_rules=bump_rules,
225
227
  breaking_title=breaking_title,
226
228
  other_title=other_title,
227
229
  )
@@ -31,12 +31,12 @@ older release section.
31
31
  from __future__ import annotations
32
32
 
33
33
  import re
34
- from collections.abc import Iterable, Sequence
34
+ from collections.abc import Iterable, Mapping, Sequence
35
35
  from dataclasses import dataclass
36
36
  from datetime import date
37
37
  from pathlib import Path
38
38
 
39
- from ..commits import Commit
39
+ from ..commits import BumpRule, Commit
40
40
  from ..config import ChangelogSection, _default_changelog_sections
41
41
  from .bucket import bucket_commits
42
42
 
@@ -75,6 +75,7 @@ def render_body(
75
75
  commits: Iterable[Commit],
76
76
  *,
77
77
  sections: Sequence[ChangelogSection] | None = None,
78
+ bump_rules: Mapping[str, BumpRule] | None = None,
78
79
  breaking_title: str = "Breaking changes",
79
80
  other_title: str = "",
80
81
  cascades: Sequence[CascadeEntry] | None = None,
@@ -87,6 +88,11 @@ def render_body(
87
88
  commits then fall through to whichever section claims their type).
88
89
  Empty string ``other_title`` drops unmatched conventional commits.
89
90
 
91
+ ``bump_rules`` (when passed) ties the rendered sections to the bump
92
+ configuration: a type with a non-``"none"`` rule is included even
93
+ if no explicit section claims it (auto-bucketed under
94
+ ``type.title()``). See :func:`bucket_commits`.
95
+
90
96
  ``cascades`` lists upstream bumps that pulled this component along
91
97
  (mirror writes, trigger edges). When present and ``cascade_title``
92
98
  is non-empty, they render as a dedicated H3 section; this also
@@ -97,6 +103,7 @@ def render_body(
97
103
  bucketed = bucket_commits(
98
104
  commits,
99
105
  sections=sections,
106
+ bump_rules=bump_rules,
100
107
  breaking_title=breaking_title,
101
108
  other_title=other_title,
102
109
  )
@@ -173,6 +180,7 @@ def render_section(
173
180
  *,
174
181
  today: date | None = None,
175
182
  sections: Sequence[ChangelogSection] | None = None,
183
+ bump_rules: Mapping[str, BumpRule] | None = None,
176
184
  breaking_title: str = "Breaking changes",
177
185
  other_title: str = "",
178
186
  cascades: Sequence[CascadeEntry] | None = None,
@@ -184,6 +192,7 @@ def render_section(
184
192
  body = render_body(
185
193
  commits,
186
194
  sections=sections,
195
+ bump_rules=bump_rules,
187
196
  breaking_title=breaking_title,
188
197
  other_title=other_title,
189
198
  cascades=cascades,
@@ -236,6 +245,7 @@ def update_changelog_file(
236
245
  *,
237
246
  today: date | None = None,
238
247
  sections: Sequence[ChangelogSection] | None = None,
248
+ bump_rules: Mapping[str, BumpRule] | None = None,
239
249
  breaking_title: str = "Breaking changes",
240
250
  other_title: str = "",
241
251
  drop_prereleases: bool = False,
@@ -254,6 +264,7 @@ def update_changelog_file(
254
264
  commits,
255
265
  today=today,
256
266
  sections=sections,
267
+ bump_rules=bump_rules,
257
268
  breaking_title=breaking_title,
258
269
  other_title=other_title,
259
270
  cascades=cascades,
@@ -274,6 +274,7 @@ def bump(
274
274
  new_version,
275
275
  relevant,
276
276
  sections=config.project.changelog_sections,
277
+ bump_rules=config.bump_rules_for(planned.component),
277
278
  breaking_title=config.project.breaking_section_title,
278
279
  other_title=config.project.other_section_title,
279
280
  drop_prereleases=is_final and strategy == "promote",
@@ -449,6 +449,7 @@ def render_release_notes(
449
449
  body = render_body(
450
450
  list(s.commits),
451
451
  sections=config.project.changelog_sections,
452
+ bump_rules=config.bump_rules_for(s.component),
452
453
  breaking_title=config.project.breaking_section_title,
453
454
  other_title=config.project.other_section_title,
454
455
  cascades=list(s.cascades) if s.cascades else None,
@@ -490,6 +491,7 @@ def render_changelog(
490
491
  body = render_body(
491
492
  list(entry.relevant),
492
493
  sections=config.project.changelog_sections,
494
+ bump_rules=config.bump_rules_for(entry.component),
493
495
  breaking_title=config.project.breaking_section_title,
494
496
  other_title=config.project.other_section_title,
495
497
  )
@@ -115,6 +115,14 @@ class DebianChangelogWriter(BaseModel):
115
115
  maintainer: str | None = None # falls back to debian/control then git config
116
116
  debian_revision: int = 1
117
117
  epoch: int | None = None
118
+ # Debian package name written into each new stanza header. Defaults
119
+ # to the multicz component name when unset. Override when the
120
+ # component name (typically short - "api", "worker") differs from
121
+ # the Debian binary package name (typically prefixed -
122
+ # "shomer-api"); ``dpkg-source`` errors out otherwise because the
123
+ # changelog's package name must match ``debian/control``'s
124
+ # ``Source:`` field.
125
+ package: str | None = None
118
126
 
119
127
 
120
128
  # Discriminated union of all writer types. Add new writer schemas here
@@ -67,13 +67,14 @@ class DebianChangelogImpl:
67
67
  )
68
68
  maintainer = _resolve_maintainer(ctx.repo, writer.maintainer)
69
69
  stanza = render_stanza(
70
- package=ctx.component_name,
70
+ package=writer.package or ctx.component_name,
71
71
  version=debian_version,
72
72
  distribution=writer.distribution,
73
73
  urgency=writer.urgency,
74
74
  commits=ctx.relevant_commits,
75
75
  maintainer=maintainer,
76
76
  sections=config.project.changelog_sections,
77
+ bump_rules=config.bump_rules_for(ctx.component_name),
77
78
  breaking_title=config.project.breaking_section_title,
78
79
  other_title=config.project.other_section_title,
79
80
  )
File without changes
File without changes
File without changes