diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml index 0f1cf0fee..b3a2df32d 100644 --- a/.github/workflows/docker.yml +++ b/.github/workflows/docker.yml @@ -37,159 +37,23 @@ jobs: - name: Checkout uses: actions/checkout@v3 - name: Prepare base version - id: prep_base_version - shell: python - run: | - import re - import os - - ref = '${{ github.ref }}' - - # Set base_version according to the github ref type - is_release_candidate = False - is_release = False - is_nightly = False - - overwrite_hathor_core_version = False - - if '${{ github.event_name }}' == 'schedule': - commit_short_sha = '${{ github.sha }}'[:8] - base_version = 'nightly-' + commit_short_sha - is_nightly = True - elif ref.startswith('refs/tags/'): - git_tag = ref[10:] - base_version = git_tag.split('-', 1)[0] - - pre_release = (git_tag.split('-', 1)[1:] or [None])[0] - # This will be used to check against the versions in our source files - check_version = base_version[1:] - print('::set-output name=check-version::' + check_version) - - # Check if this is a release-candidate - if pre_release: - if re.match(r'^rc\.[0-9]{1,3}$', pre_release): - base_version = base_version + '-' + pre_release - is_release_candidate = True - else: - raise ValueError(f'Invalid Tag Value: {git_tag}') - else: - is_release = True - elif ref.startswith('refs/heads/'): - base_version = ref[11:].replace('/', '-') - if base_version == '${{ github.event.repository.default_branch }}': - base_version = 'stable' - elif ref.startswith('refs/pull/'): - base_version = 'pr-${{ github.event.number }}' - else: - base_version = 'noop' - - overwrite_hathor_core_version = is_release or is_release_candidate or is_nightly - # This is not the only condition to notify slack. - # We will also check if the Python version being built is the default one. - should_notify_slack = is_release or is_release_candidate - - github_repository = os.environ['GITHUB_REPOSITORY'] - if github_repository.lower() != 'hathornetwork/hathor-core': - should_notify_slack = False - - print('::set-output name=base-version::' + base_version) - print('::set-output name=is-release-candidate::' + ('true' if is_release_candidate else 'false')) - print('::set-output name=is-release::' + ('true' if is_release else 'false')) - print('::set-output name=is-nightly::' + ('true' if is_nightly else 'false')) - print('::set-output name=overwrite-hathor-core-version::' + ('true' if overwrite_hathor_core_version else 'false')) - print('::set-output name=should-notify-slack::' + ('true' if should_notify_slack else 'false')) - - name: Overwrite version - if: steps.prep_base_version.outputs.overwrite-hathor-core-version == 'true' - shell: python + id: prep run: | - base_version = '${{ steps.prep_base_version.outputs.base-version }}' - - with open('BUILD_VERSION', 'w') as file: - if base_version.startswith('v'): - base_version = base_version[1:] - print('base_version', base_version) - file.write(base_version + '\n') + export GITHUB_REF='${{ github.ref }}' + export GITHUB_EVENT_NAME='${{ github.event_name }}' + export GITHUB_SHA='${{ github.sha }}' + export GITHUB_EVENT_DEFAULT_BRANCH='${{ github.event.repository.default_branch }}' + export GITHUB_EVENT_NUMBER='${{ github.event.number }}' + export MATRIX_PYTHON_IMPL='${{ matrix.python-impl }}' + export MATRIX_PYTHON_VERSION='${{ matrix.python-version }}' + export SECRETS_DOCKERHUB_IMAGE='${{ secrets.DOCKERHUB_IMAGE }}' + export SECRETS_GHCR_IMAGE='${{ secrets.GHCR_IMAGE }}' + + python extras/github/docker.py - name: Check version - if: steps.prep_base_version.outputs.check-version + if: steps.prep.outputs.check-version run: | - make check-version VERSION='${{ steps.prep_base_version.outputs.check-version }}' - - name: Prepare tags - id: prep - shell: python - run: | - import datetime - import re - - base_version = '${{ steps.prep_base_version.outputs.base-version }}' - is_release_candidate = '${{ steps.prep_base_version.outputs.is-release-candidate }}' == 'true' - - # Extract default python versions from the Dockerfiles - def extract_pyver(filename): - for line in open(filename).readlines(): - if line.startswith('ARG PYTHON'): - return line.split('=')[1].strip() - dockerfile_cpython = 'Dockerfile' - dockerfile_pypy = 'Dockerfile.pypy' - default_python = 'python' + extract_pyver(dockerfile_cpython) - default_pypy = 'pypy' + extract_pyver(dockerfile_pypy) - - # Set which Dockerfile to use based on the versions matrix - if '${{ matrix.python-impl }}' == 'pypy': - dockerfile = dockerfile_pypy - suffix = 'pypy${{ matrix.python-version }}' - else: - dockerfile = dockerfile_cpython - suffix = 'python${{ matrix.python-version }}' - - # Build the tag list - - tags = set() - - # We don't want a tag with a python suffix for release-candidates - if is_release_candidate: - version = base_version - else: - version = base_version + '-' + suffix - tags.add(version) - - if suffix == default_python: - tags.add(base_version) - print('::set-output name=slack-notification-version::' + base_version) - elif suffix == default_pypy: - tags.add(base_version + '-pypy') - - # Check if this is a stable release - if re.match(r'^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', base_version): - minor = base_version.rpartition('.')[0] - tags.add(minor + '-' + suffix) - if suffix == default_python: - tags.add('latest') - elif '${{ github.event_name }}' == 'push' and not is_release_candidate: - tags.add('sha-' + '${{ github.sha }}'[:8]) - - # Build the image list and set outputs - print('::set-output name=version::' + version) - images = [] - docker_image = '${{ secrets.DOCKERHUB_IMAGE }}' - if docker_image: - images.append(docker_image) - print('::set-output name=login-dockerhub::true') - else: - print('::set-output name=login-dockerhub::false') - ghcr_image = '${{ secrets.GHCR_IMAGE }}' - if ghcr_image: - images.append(ghcr_image) - print('::set-output name=login-ghcr::true') - else: - print('::set-output name=login-ghcr::false') - if images and tags: - print('::set-output name=tags::' + ','.join(f'{i}:{t}' for i in images for t in tags)) - print('::set-output name=push::true') - else: - print('::set-output name=tags::dont-push--local-only') - print('::set-output name=push::false') - print('::set-output name=created::' + datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ')) - print('::set-output name=dockerfile::' + dockerfile) + make check-version VERSION='${{ steps.prep.outputs.check-version }}' - name: Set up QEMU # arm64 is not available natively uses: docker/setup-qemu-action@v2 - name: Set up Docker Buildx @@ -236,7 +100,7 @@ jobs: - name: Build and push uses: docker/build-push-action@v3 continue-on-error: ${{ matrix.python-impl == 'pypy' }} # PyPy is not first-class and has been causing some build failures - if: ${{ !env.ACT }} # Skip this steps when testing locally with https://github.com/nektos/act + if: ${{ !env.ACT }} # Skip this step when testing locally with https://github.com/nektos/act with: context: . file: ${{ steps.prep.outputs.dockerfile }} @@ -258,7 +122,7 @@ jobs: cache-from: type=local,src=/tmp/.buildx-cache cache-to: type=local,dest=/tmp/.buildx-cache - name: Slack Notification - if: ${{ steps.prep.outputs.slack-notification-version && steps.prep_base_version.outputs.should-notify-slack == 'true' && job.status == 'success' }} + if: ${{ steps.prep.outputs.slack-notification-version && steps.prep_base_version.outputs.disable-slack-notification == 'false' && job.status == 'success' }} uses: rtCamp/action-slack-notify@28e8b353eabda5998a2e1203aed33c5999944779 env: SLACK_COLOR: ${{ job.status }} # It can turn the job status into a color. Success will be green. diff --git a/Makefile b/Makefile index 7748ff338..d22dbcccc 100644 --- a/Makefile +++ b/Makefile @@ -7,6 +7,7 @@ all: check tests tests_cli = tests/cli/ tests_lib = $(filter-out ${tests_cli} tests/__pycache__/, $(dir $(wildcard tests/*/.))) +tests_ci = extras/github/ pytest_flags = -p no:warnings --cov-report=term --cov-report=html --cov-report=xml --cov=hathor @@ -44,8 +45,12 @@ tests-genesis: HATHOR_TEST_CONFIG_FILE=hathor.conf.mainnet pytest tests/tx/test_genesis.py HATHOR_TEST_CONFIG_FILE=hathor.conf.testnet pytest tests/tx/test_genesis.py +.PHONY: tests-ci +tests-ci: + pytest $(tests_ci) + .PHONY: tests -tests: tests-cli tests-lib tests-genesis +tests: tests-cli tests-lib tests-genesis tests-ci .PHONY: tests-full tests-full: diff --git a/extras/__init__.py b/extras/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/extras/check_version.sh b/extras/check_version.sh index a6c9c0c27..e62cfbe01 100755 --- a/extras/check_version.sh +++ b/extras/check_version.sh @@ -14,7 +14,6 @@ OPENAPI_FILE="hathor/cli/openapi_files/openapi_base.json" SRC_FILE="hathor/version.py" PACKAGE_FILE="pyproject.toml" -BUILD_VERSION_FILE="BUILD_VERSION" OPENAPI_VERSION=`grep "version\":" ${OPENAPI_FILE} | cut -d'"' -f4` SRC_VERSION=`grep "BASE_VERSION =" ${SRC_FILE} | cut -d "'" -f2` diff --git a/extras/github/__init__.py b/extras/github/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/extras/github/docker.py b/extras/github/docker.py new file mode 100644 index 000000000..b77a5b052 --- /dev/null +++ b/extras/github/docker.py @@ -0,0 +1,173 @@ +import re +import os +from typing import Dict + +def print_output(output: Dict): + for k, v in output.items(): + print(f'::set-output name={k}::{v}') + + +def prep_base_version(environ: Dict): + GITHUB_REF = environ.get('GITHUB_REF') + GITHUB_EVENT_NAME = environ.get('GITHUB_EVENT_NAME') + GITHUB_SHA = environ.get('GITHUB_SHA') + GITHUB_EVENT_DEFAULT_BRANCH = environ.get('GITHUB_EVENT_DEFAULT_BRANCH') + GITHUB_EVENT_NUMBER = environ.get('GITHUB_EVENT_NUMBER') + GITHUB_REPOSITORY = environ.get('GITHUB_REPOSITORY') + + ref = GITHUB_REF + + # Set base_version according to the github ref type + is_release_candidate = False + is_release = False + is_nightly = False + + overwrite_hathor_core_version = False + + output = {} + + if GITHUB_EVENT_NAME == 'schedule': + commit_short_sha = GITHUB_SHA[:8] + base_version = 'nightly-' + commit_short_sha + is_nightly = True + elif ref.startswith('refs/tags/'): + git_tag = ref[10:] + base_version = git_tag.split('-', 1)[0] + + pre_release = (git_tag.split('-', 1)[1:] or [None])[0] + overwrite_hathor_core_version = True + # This will be used to check against the versions in our source files + check_version = base_version[1:] + output['check-version'] = check_version + + # Check if this is a release-candidate + if pre_release: + if re.match(r'^rc\.[0-9]{1,3}$', pre_release): + base_version = base_version + '-' + pre_release + is_release_candidate = True + else: + raise ValueError(f'Invalid Tag Value: {git_tag}') + else: + is_release = True + elif ref.startswith('refs/heads/'): + base_version = ref[11:].replace('/', '-') + if base_version == GITHUB_EVENT_DEFAULT_BRANCH: + base_version = 'stable' + elif ref.startswith('refs/pull/'): + base_version = 'pr-' + GITHUB_EVENT_NUMBER + else: + base_version = 'noop' + + overwrite_hathor_core_version = is_release or is_release_candidate or is_nightly + # We don't know for sure at this point in which cases we should enable Slack notification, + # but we know when we should disable it for sure + output['disable-slack-notification'] = not (is_release or is_release_candidate) + + if GITHUB_REPOSITORY.lower() != 'hathornetwork/hathor-core': + output['disable-slack-notification'] = True + + return output, base_version, is_release_candidate, overwrite_hathor_core_version + + +def prep_tags(environ: Dict, base_version: str, is_release_candidate: bool): + MATRIX_PYTHON_IMPL = environ.get('MATRIX_PYTHON_IMPL') + MATRIX_PYTHON_VERSION = environ.get('MATRIX_PYTHON_VERSION') + + SECRETS_DOCKERHUB_IMAGE = environ.get('SECRETS_DOCKERHUB_IMAGE') + SECRETS_GHCR_IMAGE = environ.get('SECRETS_GHCR_IMAGE') + + GITHUB_EVENT_NAME = environ.get('GITHUB_EVENT_NAME') + + import datetime + import re + + output = {} + + # Extract default python versions from the Dockerfiles + def extract_pyver(filename): + for line in open(filename).readlines(): + if line.startswith('ARG PYTHON'): + return line.split('=')[1].strip() + dockerfile_cpython = 'Dockerfile' + dockerfile_pypy = 'Dockerfile.pypy' + default_python = 'python' + extract_pyver(dockerfile_cpython) + default_pypy = 'pypy' + extract_pyver(dockerfile_pypy) + + # Set which Dockerfile to use based on the versions matrix + if MATRIX_PYTHON_IMPL == 'pypy': + dockerfile = dockerfile_pypy + suffix = 'pypy' + MATRIX_PYTHON_VERSION + else: + dockerfile = dockerfile_cpython + suffix = 'python' + MATRIX_PYTHON_VERSION + + # Build the tag list + + tags = set() + + # We don't want a tag with a python suffix for release-candidates + if is_release_candidate: + version = base_version + else: + version = base_version + '-' + suffix + tags.add(version) + + if suffix == default_python: + tags.add(base_version) + output['slack-notification-version'] = base_version + elif suffix == default_pypy: + tags.add(base_version + '-pypy') + + # Check if this is a stable release + if re.match(r'^v[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}$', base_version): + minor = base_version.rpartition('.')[0] + tags.add(minor + '-' + suffix) + if suffix == default_python: + tags.add('latest') + elif GITHUB_EVENT_NAME == 'push' and not is_release_candidate: + tags.add('sha-' + '${{ github.sha }}'[:8]) + + # Build the image list and set outputs + output['version'] = version + images = [] + docker_image = SECRETS_DOCKERHUB_IMAGE + if docker_image: + images.append(docker_image) + output['login-dockerhub'] = 'true' + else: + output['login-dockerhub'] = 'false' + ghcr_image = SECRETS_GHCR_IMAGE + if ghcr_image: + images.append(ghcr_image) + output['login-ghcr'] = 'true' + else: + output['login-ghcr'] = 'false' + if images and tags: + output['tags'] = ','.join(f'{i}:{t}' for i in images for t in tags) + output['push'] = 'true' + else: + output['tags'] = 'dont-push--local-only' + output['push'] = 'false' + + output['created'] = datetime.datetime.utcnow().strftime('%Y-%m-%dT%H:%M:%SZ') + output['dockerfile'] = dockerfile + + return output + + +def overwrite_version(base_version: str): + with open('BUILD_VERSION', 'w') as file: + if base_version.startswith('v'): + base_version = base_version[1:] + file.write(base_version + '\n') + + +if __name__ == '__main__': + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + print_output(output) + + output = prep_tags(os.environ, base_version, is_release_candidate) + print_output(output) + + if overwrite_hathor_core_version: + overwrite_version(base_version) diff --git a/extras/github/test_docker.py b/extras/github/test_docker.py new file mode 100644 index 000000000..4e7d997b4 --- /dev/null +++ b/extras/github/test_docker.py @@ -0,0 +1,194 @@ +import os +import unittest + +from extras.github.docker import prep_base_version, prep_tags + +class DockerWorkflowTest(unittest.TestCase): + def test_nightly_build_no_github_secret(self): + os.environ.update({ + 'GITHUB_REF': 'refs/heads/ci/extract-python-scripts', + 'GITHUB_EVENT_NAME': 'schedule', + 'GITHUB_SHA': '55629a7d0ae267cdd27618f452e9f1ad6764fd43', + 'GITHUB_EVENT_DEFAULT_BRANCH': 'master', + 'GITHUB_EVENT_NUMBER': '', + 'MATRIX_PYTHON_IMPL': 'python', + 'MATRIX_PYTHON_VERSION': '3.9', + 'SECRETS_DOCKERHUB_IMAGE': '', + 'SECRETS_GHCR_IMAGE': '', + }) + + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + + self.assertTrue(overwrite_hathor_core_version) + self.assertFalse(is_release_candidate) + self.assertTrue(output['disable-slack-notification']) + self.assertEqual(base_version, 'nightly-55629a7d') + + output = prep_tags(os.environ, base_version, is_release_candidate) + + self.assertEqual(output['slack-notification-version'], base_version) + self.assertEqual(output['version'], base_version + '-python3.9') + self.assertEqual(output['login-dockerhub'], 'false') + self.assertEqual(output['login-ghcr'], 'false') + self.assertEqual(output['tags'], 'dont-push--local-only') + self.assertEqual(output['push'], 'false') + self.assertEqual(output['dockerfile'], 'Dockerfile') + + def test_nightly_build(self): + os.environ.update({ + 'GITHUB_REF': 'refs/heads/ci/extract-python-scripts', + 'GITHUB_EVENT_NAME': 'schedule', + 'GITHUB_SHA': '55629a7d0ae267cdd27618f452e9f1ad6764fd43', + 'GITHUB_EVENT_DEFAULT_BRANCH': 'master', + 'GITHUB_EVENT_NUMBER': '', + 'MATRIX_PYTHON_IMPL': 'python', + 'MATRIX_PYTHON_VERSION': '3.9', + 'SECRETS_DOCKERHUB_IMAGE': 'mock_image', + 'SECRETS_GHCR_IMAGE': '', + }) + + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + + self.assertTrue(overwrite_hathor_core_version) + self.assertFalse(is_release_candidate) + self.assertTrue(output['disable-slack-notification']) + self.assertEqual(base_version, 'nightly-55629a7d') + + output = prep_tags(os.environ, base_version, is_release_candidate) + + self.assertEqual(output['slack-notification-version'], base_version) + self.assertEqual(output['version'], base_version + '-python3.9') + self.assertEqual(output['login-dockerhub'], 'true') + self.assertEqual(output['login-ghcr'], 'false') + self.assertEqual(len(output['tags'].split(',')), 2) + self.assertIn('mock_image:nightly-55629a7d', output['tags'].split(',')) + self.assertIn('mock_image:nightly-55629a7d-python3.9', output['tags'].split(',')) + self.assertEqual(output['push'], 'true') + self.assertEqual(output['dockerfile'], 'Dockerfile') + + + def test_release_candidate_non_default_python(self): + os.environ.update({ + 'GITHUB_REF': 'refs/tags/v0.53.0-rc.1', + 'GITHUB_EVENT_NAME': 'push', + 'GITHUB_SHA': '55629a7d0ae267cdd27618f452e9f1ad6764fd43', + 'GITHUB_EVENT_DEFAULT_BRANCH': 'master', + 'GITHUB_EVENT_NUMBER': '', + 'MATRIX_PYTHON_IMPL': 'python', + 'MATRIX_PYTHON_VERSION': '3.11', + 'SECRETS_DOCKERHUB_IMAGE': 'mock_image', + 'SECRETS_GHCR_IMAGE': '', + }) + + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + + self.assertTrue(overwrite_hathor_core_version) + self.assertTrue(is_release_candidate) + self.assertFalse(output['disable-slack-notification']) + self.assertEqual(base_version, 'v0.53.0-rc.1') + + output = prep_tags(os.environ, base_version, is_release_candidate) + + self.assertNotIn('slack-notification-version', output) + self.assertEqual(output['version'], base_version) + self.assertEqual(output['login-dockerhub'], 'true') + self.assertEqual(output['login-ghcr'], 'false') + self.assertEqual(output['tags'], 'dont-push--local-only') + self.assertEqual(output['push'], 'false') + self.assertEqual(output['dockerfile'], 'Dockerfile') + + def test_release_candidate_default_python(self): + os.environ.update({ + 'GITHUB_REF': 'refs/tags/v0.53.0-rc.1', + 'GITHUB_EVENT_NAME': 'push', + 'GITHUB_SHA': '55629a7d0ae267cdd27618f452e9f1ad6764fd43', + 'GITHUB_EVENT_DEFAULT_BRANCH': 'master', + 'GITHUB_EVENT_NUMBER': '', + 'MATRIX_PYTHON_IMPL': 'python', + 'MATRIX_PYTHON_VERSION': '3.9', + 'SECRETS_DOCKERHUB_IMAGE': 'mock_image', + 'SECRETS_GHCR_IMAGE': '', + }) + + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + + self.assertTrue(overwrite_hathor_core_version) + self.assertTrue(is_release_candidate) + self.assertFalse(output['disable-slack-notification']) + self.assertEqual(base_version, 'v0.53.0-rc.1') + + output = prep_tags(os.environ, base_version, is_release_candidate) + + self.assertEqual(output['slack-notification-version'], base_version) + self.assertEqual(output['version'], base_version) + self.assertEqual(output['login-dockerhub'], 'true') + self.assertEqual(output['login-ghcr'], 'false') + self.assertEqual(output['tags'], 'mock_image:v0.53.0-rc.1') + self.assertEqual(output['push'], 'true') + self.assertEqual(output['dockerfile'], 'Dockerfile') + + def test_release_default_python(self): + os.environ.update({ + 'GITHUB_REF': 'refs/tags/v0.53.0', + 'GITHUB_EVENT_NAME': 'push', + 'GITHUB_SHA': '55629a7d0ae267cdd27618f452e9f1ad6764fd43', + 'GITHUB_EVENT_DEFAULT_BRANCH': 'master', + 'GITHUB_EVENT_NUMBER': '', + 'MATRIX_PYTHON_IMPL': 'python', + 'MATRIX_PYTHON_VERSION': '3.9', + 'SECRETS_DOCKERHUB_IMAGE': 'mock_image', + 'SECRETS_GHCR_IMAGE': '', + }) + + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + + self.assertTrue(overwrite_hathor_core_version) + self.assertFalse(is_release_candidate) + self.assertFalse(output['disable-slack-notification']) + self.assertEqual(base_version, 'v0.53.0') + + output = prep_tags(os.environ, base_version, is_release_candidate) + + self.assertEqual(output['slack-notification-version'], base_version) + self.assertEqual(output['version'], base_version + '-python3.9') + self.assertEqual(output['login-dockerhub'], 'true') + self.assertEqual(output['login-ghcr'], 'false') + self.assertEqual(len(output['tags'].split(',')), 4) + self.assertIn('mock_image:v0.53-python3.9', output['tags'].split(',')) + self.assertIn('mock_image:v0.53.0-python3.9', output['tags'].split(',')) + self.assertIn('mock_image:v0.53.0', output['tags'].split(',')) + self.assertIn('mock_image:latest', output['tags'].split(',')) + self.assertEqual(output['push'], 'true') + self.assertEqual(output['dockerfile'], 'Dockerfile') + + def test_release_non_default_python(self): + os.environ.update({ + 'GITHUB_REF': 'refs/tags/v0.53.0', + 'GITHUB_EVENT_NAME': 'push', + 'GITHUB_SHA': '55629a7d0ae267cdd27618f452e9f1ad6764fd43', + 'GITHUB_EVENT_DEFAULT_BRANCH': 'master', + 'GITHUB_EVENT_NUMBER': '', + 'MATRIX_PYTHON_IMPL': 'pypy', + 'MATRIX_PYTHON_VERSION': '3.8', + 'SECRETS_DOCKERHUB_IMAGE': 'mock_image', + 'SECRETS_GHCR_IMAGE': '', + }) + + output, base_version, is_release_candidate, overwrite_hathor_core_version = prep_base_version(os.environ) + + self.assertTrue(overwrite_hathor_core_version) + self.assertFalse(is_release_candidate) + self.assertFalse(output['disable-slack-notification']) + self.assertEqual(base_version, 'v0.53.0') + + output = prep_tags(os.environ, base_version, is_release_candidate) + + self.assertNotIn('slack-notification-version', output) + self.assertEqual(output['version'], 'v0.53.0-pypy3.8') + self.assertEqual(output['login-dockerhub'], 'true') + self.assertEqual(output['login-ghcr'], 'false') + self.assertEqual(len(output['tags'].split(',')), 2) + self.assertIn('mock_image:v0.53-pypy3.8', output['tags'].split(',')) + self.assertIn('mock_image:v0.53.0-pypy3.8', output['tags'].split(',')) + self.assertEqual(output['push'], 'true') + self.assertEqual(output['dockerfile'], 'Dockerfile.pypy') \ No newline at end of file