Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature: Add versioning support for images with prerelease identifiers #408

Merged
merged 1 commit into from
May 9, 2024
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
37 changes: 23 additions & 14 deletions src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -79,12 +79,12 @@ def _create_new_version_artifacts(args):
raise Exception()

base_patch_version = get_semver(args.base_patch_version)
if base_patch_version.prerelease:
# We don't support creating new patch/major/minor versions from a prerelease version
# Re-run the build command for the prerelease version again to pick the latest versions
# of the marquee packages
if base_patch_version.prerelease and args.pre_release_identifier:
aws-tianquaw marked this conversation as resolved.
Show resolved Hide resolved
# We support creating new patch/major/minor versions from a pre-release version.
# But We don't support passing the pre_release_identifier parameter while creating a new
# patch/major/minor versions from the pre-release version.
raise Exception()
next_version = getattr(base_patch_version, runtime_version_upgrade_func)()
next_version = _get_next_version(base_patch_version, runtime_version_upgrade_func)

if args.pre_release_identifier:
next_version = next_version.replace(prerelease=args.pre_release_identifier)
Expand Down Expand Up @@ -291,32 +291,41 @@ def _build_local_images(
return generated_image_ids, generated_image_versions


def _get_next_version(current_version: Version, upgrade_func: str) -> Version:
next_version = getattr(current_version, upgrade_func)()
if current_version.prerelease:
# Semver Ignores prerelease identifier when we do bump_{patch/minor/major}
next_version = next_version.replace(prerelease=current_version.prerelease)
return next_version


# At some point of time, let's say some patch versions exist for both 2.6 and 2.7, and we create new patch
# versions for both of them. Now, for the new 2.6.x, we can tag it as '2.6.x-cpu' and '2.6-cpu' but NOT '2-cpu' because
# there is a more recent version (i.e. 2.7.x) that should be considered '2-cpu'. So, given a patch version, the
# following function returns a list of versions for which the current patch version is latest for.
# For versions with pre-release identifier, this method will return the appropriate tags
# Example: For an version 2.0.0-beta, this method will return [2.0.0-beta, 2.0-beta, 2-beta,
# latest-beta]
def _get_version_tags(target_version: Version, env_out_file_name: str) -> list[str]:
# First, add '2.6.x' as is.
res = [str(target_version)]
# If this is a pre-release version, then don't add additional tags
if target_version.prerelease:
return res
prerelease_version_suffix = f"-{target_version.prerelease}" if target_version.prerelease else ""

# If we were to add '2.6', check if '2.6.(x+1)' is present.
if not is_exists_dir_for_version(target_version.bump_patch(), env_out_file_name):
res.append(f"{target_version.major}.{target_version.minor}")
if not is_exists_dir_for_version(_get_next_version(target_version, "bump_patch"), env_out_file_name):
res.append(f"{target_version.major}.{target_version.minor}{prerelease_version_suffix}")
else:
return res

# If we were to add '2', check if '2.7' is present.
if not is_exists_dir_for_version(target_version.bump_minor(), env_out_file_name):
res.append(str(target_version.major))
if not is_exists_dir_for_version(_get_next_version(target_version, "bump_minor"), env_out_file_name):
res.append(f"{target_version.major}{prerelease_version_suffix}")
else:
return res

# If we were to add 'latest', check if '3.0.0' is present.
if not is_exists_dir_for_version(target_version.bump_major(), env_out_file_name):
res.append("latest")
if not is_exists_dir_for_version(_get_next_version(target_version, "bump_major"), env_out_file_name):
res.append(f"latest{prerelease_version_suffix}")

return res

Expand Down
40 changes: 35 additions & 5 deletions test/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -169,13 +169,13 @@ def test_get_semver_version():

def test_new_version_artifacts_for_an_input_prerelease_version():
input_version = "1.23.0-beta"
args = CreateVersionArgs("patch", input_version)
args = CreateVersionArgs("patch", input_version, pre_release_identifier="new-beta")
with pytest.raises(Exception):
create_patch_version_artifacts(args)
args = CreateVersionArgs("minor", input_version)
args = CreateVersionArgs("minor", input_version, pre_release_identifier="new-beta")
with pytest.raises(Exception):
create_minor_version_artifacts(args)
args = CreateVersionArgs("major", input_version)
args = CreateVersionArgs("major", input_version, pre_release_identifier="new-beta")
with pytest.raises(Exception):
create_major_version_artifacts(args)

Expand Down Expand Up @@ -528,8 +528,38 @@ def test_get_version_tags(mock_path_exists):
# case 4.2 The patch version is not a prerelease version
mock_path_exists.side_effect = [True, True]
assert _get_version_tags(version, file_name) == ["1.124.5"]
# case 5: The given version includes a prerelease identifier
assert _get_version_tags(get_semver("1.124.5-beta"), file_name) == ["1.124.5-beta"]


@patch("os.path.exists")
def test_get_version_tags_with_prerelease_identifier(mock_path_exists):
version = get_semver("1.124.5-beta")
file_name = "cpu.env.out"
# case 1: The given version is the latest for patch, minor and major
mock_path_exists.side_effect = [False, False, False]
assert _get_version_tags(version, file_name) == ["1.124.5-beta", "1.124-beta", "1-beta", "latest-beta"]
# case 2: The given version is the latest for patch, minor but not major
# case 2.1 The major version is a different prerelease version
mock_path_exists.side_effect = [False, False, True, False]
assert _get_version_tags(version, file_name) == ["1.124.5-beta", "1.124-beta", "1-beta", "latest-beta"]
# case 2.2 The major version is not a prerelease version
mock_path_exists.side_effect = [False, False, True, True]
assert _get_version_tags(version, file_name) == ["1.124.5-beta", "1.124-beta", "1-beta"]
# case 3: The given version is the latest for patch and major but not for minor
# case 3.1 The minor version is a prerelease version (we need to mock path.exists for major
# version twice - one for the actual directory, one for the docker file)
mock_path_exists.side_effect = [False, True, False, True, True]
assert _get_version_tags(version, file_name) == ["1.124.5-beta", "1.124-beta", "1-beta"]
# case 3.2 The minor version is not a prerelease version
mock_path_exists.side_effect = [False, True, True]
assert _get_version_tags(version, file_name) == ["1.124.5-beta", "1.124-beta"]
# case 4: The given version is not the latest for patch, minor, major
# case 4.1 The patch version is a prerelease version (we need to mock path.exists for minor
# and major twice - one for the actual directory, one for the docker file)
mock_path_exists.side_effect = [True, False, True, True, True, True]
assert _get_version_tags(version, file_name) == ["1.124.5-beta", "1.124-beta"]
# case 4.2 The patch version is not a prerelease version
mock_path_exists.side_effect = [True, True]
assert _get_version_tags(version, file_name) == ["1.124.5-beta"]


def _test_push_images_upstream(mocker, repository):
Expand Down