Skip to content

Commit 8edcc2b

Browse files
Merge pull request #23525 from nrainer-materialize/ci/override-comparison-target
ci: provide mechanism to override ancestor release
2 parents d9f27b5 + edf444d commit 8edcc2b

File tree

3 files changed

+133
-51
lines changed

3 files changed

+133
-51
lines changed

misc/python/materialize/docker.py

Lines changed: 113 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -13,6 +13,17 @@
1313
from materialize import buildkite, git
1414
from materialize.mz_version import MzVersion
1515

16+
"""
17+
Git revisions that are based on commits listed as keys require at least the version specified in the value.
18+
Note that specified versions do not necessarily need to be already published.
19+
Commits must be ordered descending by their date.
20+
"""
21+
MIN_ANCESTOR_MZ_VERSION_PER_COMMIT: dict[str, MzVersion] = {
22+
# insert newer commits at the top
23+
# PR#23421 (coord: smorgasbord of improvements for the crdb-backed timestamp oracle) introduces regressions against 0.78.13
24+
"5179ebd39aea4867622357a832aaddcde951b411": MzVersion.parse_mz("v0.79.0")
25+
}
26+
1627

1728
def resolve_ancestor_image_tag() -> str:
1829
image_tag, context = _resolve_ancestor_image_tag()
@@ -22,71 +33,99 @@ def resolve_ancestor_image_tag() -> str:
2233

