Skip to content
Merged
168 changes: 16 additions & 152 deletions .github/workflows/docker.yml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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 }}
Expand All @@ -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.
Expand Down
7 changes: 6 additions & 1 deletion Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -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

Expand Down Expand Up @@ -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:
Expand Down
Empty file added extras/__init__.py
Empty file.
1 change: 0 additions & 1 deletion extras/check_version.sh
Original file line number Diff line number Diff line change
Expand Up @@ -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`
Expand Down
Empty file added extras/github/__init__.py
Empty file.
173 changes: 173 additions & 0 deletions extras/github/docker.py
Original file line number Diff line number Diff line change
@@ -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)
Loading