Skip to content

Commit

Permalink
Use GitHub actions for CI (#689)
Browse files Browse the repository at this point in the history
Replace use of Travis CI with GitHub Actions.  Actions are more flexible and have the advantage that they are copied with the repository, so we don't need to set them up again each time the repository moves or is cloned.

This started out as a straightforward port of the Travis CI configuration, but I ended up with a slightly different organization.  There are 2 main workflows:

- the "Tests" workflow in `tests.yml` which runs:

  * unit tests, for all versions of Python supported by GitHub actions
  * installation tests on all the runner platforms supported by GitHub, using the default `python` binary there

- the "Publish" workflow in `publish.yml` which creates a Docker image; publishing is triggered only on completion of the "Tests" workflow on the `master` branch, and is aborted immediately if any of the tests is not successful.  Pull requests should *not* be published automatically, as contributors may then publish images using secrets stored on the `master` branch.
  • Loading branch information
riccardomurri committed Jan 5, 2021
1 parent 8656d1a commit 9aa6439
Show file tree
Hide file tree
Showing 4 changed files with 245 additions and 51 deletions.
69 changes: 69 additions & 0 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,69 @@
name: Publish released code

on:
workflow_run:
workflows: ["Tests"]
branches:
- master
types:
- completed

jobs:
dockerhub:
name: Build and upload Docker image
runs-on: ubuntu-latest
steps:

- name: Exit early if tests were unsuccessful
if: github.event.workflow_run.conclusion != 'success'
run: |
echo "Tests were unsuccessful, skipping publishing."
exit 1
#
# Install from sources
#
- name: Checkout ElastiCluster sources
# see: https://github.com/actions/checkout
uses: actions/checkout@v2
with:
persist-credentials: false

- uses: actions/setup-python@v2
with:
python-version: "3.7"

- name: Install `pip` and `setuptools`
run: "pip install --upgrade 'setuptools' 'pip>=8.1.2'"

- name: Install ElastiCluster from sources
run: "pip install -e ."

#
# Upload Docker image
#
- name: Publish to DockerHub
uses: elgohr/Publish-Docker-Github-Action@master
with:
name: "riccardomurri/elasticluster"
username: ${{ secrets.DOCKER_USER }}
password: ${{ secrets.DOCKER_PASS }}

# FIXME: we occasionally might want to also publish artifacts from PRs,
# in which case the `tags:` line should *not* include `latest` and be only
# `tags: pr-${{ github.event.number }}` But it is dangerous to automatically
# do this, as contributors could modify these workflows as well, so PR publishing
# should always be a manual action.
tags: "latest,${{ github.head_ref }}"

# push tags/release by their git name (e.g. refs/tags/MY_TAG_NAME)
tag_names: true

# push tags using the semver syntax by their git name
# (e.g. refs/tags/v1.2.3 will push four docker tags: 1.2.3, 1.2 and 1)
tag_semver: true

- name: Success
run: |
echo "OK: Docker image successfully pushed."
exit 0
156 changes: 156 additions & 0 deletions .github/workflows/tests.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
name: Tests

on:
push:
pull_request:
types:
- opened
- ready_for_review
- review_request_removed
- synchronize
- reopened

jobs:
unit-tests:
name: Run Unit Tests
strategy:
# we want to know which Python's pass and which fail
fail-fast: false
matrix:
# use the `matrix: include:` trick to build a simple list of
# parameters; we only need to override some package versions in
# specific cases (e.g., Python 2.7 needs older `setuptools` and `pip`)
include:

- python: "2.7"
# as of 2021-01-05, setuptools 51 and pip 21 have dropped support
# for Py 2.7 completely, so we need to use earlier versions to
# continue testing
prerequisites: "'setuptools<=44.1.1' 'pip==20.3.3'"

- python: "3.5"
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"

- python: "3.6"
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"

- python: "3.7"
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"

- python: "3.8"
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"

- python: "3.9"
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"

env:
PYTHON: ${{ matrix.python }}

runs-on: ubuntu-latest
steps:

- name: Show Python location and version
run: |
# display data about Python interpreter
set -e
echo "Python executable: $(command -v python)"
echo "Python version: $(python -V 2>&1)"
#
# Install from sources
#
- name: Checkout ElastiCluster sources
# see: https://github.com/actions/checkout
uses: actions/checkout@v2
with:
persist-credentials: false

- uses: actions/setup-python@v2
with:
python-version: ${{ matrix.python }}

- name: Install `pip` and `setuptools`
run: "pip install --upgrade ${{ matrix.prerequisites }}"

- name: Install ElastiCluster from sources
run: "pip install -e ."

#
# Unit tests
#
- name: Install test dependencies
run: "pip install 'pytest>=2.10' 'pytest-cov' 'mock' 'tox' 'codecov'"

- name: Run unit tests
run: "pytest -v --cov=elasticluster --cov-branch"

- name: Upload coverage to Codecov
uses: codecov/codecov-action@v1
with:
flags: unittests
env_vars: PYTHON

- name: Success
run: |
echo "OK: ElastiCluster's unit tests successfully performed."
exit 0
install-tests:
name: Test installation on different OSes
strategy:
matrix:
env:
- os: ubuntu-20.04
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"
path: "/home/runner/.local/bin"
- os: ubuntu-18.04
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"
path: "/home/runner/.local/bin"
- os: ubuntu-16.04
prerequisites: "'setuptools<=44.1.1' 'pip==20.3.3'"
path: "/home/runner/.local/bin"
- os: macos-11.0
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"
path: '/Library/Frameworks/Python.framework/Versions/2.7/bin'
- os: macos-10.15
prerequisites: "'setuptools>=21' 'pip>=9.0.0'"
path: '/Library/Frameworks/Python.framework/Versions/2.7/bin'
# we want to know which OSes pass and which fail
fail-fast: false

runs-on: ${{ matrix.env.os }}
steps:

- name: Show Python location and version
run: |
# display data about Python interpreter
echo "Python executable: $(command -v python)"
echo "Python version: $(python -V 2>&1)"
#
# Install from sources
#
- name: Checkout ElastiCluster sources
# see: https://github.com/actions/checkout
uses: actions/checkout@v2
with:
persist-credentials: false

- name: Install `pip` and `setuptools`
run: "pip install --upgrade ${{ matrix.env.prerequisites }}"

- name: Install ElastiCluster from sources
run: "pip install ."

- name: Try running `elasticluster` commands
run: |
# different incantations of the `elasticluster` command
export PATH="$PATH:${{ matrix.env.path }}"
elasticluster --version
elasticluster list-templates
- name: Success
run: |
echo "OK: ElastiCluster successfully installed."
exit 0
43 changes: 0 additions & 43 deletions .travis.yml

This file was deleted.

28 changes: 20 additions & 8 deletions tests/test_cli.py
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,10 @@
from future import standard_library
standard_library.install_aliases()

from io import StringIO
import os
import subprocess
import sys

import pytest

from elasticluster.utils import temporary_dir, environment

__author__ = (', '.join([
Expand All @@ -37,14 +34,29 @@


# TODO: Could be a parametric fixture I guess
def _run_command(argv):
def _run_elasticluster_cmd(argv):
"""
Run the `elasticluster` command with additional arguments.
Return STDOUT, STDERR, and the process exit status.
"""
with temporary_dir() as tmpdir:
with environment(HOME=os.getcwd()) as env:
with environment(
HOME=os.getcwd(),
PYTHONWARNINGS=(
# as support for Py2 wanes, we must silence warnings that
# Python 2.7 will no longer be supported, as they make the
# tests fail unnecessarily (functionality is OK, we just get
# some extra lines into STDERR). However,
# `cryptography.utils.CryptographyDeprecationWarning` is a
# subclass of `UserWarning` exactly because
# `DeprecationWarnings` are ignored by default, so we need to
# ignore all `UserWarnings` as well (cannot ignore a non-builtin
# exception class via an environmental variable)
'ignore::DeprecationWarning,ignore::UserWarning' if sys.version_info < (3, 6)
# display all warnings on Py3.6+
else ''),
) as env:
proc = subprocess.Popen(
['elasticluster'] + argv,
stdin=None,
Expand All @@ -57,22 +69,22 @@ def _run_command(argv):


def test_cli_help():
out, err, code = _run_command(["--help"])
out, err, code = _run_elasticluster_cmd(["--help"])
assert out.startswith(b"usage: elasticluster [-h] [-v]")
assert not err
assert not code


def test_cli_version():
from elasticluster import __version__ as elasticluster_version
out, err, code = _run_command(["--version"])
out, err, code = _run_elasticluster_cmd(["--version"])
assert not err
assert not code
assert out.rstrip() == ("elasticluster version {0}".format(elasticluster_version)).encode('ascii')


def test_cli_list_default(tmpdir):
out, err, code = _run_command(["list"])
out, err, code = _run_elasticluster_cmd(["list"])
assert out.rstrip() == b"No clusters found."
# default configuration is insufficient
assert b"references non-existing login section" in err
Expand Down

0 comments on commit 9aa6439

Please sign in to comment.