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

Fix version match test #2005

Merged
merged 3 commits into from
Oct 18, 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
2 changes: 1 addition & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ __pycache__
build
dist
*.egg-info
pkg/dockerfile/embed/*.whl
pkg/dockerfile/embed/*
# Used by a vim plugin (projectionist)
.projections.json
.tox/
Expand Down
45 changes: 23 additions & 22 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,8 @@ GORELEASER := $(GO) run github.com/goreleaser/goreleaser/v2@v2.2.0
PYTHON ?= python
TOX := $(PYTHON) -Im tox

# If cog's wheel has been prebuilt, it can be specified with the COG_WHEEL
# environment variable and we will not attempt to build it.
ifndef COG_WHEEL
COG_PYTHON_VERSION := $(shell $(PYTHON) -m setuptools_scm 2>/dev/null)
ifndef COG_PYTHON_VERSION
$(error Could not determine a version for cog! Did you `pip install -e '.[dev]'` first?)
endif
COG_WHEEL := dist/cog-$(COG_PYTHON_VERSION)-py3-none-any.whl
endif

COG_GO_SOURCE := $(shell find cmd pkg -type f)
COG_PYTHON_SOURCE := $(shell find python/cog -type f -name '*.py')
COG_EMBEDDED_WHEEL := pkg/dockerfile/embed/$(notdir $(COG_WHEEL))

COG_BINARIES := cog base-image

Expand All @@ -36,18 +25,30 @@ default: all
all: cog

.PHONY: wheel
wheel: $(COG_EMBEDDED_WHEEL)
wheel: pkg/dockerfile/embed/.wheel

$(COG_EMBEDDED_WHEEL): $(COG_WHEEL)
ifdef COG_WHEEL
pkg/dockerfile/embed/.wheel: $(COG_WHEEL)
@mkdir -p pkg/dockerfile/embed
@rm -f pkg/dockerfile/embed/*.whl # there can only be one embedded wheel
cp $< $@

$(COG_WHEEL): $(COG_PYTHON_SOURCE)
$(PYTHON) -m build
@echo "Using prebuilt COG_WHEEL $<"
cp $< pkg/dockerfile/embed/
@touch $@
else
pkg/dockerfile/embed/.wheel: $(COG_PYTHON_SOURCE)
@mkdir -p pkg/dockerfile/embed
@rm -f pkg/dockerfile/embed/*.whl # there can only be one embedded wheel
$(PYTHON) -m pip wheel --no-deps --no-binary=:all: --wheel-dir=pkg/dockerfile/embed .
@touch $@
endif

$(COG_BINARIES): $(COG_GO_SOURCE) $(COG_EMBEDDED_WHEEL)
$(GORELEASER) build --clean --snapshot --single-target --id $@ --output $@
$(COG_BINARIES): $(COG_GO_SOURCE) pkg/dockerfile/embed/.wheel
@echo Building $@
@if git name-rev --name-only --tags HEAD | grep -qFx undefined; then \
$(GORELEASER) build --clean --snapshot --single-target --id $@ --output $@; \
else \
$(GORELEASER) build --clean --auto-snapshot --single-target --id $@ --output $@; \
fi

.PHONY: install
install: $(COG_BINARIES)
Expand All @@ -60,7 +61,7 @@ clean:
rm -f $(COG_BINARIES)

.PHONY: test-go
test-go: $(COG_EMBEDDED_WHEEL)
test-go: pkg/dockerfile/embed/.wheel
$(GO) get gotest.tools/gotestsum
$(GO) run gotest.tools/gotestsum -- -timeout 1200s -parallel 5 ./... $(ARGS)

Expand All @@ -84,7 +85,7 @@ generate:
$(GO) generate ./...

.PHONY: vet
vet:
vet: pkg/dockerfile/embed/.wheel
$(GO) vet ./...

.PHONY: check-fmt
Expand All @@ -93,7 +94,7 @@ check-fmt:
@test -z $$($(GO) run golang.org/x/tools/cmd/goimports -l .)

.PHONY: lint
lint: $(COG_EMBEDDED_WHEEL) $(COG_WHEEL) check-fmt vet
lint: pkg/dockerfile/embed/.wheel check-fmt vet
$(GO) run github.com/golangci/golangci-lint/cmd/golangci-lint run ./...
$(TOX) run --installpkg $(COG_WHEEL) -e lint,typecheck-pydantic2

Expand Down
23 changes: 9 additions & 14 deletions test-integration/test_integration/test_build.py
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import json
import os
import subprocess
import sys
from pathlib import Path

import pytest

from .util import assert_versions_match


def test_build_without_predictor(docker_image):
project_dir = Path(__file__).parent / "fixtures/no-predictor-project"
Expand Down Expand Up @@ -318,12 +319,8 @@ def test_precompile(docker_image):
assert build_process.returncode == 0


@pytest.mark.skipif(
not sys.platform.startswith("linux"),
reason="only runs on linux due to CUDA binaries",
)
def test_cog_install_base_image(docker_image):
project_dir = Path(__file__).parent / "fixtures/torch-cuda-baseimage-project"
project_dir = Path(__file__).parent / "fixtures/string-project"
build_process = subprocess.run(
[
"cog",
Expand All @@ -350,10 +347,7 @@ def test_cog_install_base_image(docker_image):
capture_output=True,
)
assert cog_installed_version_process.returncode == 0
# Clean up the cog python version to go from 0.11.2.dev15+g54c08f0 to 0.11.2
cog_installed_version_stdout = ".".join(
cog_installed_version_process.stdout.decode().strip().split(".")[:3]
)
cog_installed_version = cog_installed_version_process.stdout.decode().strip()
cog_version_process = subprocess.run(
[
"cog",
Expand All @@ -362,8 +356,9 @@ def test_cog_install_base_image(docker_image):
cwd=project_dir,
capture_output=True,
)
cog_version_stdout = cog_version_process.stdout.decode().strip().split()[2]
# Clean up the cog go version to go from 0.11.2-dev+g54c08f0 to 0.11.2
cog_version_stdout = cog_version_stdout.split("-")[0]
cog_version = cog_version_process.stdout.decode().strip().split()[2]

assert cog_version_stdout == cog_installed_version_stdout
assert_versions_match(
semver_version=cog_version,
pep440_version=cog_installed_version,
)
89 changes: 89 additions & 0 deletions test-integration/test_integration/util.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,97 @@
import random
import re
import string
import subprocess
import time

from packaging.version import VERSION_PATTERN

# From the SemVer spec: https://semver.org/
SEMVER_PATTERN = r"^(?P<major>0|[1-9]\d*)\.(?P<minor>0|[1-9]\d*)\.(?P<patch>0|[1-9]\d*)(?:-(?P<prerelease>(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*)(?:\.(?:0|[1-9]\d*|\d*[a-zA-Z-][0-9a-zA-Z-]*))*))?(?:\+(?P<buildmetadata>[0-9a-zA-Z-]+(?:\.[0-9a-zA-Z-]+)*))?$"


# Used to help ensure that the cog binary reports a semver version that matches
# the PEP440 version of the embedded Python package.
#
# These are all valid pairs:
#
# SEMVER PEP440 NOTES
# 0.11.2 0.11.2
# 0.11.2-alpha2 0.11.2a2 prerelease counters are not checked
# 0.11.2-beta1 0.11.2b1 " " " " "
# 0.11.2-rc4 0.11.2rc4 " " " " "
# 0.11.2-dev 0.11.2rc4.dev10 dev status overrides prerelease status
# 0.11.2+gabcd 0.11.2+gabce
#
# The following are not valid pairs:
#
# SEMVER PEP440 NOTES
# 0.11.2 0.11.3 mismatched release versions
# 0.11.2-alpha2 0.11.2alpha2 PEP440 uses 'a' instead of 'alpha'
# 0.11.2-alpha2 0.11.2b2 mismatched prerelease status
# 0.11.2-rc4 0.11.2rc4.dev10 dev status should have overridden prerelease status
# 0.11.2+gabcd 0.11.2+gdefg mismatched local/build metadata
#
def assert_versions_match(semver_version: str, pep440_version: str):
semver_re = re.compile(SEMVER_PATTERN)
pep440_re = re.compile(VERSION_PATTERN, re.VERBOSE | re.IGNORECASE)

semver_match = semver_re.match(semver_version)
pep440_match = pep440_re.match(pep440_version)

assert semver_match, f"Invalid semver version: {semver_version}"
assert pep440_match, f"Invalid PEP 440 version: {pep440_version}"

semver_groups = semver_match.groupdict()
pep440_groups = pep440_match.groupdict()

semver_release = (
f"{semver_groups['major']}.{semver_groups['minor']}.{semver_groups['patch']}"
)

# Check base release version
assert (
semver_release == pep440_groups["release"]
), f"Release versions do not match: {semver_release} != {pep440_groups['release']}"

# Check prerelease status
semver_pre = semver_groups["prerelease"]
pep440_pre = pep440_groups["pre"] or pep440_groups["dev"]

assert bool(semver_pre) == bool(pep440_pre), "Pre-release status does not match"

if semver_pre:
if semver_pre.startswith("alpha"):
assert (
pep440_groups["pre_l"] == "a"
), "Alpha pre-release status does not match"
assert not pep440_groups[
"dev"
], "Semver pre-release cannot also be a PEP440 dev build"

if semver_pre.startswith("beta"):
assert (
pep440_groups["pre_l"] == "b"
), "Beta pre-release status does not match"
assert not pep440_groups[
"dev"
], "Semver pre-release cannot also be a PEP440 dev build"

if semver_pre.startswith("rc"):
assert (
pep440_groups["pre_l"] == "rc"
), "Release candidate pre-release status does not match"
assert not pep440_groups[
"dev"
], "Semver pre-release cannot also be a PEP440 dev build"

if semver_pre.startswith("dev"):
assert pep440_groups["dev_l"] == "dev", "Dev build status does not match"

assert (
semver_groups["buildmetadata"] == pep440_groups["local"]
), f"Local/build metadata component does not match: {semver_groups['buildmetadata']} != {pep440_groups['local']}"


def random_string(length):
return "".join(random.choice(string.ascii_lowercase) for i in range(length))
Expand Down
1 change: 1 addition & 0 deletions tox.ini
Original file line number Diff line number Diff line change
Expand Up @@ -65,6 +65,7 @@ base_python = python3.12
changedir = test-integration
skip_install = true
deps =
packaging
pytest
pytest-rerunfailures
pytest-timeout
Expand Down