multicz 0.2.1__tar.gz → 0.3.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.
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: multicz
3
- Version: 0.2.1
3
+ Version: 0.3.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
@@ -1,6 +1,6 @@
1
1
  [project]
2
2
  name = "multicz"
3
- version = "0.2.1"
3
+ version = "0.3.0"
4
4
  description = "Multi-component versioning for monorepos: bump apps, charts, and images independently from conventional commits."
5
5
  readme = "README.md"
6
6
  authors = [
@@ -0,0 +1,3 @@
1
+ from .cli import app
2
+
3
+ app()
@@ -29,12 +29,24 @@ from __future__ import annotations
29
29
 
30
30
  import re
31
31
  from collections.abc import Iterable, Sequence
32
+ from dataclasses import dataclass
32
33
  from datetime import date
33
34
  from pathlib import Path
34
35
 
35
36
  from .commits import Commit
36
37
  from .config import ChangelogSection, _default_changelog_sections
37
38
 
39
+
40
+ @dataclass(frozen=True)
41
+ class CascadeEntry:
42
+ """A non-commit reason for a bump (mirror or trigger), surfaced in
43
+ the changelog so cascade-only releases describe what made them
44
+ happen instead of rendering ``_No notable changes._``.
45
+ """
46
+
47
+ upstream: str
48
+ upstream_version: str
49
+
38
50
  _PREAMBLE = (
39
51
  "# Changelog\n"
40
52
  "\n"
@@ -49,16 +61,34 @@ def render_body(
49
61
  sections: Sequence[ChangelogSection] | None = None,
50
62
  breaking_title: str = "Breaking changes",
51
63
  other_title: str = "",
64
+ cascades: Sequence[CascadeEntry] | None = None,
65
+ cascade_title: str = "Dependencies",
66
+ cascade_format: str = "Track `{upstream}` `{upstream_version}`",
52
67
  ) -> str:
53
68
  """Render the section bodies (no leading H2).
54
69
 
55
70
  Empty string ``breaking_title`` disables the breaking bucket (breaking
56
71
  commits then fall through to whichever section claims their type).
57
72
  Empty string ``other_title`` drops unmatched conventional commits.
73
+
74
+ ``cascades`` lists upstream bumps that pulled this component along
75
+ (mirror writes, trigger edges). When present and ``cascade_title``
76
+ is non-empty, they render as a dedicated H3 section; this also
77
+ suppresses the ``_No notable changes._`` placeholder when no
78
+ commits otherwise apply.
58
79
  """
59
80
  sections = list(sections) if sections is not None else _default_changelog_sections()
60
81
  relevant = [c for c in commits if c.is_conventional]
61
- if not relevant:
82
+ cascade_lines: list[str] = []
83
+ if cascades and cascade_title:
84
+ for entry in cascades:
85
+ cascade_lines.append(
86
+ cascade_format.format(
87
+ upstream=entry.upstream,
88
+ upstream_version=entry.upstream_version,
89
+ )
90
+ )
91
+ if not relevant and not cascade_lines:
62
92
  return "_No notable changes._\n"
63
93
 
64
94
  breaking: list[Commit] = []
@@ -94,7 +124,7 @@ def render_body(
94
124
  if other_title and other_title in buckets:
95
125
  ordered.append((other_title, buckets[other_title]))
96
126
 
97
- if not ordered:
127
+ if not ordered and not cascade_lines:
98
128
  return "_No notable changes._\n"
99
129
 
100
130
  lines: list[str] = []
@@ -105,6 +135,12 @@ def render_body(
105
135
  scope = f"**{commit.scope}**: " if commit.scope else ""
106
136
  lines.append(f"- {scope}{commit.subject} (`{commit.sha[:7]}`)")
107
137
  lines.append("")
138
+ if cascade_lines:
139
+ lines.append(f"### {cascade_title}")
140
+ lines.append("")
141
+ for entry in cascade_lines:
142
+ lines.append(f"- {entry}")
143
+ lines.append("")
108
144
  return "\n".join(lines)
109
145
 
110
146
 
@@ -116,6 +152,9 @@ def render_section(
116
152
  sections: Sequence[ChangelogSection] | None = None,
117
153
  breaking_title: str = "Breaking changes",
118
154
  other_title: str = "",
155
+ cascades: Sequence[CascadeEntry] | None = None,
156
+ cascade_title: str = "Dependencies",
157
+ cascade_format: str = "Track `{upstream}` `{upstream_version}`",
119
158
  ) -> str:
120
159
  """Render the markdown for a single release section."""
121
160
  when = (today or date.today()).isoformat()
@@ -124,6 +163,9 @@ def render_section(
124
163
  sections=sections,
125
164
  breaking_title=breaking_title,
126
165
  other_title=other_title,
166
+ cascades=cascades,
167
+ cascade_title=cascade_title,
168
+ cascade_format=cascade_format,
127
169
  )
128
170
  return f"## [{version}] - {when}\n\n" + body
129
171
 
@@ -174,6 +216,9 @@ def update_changelog_file(
174
216
  breaking_title: str = "Breaking changes",
175
217
  other_title: str = "",
176
218
  drop_prereleases: bool = False,
219
+ cascades: Sequence[CascadeEntry] | None = None,
220
+ cascade_title: str = "Dependencies",
221
+ cascade_format: str = "Track `{upstream}` `{upstream_version}`",
177
222
  ) -> None:
178
223
  """Render a new section and merge it into ``path`` (creating the file if needed).
179
224
 
@@ -188,6 +233,9 @@ def update_changelog_file(
188
233
  sections=sections,
189
234
  breaking_title=breaking_title,
190
235
  other_title=other_title,
236
+ cascades=cascades,
237
+ cascade_title=cascade_title,
238
+ cascade_format=cascade_format,
191
239
  )
192
240
  existing = path.read_text(encoding="utf-8") if path.exists() else ""
193
241
  if drop_prereleases:
@@ -14,7 +14,7 @@ from rich.console import Console
14
14
  from rich.table import Table
15
15
 
16
16
  from . import __version__
17
- from .changelog import render_body, update_changelog_file
17
+ from .changelog import CascadeEntry, render_body, update_changelog_file
18
18
  from .commits import (
19
19
  DEFAULT_TYPES,
20
20
  commits_in_range,
@@ -1047,7 +1047,8 @@ def _run_post_bump_hook(repo: Path, command: str) -> None:
1047
1047
  args = shlex.split(command)
1048
1048
  if not args:
1049
1049
  return
1050
- console.print(f" [dim]post_bump:[/] {command}")
1050
+ # stderr, so `multicz bump --output json | jq` stays parseable.
1051
+ err.print(f" [dim]post_bump:[/] {command}")
1051
1052
  result = subprocess.run(
1052
1053
  args, cwd=repo, capture_output=True, text=True
1053
1054
  )
@@ -1375,6 +1376,27 @@ def bump(
1375
1376
  planned.component, config, repo, matcher,
1376
1377
  since_stable=use_stable_since,
1377
1378
  )
1379
+ # Surface mirror/trigger cascades as a Dependencies
1380
+ # section: when a release is purely cascade-driven
1381
+ # (e.g. chart bumps because api updated appVersion),
1382
+ # this is the only thing that explains *why* the
1383
+ # release exists.
1384
+ cascade_entries: list[CascadeEntry] = []
1385
+ seen_upstreams: set[str] = set()
1386
+ for reason in planned.reasons:
1387
+ if isinstance(reason, MirrorReason | TriggerReason):
1388
+ if reason.upstream in seen_upstreams:
1389
+ continue
1390
+ upstream_planned = plan.bumps.get(reason.upstream)
1391
+ if upstream_planned is None:
1392
+ continue
1393
+ cascade_entries.append(
1394
+ CascadeEntry(
1395
+ upstream=reason.upstream,
1396
+ upstream_version=upstream_planned.next,
1397
+ )
1398
+ )
1399
+ seen_upstreams.add(reason.upstream)
1378
1400
  changelog_path = repo / comp.changelog
1379
1401
  update_changelog_file(
1380
1402
  changelog_path,
@@ -1384,6 +1406,9 @@ def bump(
1384
1406
  breaking_title=config.project.breaking_section_title,
1385
1407
  other_title=config.project.other_section_title,
1386
1408
  drop_prereleases=is_final and strategy == "promote",
1409
+ cascades=cascade_entries,
1410
+ cascade_title=config.project.cascade_section_title,
1411
+ cascade_format=config.project.cascade_changelog_format,
1387
1412
  )
1388
1413
  if changelog_path not in written:
1389
1414
  written.append(changelog_path)
@@ -231,6 +231,14 @@ class ProjectSettings(BaseModel):
231
231
  )
232
232
  breaking_section_title: str = "Breaking changes"
233
233
  other_section_title: str = ""
234
+ # Section + line format for cascade-only bumps (mirror or trigger).
235
+ # Replaces the legacy ``_No notable changes._`` placeholder when the
236
+ # only reason for a release is an upstream component bumping. Set
237
+ # ``cascade_section_title = ""`` to disable and fall back to the
238
+ # placeholder. ``cascade_changelog_format`` accepts ``{upstream}`` and
239
+ # ``{upstream_version}`` placeholders.
240
+ cascade_section_title: str = "Dependencies"
241
+ cascade_changelog_format: str = "Track `{upstream}` `{upstream_version}`"
234
242
  finalize_strategy: Literal["consolidate", "promote", "annotate"] = "consolidate"
235
243
  overlap_policy: Literal["error", "first-match", "allow", "all"] = "error"
236
244
  ignored_types: list[str] = Field(default_factory=list)
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes
File without changes