2334
def _resolve_ancestor_image_tag() -> tuple[str, str]:
2435
if buildkite.is_in_buildkite():
25-
if buildkite.is_in_pull_request():
26-
# return the merge base
27-
common_ancestor_commit = buildkite.get_merge_base()
28-
if _image_of_commit_exists(common_ancestor_commit):
29-
return (
30-
_commit_to_image_tag(common_ancestor_commit),
31-
"merge base of pull request",
32-
)
33-
else:
34-
return (
35-
_version_to_image_tag(get_latest_published_version()),
36-
"latest release because image of merge base of pull request not available",
37-
)
38-
elif git.is_on_release_version():
39-
# return the previous release
40-
tagged_release_version = git.get_tagged_release_version(
41-
version_type=MzVersion
42-
)
43-
assert tagged_release_version is not None
44-
previous_release_version = get_previous_published_version(
45-
tagged_release_version
46-
)
36+
return _resolve_ancestor_image_tag_when_in_buildkite()
37+
else:
38+
return _resolve_ancestor_image_tag_when_running_locally()
39+
40+
41+
def _resolve_ancestor_image_tag_when_in_buildkite() -> tuple[str, str]:
42+
if buildkite.is_in_pull_request():
43+
# return the merge base
44+
common_ancestor_commit = buildkite.get_merge_base()
45+
if _image_of_commit_exists(common_ancestor_commit):
4746
return (
48-
_version_to_image_tag(previous_release_version),
49-
f"previous release because on release branch {tagged_release_version}",
47+
_commit_to_image_tag(common_ancestor_commit),
48+
"merge base of pull request",
5049
)
5150
else:
52-
# return the latest release
5351
return (
5452
_version_to_image_tag(get_latest_published_version()),
55-
"latest release because not in a pull request and not on a release branch",
53+
"latest release because image of merge base of pull request not available",
5654
)
55+
elif git.is_on_release_version():
56+
# return the previous release
57+
tagged_release_version = git.get_tagged_release_version(version_type=MzVersion)
58+
assert tagged_release_version is not None
59+
previous_release_version = get_previous_published_version(
60+
tagged_release_version
61+
)
62+
return (
63+
_version_to_image_tag(previous_release_version),
64+
f"previous release because on release branch {tagged_release_version}",
65+
)
5766
else:
58-
if git.is_on_release_version():
59-
# return the previous release
60-
tagged_release_version = git.get_tagged_release_version(
61-
version_type=MzVersion
67+
latest_published_version = get_latest_published_version()
68+
override_commit = _get_override_commit_instead_of_version(
69+
latest_published_version
70+
)
71+
72+
if override_commit is not None:
73+
# use the commit instead of the latest release
74+
return (
75+
_commit_to_image_tag(override_commit),
76+
f"commit override instead of latest release ({latest_published_version})",
6277
)
63-
assert tagged_release_version is not None
64-
previous_release_version = get_previous_published_version(
65-
tagged_release_version
78+
79+
# return the latest release
80+
return (
81+
_version_to_image_tag(latest_published_version),
82+
"latest release because not in a pull request and not on a release branch",
83+
)
84+
85+
86+
def _resolve_ancestor_image_tag_when_running_locally() -> tuple[str, str]:
87+
if git.is_on_release_version():
88+
# return the previous release
89+
tagged_release_version = git.get_tagged_release_version(version_type=MzVersion)
90+
assert tagged_release_version is not None
91+
previous_release_version = get_previous_published_version(
92+
tagged_release_version
93+
)
94+
return (
95+
_version_to_image_tag(previous_release_version),
96+
f"previous release because on local release branch {tagged_release_version}",
97+
)
98+
elif git.is_on_main_branch():
99+
# return the latest release
100+
latest_published_version = get_latest_published_version()
101+
override_commit = _get_override_commit_instead_of_version(
102+
latest_published_version
103+
)
104+
105+
if override_commit is not None:
106+
# use the commit instead of the latest release
107+
return (
108+
_commit_to_image_tag(override_commit),
109+
f"commit override instead of latest release ({latest_published_version})",
66110
)
111+
112+
return (
113+
_version_to_image_tag(latest_published_version),
114+
"latest release because on local main branch",
115+
)
116+
else:
117+
# return the merge base
118+
common_ancestor_commit = buildkite.get_merge_base()
119+
if _image_of_commit_exists(common_ancestor_commit):
67120
return (
68-
_version_to_image_tag(previous_release_version),
69-
f"previous release because on local release branch {tagged_release_version}",
121+
_commit_to_image_tag(common_ancestor_commit),
122+
"merge base of local non-main branch",
70123
)
71-
elif git.is_on_main_branch():
72-
# return the latest release
124+
else:
73125
return (
74126
_version_to_image_tag(get_latest_published_version()),
75-
"latest release because on local main branch",
127+
"latest release because image of merge base of local non-main branch not available",
76128
)
77-
else:
78-
# return the merge base
79-
common_ancestor_commit = buildkite.get_merge_base()
80-
if _image_of_commit_exists(common_ancestor_commit):
81-
return (
82-
_commit_to_image_tag(common_ancestor_commit),
83-
"merge base of local non-main branch",
84-
)
85-
else:
86-
return (
87-
_version_to_image_tag(get_latest_published_version()),
88-
"latest release because image of merge base of local non-main branch not available",
89-
)
90129

91130

92131
def get_latest_published_version() -> MzVersion:
@@ -121,6 +160,29 @@ def get_previous_published_version(release_version: MzVersion) -> MzVersion:
121160
excluded_versions.add(previous_published_version)
122161

123162

163+
def _get_override_commit_instead_of_version(
164+
latest_published_version: MzVersion,
165+
) -> str | None:
166+
"""
167+
If a commit specifies a mz version as prerequisite (to avoid regressions) that is newer than the
168+
provided latest version (i.e., prerequisite not satisfied by the latest version), then return
169+
that commit's hash if the commit contained in the current state.
170+
Otherwise, return none.
171+
"""
172+
for (
173+
commit_hash,
174+
min_required_mz_version,
175+
) in MIN_ANCESTOR_MZ_VERSION_PER_COMMIT.items():
176+
if latest_published_version >= min_required_mz_version:
177+
continue
178+
179+
if git.contains_commit(commit_hash):
180+
# commit would require at least min_required_mz_version
181+
return commit_hash
182+
183+
return None
184+
185+
124186
def _image_of_release_version_exists(version: MzVersion) -> bool:
125187
return _mz_image_tag_exists(_version_to_image_tag(version))
126188

misc/python/materialize/git.py

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -278,6 +278,12 @@ def is_on_main_branch() -> bool:
278278
return get_branch_name() == "main"
279279

280280

281+
def contains_commit(commit_sha: str, target: str = "HEAD") -> bool:
282+
command = ["git", "merge-base", "--is-ancestor", commit_sha, target]
283+
return_code = spawn.run_and_get_return_code(command)
284+
return return_code == 0
285+
286+
281287
def get_tagged_release_version(version_type: type[VERSION_TYPE]) -> VERSION_TYPE | None:
282288
"""
283289
This returns the release version if exactly this commit is tagged.

misc/python/materialize/spawn.py

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -125,3 +125,17 @@ def capture(
125125
return subprocess.check_output(
126126
args, cwd=cwd, env=env, input=input, stdin=stdin, stderr=stderr, text=True
127127
)
128+
129+
130+
def run_and_get_return_code(
131+
args: Sequence[Path | str],
132+
*,
133+
cwd: Path | None = None,
134+
env: dict[str, str] | None = None,
135+
) -> int:
136+
"""Run a subprocess and return the return code."""
137+
try:
138+
capture(args, cwd=cwd, env=env, stderr=subprocess.DEVNULL)
139+
return 0
140+
except CalledProcessError as e:
141+
return e.returncode

0 commit comments

Comments
 (0)