Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
48 changes: 47 additions & 1 deletion ci/jobs/scripts/clickhouse_version.py
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import time
from pathlib import Path

from ci.praktika.info import Info
Expand Down Expand Up @@ -77,13 +78,58 @@ def _tweak_from_git(githash) -> int:
)


def release_tags(version: "CHVersion", retries=3) -> dict:
"""Remote tags matching `v<version.major>.<version.minor>.<version.patch>.*`, mapped
to commit SHA.

Raises if `git ls-remote` still fails after retries. An empty dict means
there are no releases yet, so a failed lookup must not look like one."""
command = (
f"git ls-remote --tags origin "
f"'refs/tags/v{version.major}.{version.minor}.{version.patch}.*'"
)
for attempt in range(retries):
code, stdout, stderr = Shell.get_res_stdout_stderr(command)
if code == 0:
break
if attempt < retries - 1:
time.sleep(2**attempt)
else:
raise RuntimeError(f"[{command}] failed: {stderr}")
tags = {}
for line in stdout.splitlines():
sha, ref = line.split()
# Annotated tags also list the commit as `<tag>^{}`.
tag = ref.removeprefix("refs/tags/").removesuffix("^{}")
tags[tag] = sha
return tags


def next_release_tweak(
tags: dict, version: "CHVersion", track: int, current_sha: str
) -> int:
"""Next tweak on this track: `track`xxxx, one past the highest existing
counter. Tags on `current_sha` are skipped so a tag push keeps its own number.
"""
prefix = f"v{version.major}.{version.minor}.{version.patch}."
last_tweak = track * 10000
for tag, sha in tags.items():
if sha == current_sha or not tag.startswith(prefix):
continue
tweak = tag.split(".")[3]
if tweak.isdigit() and int(tweak) // 10000 == track:
last_tweak = max(last_tweak, int(tweak))
return last_tweak + 1


def _version_type_from_describe(versions: dict) -> str:
"""Recover the version type from the file's `VERSION_DESCRIBE` suffix, which
is `v<string>-<version_type>` upstream and `v<string>` for Altinity
flavoured versions."""
describe = str(versions.get("describe", ""))
flavour = str(versions.get("flavour") or "")
if flavour and describe == f"v{versions.get('string', '')}":
# NOTE (strtgbb): VERSION_DESCRIBE is left as is to avoid merge conflicts
if flavour:
return flavour
return describe.rsplit("-", 1)[1] if "-" in describe else ""

Expand Down
55 changes: 47 additions & 8 deletions ci/jobs/scripts/workflow_hooks/version_log.py
Original file line number Diff line number Diff line change
@@ -1,26 +1,65 @@
import os
from datetime import datetime

from praktika.info import Info
from praktika.runtime import RunConfig
from praktika.utils import Shell

from ci.jobs.scripts.cidb_cluster import CIDBCluster
from ci.jobs.scripts.clickhouse_version import CHVersion
from ci.jobs.scripts.clickhouse_version import (
CHVersion,
next_release_tweak,
release_tags,
)


def _track_from_branch(branch):
"""Track and pre-release flavour from a release-branch name, or None.

PRs use the base branch; pushes use the branch. Prefixes `fips-`,
`antalya-`, and `stable-` select the track, in that order. Anything else
keeps the file values."""
if not branch:
return None
if branch.startswith("fips-"):
return 3, "altinityfipstest"
if branch.startswith("antalya-"):
return 2, "antalyatest"
if branch.startswith("stable-"):
return 1, "altinitytest"
return None


def _build_version(info):
"""The build version recorded for this run.

In a PR an unflavoured version's tweak is pinned to 1. `HEAD` is the
ephemeral merge commit, whose first-parent commit count diverges across
close/reopen and re-runs of the same PR as `master` advances. Artifacts are
keyed by the head SHA, so an unpinned tweak would store diverging version
strings under one artifact prefix. Flavoured Altinity versions keep the
explicit tweak committed in the version file."""
Unflavoured PR builds pin the tweak to 1. `HEAD` is the merge commit, and
its first-parent count changes as `master` moves, so an unpinned tweak
would store different version strings under one artifact prefix.

Flavoured builds take flavour and tweak from the tag on a tag push, or
from the branch and existing release tags when the branch names a track.
Otherwise the committed file values are used."""
version = CHVersion.get_current_version(no_strict=True)
if info.pr_number != 0 and not version.flavour:
version = version.with_tweak(1)
return version
if not version.flavour:
return version
# NOTE (strtgbb): tag pushes take tweak and flavour from the tag.
# --check-tag validates the counter. Other runs use the branch.
if os.getenv("GITHUB_REF_TYPE") == "tag":
*_, tweak, flavour = os.getenv("GITHUB_REF_NAME", "").split(".")
version.flavour = flavour
return version.with_tweak(int(tweak)).with_description(flavour)
branch = info.base_branch if info.pr_number != 0 else info.git_branch
derived = _track_from_branch(branch)
if not derived:
return version
track, flavour = derived
tags = release_tags(version)
tweak = next_release_tweak(tags, version, track, info.sha)
version.flavour = flavour
return version.with_tweak(tweak).with_description(flavour)


def _add_build_to_version_history():
Expand Down
Loading
Loading