From 3c63b3e46ef2c4c99c1cab341eb5e09c5f4e8884 Mon Sep 17 00:00:00 2001 From: Andrew-Chen-Wang Date: Tue, 14 Apr 2020 11:52:14 -0400 Subject: [PATCH 1/5] Add Docker support to Travis and GitLab CI * Celerybeat start files wait for table migration so that errors don't occur if a migration does not happen post-build --- tests/test_cookiecutter_generation.py | 10 +++- {{cookiecutter.project_slug}}/.gitlab-ci.yml | 12 +++++ {{cookiecutter.project_slug}}/.travis.yml | 52 ++++++++++++++----- .../compose/local/django/celery/beat/start | 37 +++++++++++++ .../production/django/celery/beat/start | 35 +++++++++++++ 5 files changed, 132 insertions(+), 14 deletions(-) diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index 2c7e6dc4..8785d7ce 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -181,7 +181,15 @@ def test_travis_invokes_pytest(cookies, context): with open(f"{result.project}/.travis.yml", "r") as travis_yml: try: - assert yaml.load(travis_yml, Loader=yaml.FullLoader)["script"] == ["pytest"] + yml = yaml.load(travis_yml, Loader=yaml.FullLoader)["jobs"]["include"] + assert yml[0]["script"] == ["flake8"] + + if context.get("use_docker") == "y": + assert yml[1]["script"] == [ + "docker-compose -f local.yml run django pytest" + ] + else: + assert yml[1]["script"] == ["pytest"] except yaml.YAMLError as e: pytest.fail(e) diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 91ad7238..8c032bf7 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -19,8 +19,19 @@ flake8: pytest: stage: test image: python:3.7 + {% if cookiecutter.use_docker == 'y' -%} tags: - docker + services: + - docker + before_script: + - docker-compose -f local.yml build + - docker-compose -f local.yml up -d + script: + - docker-compose -f local.yml run django pytest + {%- else %} + tags: + - python services: - postgres:11 variables: @@ -31,4 +42,5 @@ pytest: script: - pytest + {%- endif %} diff --git a/{{cookiecutter.project_slug}}/.travis.yml b/{{cookiecutter.project_slug}}/.travis.yml index 31695e41..7b618e37 100644 --- a/{{cookiecutter.project_slug}}/.travis.yml +++ b/{{cookiecutter.project_slug}}/.travis.yml @@ -1,17 +1,43 @@ dist: xenial -services: - - postgresql -before_install: - - sudo apt-get update -qq - - sudo apt-get install -qq build-essential gettext python-dev zlib1g-dev libpq-dev xvfb - - sudo apt-get install -qq libjpeg8-dev libfreetype6-dev libwebp-dev - - sudo apt-get install -qq graphviz-dev python-setuptools python3-dev python-virtualenv python-pip - - sudo apt-get install -qq firefox automake libtool libreadline6 libreadline6-dev libreadline-dev - - sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm + language: python python: - "3.7" -install: - - pip install -r requirements/local.txt -script: - - "pytest" + +services: + - {% if cookiecutter.use_docker == 'y' %}docker{% else %}postgresql{% endif %} +jobs: + include: + - name: "Linter" + before_script: + - pip install -q flake8 + script: + - "flake8" + + - name: "Django Test" + {% if cookiecutter.use_docker == 'y' -%} + before_script: + - docker-compose -v + - docker -v + - docker-compose -f local.yml build + - docker-compose -f local.yml up -d + script: + - "docker-compose -f local.yml run django pytest" + after_failure: + - docker-compose -f local.yml logs + {%- else %} + before_install: + - sudo apt-get update -qq + - sudo apt-get install -qq build-essential gettext python-dev zlib1g-dev libpq-dev xvfb + - sudo apt-get install -qq libjpeg8-dev libfreetype6-dev libwebp-dev + - sudo apt-get install -qq graphviz-dev python-setuptools python3-dev python-virtualenv python-pip + - sudo apt-get install -qq firefox automake libtool libreadline6 libreadline6-dev libreadline-dev + - sudo apt-get install -qq libsqlite3-dev libxml2 libxml2-dev libssl-dev libbz2-dev wget curl llvm + language: python + python: + - "3.8" + install: + - pip install -r requirements/local.txt + script: + - "pytest" + {%- endif %} diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start index c04a7365..779750ca 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start @@ -5,4 +5,41 @@ set -o nounset rm -f './celerybeat.pid' + +postgres_ready() { +python << END +import sys +from time import sleep +import psycopg2 +try: + conn = psycopg2.connect( + dbname="${POSTGRES_DB}", + user="${POSTGRES_USER}", + password="${POSTGRES_PASSWORD}", + host="${POSTGRES_HOST}", + port="${POSTGRES_PORT}", + ) + # Check if table exists yet. + # If not, wait for docker-compose up to migrate all tables. + cur = conn.cursor() + cur.execute( + "select exists(select * from ${POSTGRES_DB}.tables where table_name=%s)", + ('django_celery_beat_periodictask',) + ) + conn.close() +except psycopg2.OperationalError: + sys.exit(-1) +except psycopg2.errors.UndefinedTable: + conn.close() + sys.exit(-1) + +sys.exit(0) +END +} +until postgres_ready; do + >&2 echo 'Waiting for celerybeat models to be migrated...' + sleep 1 +done +>&2 echo 'PostgreSQL is ready' + celery -A config.celery_app beat -l INFO diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start index 20b93123..0bc76667 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start @@ -4,5 +4,40 @@ set -o errexit set -o pipefail set -o nounset +postgres_ready() { +python << END +import sys +from time import sleep +import psycopg2 +try: + conn = psycopg2.connect( + dbname="${POSTGRES_DB}", + user="${POSTGRES_USER}", + password="${POSTGRES_PASSWORD}", + host="${POSTGRES_HOST}", + port="${POSTGRES_PORT}", + ) + # Check if table exists yet. + # If not, wait for docker-compose up to migrate all tables. + cur = conn.cursor() + cur.execute( + "select exists(select * from ${POSTGRES_DB}.tables where table_name=%s)", + ('django_celery_beat_periodictask',) + ) + conn.close() +except psycopg2.OperationalError: + sys.exit(-1) +except psycopg2.errors.UndefinedTable: + conn.close() + sys.exit(-1) + +sys.exit(0) +END +} +until postgres_ready; do + >&2 echo 'Waiting for celerybeat models to be migrated...' + sleep 1 +done +>&2 echo 'PostgreSQL is ready' celery -A config.celery_app beat -l INFO From dd0e7c0c801a55f13c6235dc5937ef81664752e8 Mon Sep 17 00:00:00 2001 From: Andrew-Chen-Wang Date: Tue, 14 Apr 2020 12:04:41 -0400 Subject: [PATCH 2/5] Remove unnecssary import in celerybeat start files --- .../compose/local/django/celery/beat/start | 1 - .../compose/production/django/celery/beat/start | 1 - 2 files changed, 2 deletions(-) diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start index 779750ca..2499f866 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start @@ -9,7 +9,6 @@ rm -f './celerybeat.pid' postgres_ready() { python << END import sys -from time import sleep import psycopg2 try: conn = psycopg2.connect( diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start index 0bc76667..3882e194 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start @@ -7,7 +7,6 @@ set -o nounset postgres_ready() { python << END import sys -from time import sleep import psycopg2 try: conn = psycopg2.connect( From 25cd8ea72b8dd3e017ddac738373ab64ce934524 Mon Sep 17 00:00:00 2001 From: Andrew-Chen-Wang Date: Tue, 14 Apr 2020 15:05:53 -0400 Subject: [PATCH 3/5] Update beat start files with better? SQL statement --- .../compose/local/django/celery/beat/start | 13 +++++++------ .../compose/production/django/celery/beat/start | 12 +++++++----- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start index 2499f866..477dfca3 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start @@ -22,17 +22,18 @@ try: # If not, wait for docker-compose up to migrate all tables. cur = conn.cursor() cur.execute( - "select exists(select * from ${POSTGRES_DB}.tables where table_name=%s)", + "select exists(select * from information_schema.tables where table_name=%s)", ('django_celery_beat_periodictask',) ) - conn.close() + if cur.fetchone()[0] == 1: + cur.close() + sys.exit(0) + else: + cur.close() + sys.exit(-1) except psycopg2.OperationalError: sys.exit(-1) -except psycopg2.errors.UndefinedTable: - conn.close() - sys.exit(-1) -sys.exit(0) END } until postgres_ready; do diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start index 3882e194..0cc8ae8a 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start @@ -20,15 +20,17 @@ try: # If not, wait for docker-compose up to migrate all tables. cur = conn.cursor() cur.execute( - "select exists(select * from ${POSTGRES_DB}.tables where table_name=%s)", + "select exists(select * from information_schema.tables where table_name=%s)", ('django_celery_beat_periodictask',) ) - conn.close() + if cur.fetchone()[0] == 1: + cur.close() + sys.exit(0) + else: + cur.close() + sys.exit(-1) except psycopg2.OperationalError: sys.exit(-1) -except psycopg2.errors.UndefinedTable: - conn.close() - sys.exit(-1) sys.exit(0) END From d2285d9e2d1798e863d355e0efee76fe7e73fdd1 Mon Sep 17 00:00:00 2001 From: Andrew-Chen-Wang Date: Thu, 16 Apr 2020 13:34:12 -0400 Subject: [PATCH 4/5] pytest.parametrized test for generating CI * Removed celerybeat start's postgres_ready() for new PR for closer inspection and further review. Travis and GitLab CI reflect changes --- tests/test_cookiecutter_generation.py | 30 ++++++++------- {{cookiecutter.project_slug}}/.gitlab-ci.yml | 2 + {{cookiecutter.project_slug}}/.travis.yml | 2 + .../compose/local/django/celery/beat/start | 37 ------------------- .../production/django/celery/beat/start | 36 ------------------ 5 files changed, 21 insertions(+), 86 deletions(-) diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index 8785d7ce..ecd08ea0 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -170,8 +170,12 @@ def test_black_passes(cookies, context_override): pytest.fail(e.stdout.decode()) -def test_travis_invokes_pytest(cookies, context): - context.update({"ci_tool": "Travis"}) +@pytest.mark.parametrize( + ["use_docker", "expected_test_script"], + [("n", "pytest"), ("y", "docker-compose -f local.yml run django pytest"),], +) +def test_travis_invokes_pytest(cookies, context, use_docker, expected_test_script): + context.update({"ci_tool": "Travis", "use_docker": use_docker}) result = cookies.bake(extra_context=context) assert result.exit_code == 0 @@ -183,19 +187,19 @@ def test_travis_invokes_pytest(cookies, context): try: yml = yaml.load(travis_yml, Loader=yaml.FullLoader)["jobs"]["include"] assert yml[0]["script"] == ["flake8"] - - if context.get("use_docker") == "y": - assert yml[1]["script"] == [ - "docker-compose -f local.yml run django pytest" - ] - else: - assert yml[1]["script"] == ["pytest"] + assert yml[1]["script"] == [expected_test_script] except yaml.YAMLError as e: - pytest.fail(e) + pytest.fail(str(e)) -def test_gitlab_invokes_flake8_and_pytest(cookies, context): - context.update({"ci_tool": "Gitlab"}) +@pytest.mark.parametrize( + ["use_docker", "expected_test_script"], + [("n", "pytest"), ("y", "docker-compose -f local.yml run django pytest"),], +) +def test_gitlab_invokes_flake8_and_pytest( + cookies, context, use_docker, expected_test_script +): + context.update({"ci_tool": "Gitlab", "use_docker": use_docker}) result = cookies.bake(extra_context=context) assert result.exit_code == 0 @@ -207,7 +211,7 @@ def test_gitlab_invokes_flake8_and_pytest(cookies, context): try: gitlab_config = yaml.load(gitlab_yml, Loader=yaml.FullLoader) assert gitlab_config["flake8"]["script"] == ["flake8"] - assert gitlab_config["pytest"]["script"] == ["pytest"] + assert gitlab_config["pytest"]["script"] == [expected_test_script] except yaml.YAMLError as e: pytest.fail(e) diff --git a/{{cookiecutter.project_slug}}/.gitlab-ci.yml b/{{cookiecutter.project_slug}}/.gitlab-ci.yml index 4d67f8a1..a74d5de8 100644 --- a/{{cookiecutter.project_slug}}/.gitlab-ci.yml +++ b/{{cookiecutter.project_slug}}/.gitlab-ci.yml @@ -29,6 +29,8 @@ pytest: - docker before_script: - docker-compose -f local.yml build + # Ensure celerybeat does not crash due to non-existent tables + - docker-compose -f local.yml run --rm django python manage.py migrate - docker-compose -f local.yml up -d script: - docker-compose -f local.yml run django pytest diff --git a/{{cookiecutter.project_slug}}/.travis.yml b/{{cookiecutter.project_slug}}/.travis.yml index 7f90b5f5..fac60650 100644 --- a/{{cookiecutter.project_slug}}/.travis.yml +++ b/{{cookiecutter.project_slug}}/.travis.yml @@ -20,6 +20,8 @@ jobs: - docker-compose -v - docker -v - docker-compose -f local.yml build + # Ensure celerybeat does not crash due to non-existent tables + - docker-compose -f local.yml run --rm django python manage.py migrate - docker-compose -f local.yml up -d script: - "docker-compose -f local.yml run django pytest" diff --git a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start index 477dfca3..c04a7365 100644 --- a/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/local/django/celery/beat/start @@ -5,41 +5,4 @@ set -o nounset rm -f './celerybeat.pid' - -postgres_ready() { -python << END -import sys -import psycopg2 -try: - conn = psycopg2.connect( - dbname="${POSTGRES_DB}", - user="${POSTGRES_USER}", - password="${POSTGRES_PASSWORD}", - host="${POSTGRES_HOST}", - port="${POSTGRES_PORT}", - ) - # Check if table exists yet. - # If not, wait for docker-compose up to migrate all tables. - cur = conn.cursor() - cur.execute( - "select exists(select * from information_schema.tables where table_name=%s)", - ('django_celery_beat_periodictask',) - ) - if cur.fetchone()[0] == 1: - cur.close() - sys.exit(0) - else: - cur.close() - sys.exit(-1) -except psycopg2.OperationalError: - sys.exit(-1) - -END -} -until postgres_ready; do - >&2 echo 'Waiting for celerybeat models to be migrated...' - sleep 1 -done ->&2 echo 'PostgreSQL is ready' - celery -A config.celery_app beat -l INFO diff --git a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start index 0cc8ae8a..20b93123 100644 --- a/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start +++ b/{{cookiecutter.project_slug}}/compose/production/django/celery/beat/start @@ -4,41 +4,5 @@ set -o errexit set -o pipefail set -o nounset -postgres_ready() { -python << END -import sys -import psycopg2 -try: - conn = psycopg2.connect( - dbname="${POSTGRES_DB}", - user="${POSTGRES_USER}", - password="${POSTGRES_PASSWORD}", - host="${POSTGRES_HOST}", - port="${POSTGRES_PORT}", - ) - # Check if table exists yet. - # If not, wait for docker-compose up to migrate all tables. - cur = conn.cursor() - cur.execute( - "select exists(select * from information_schema.tables where table_name=%s)", - ('django_celery_beat_periodictask',) - ) - if cur.fetchone()[0] == 1: - cur.close() - sys.exit(0) - else: - cur.close() - sys.exit(-1) -except psycopg2.OperationalError: - sys.exit(-1) - -sys.exit(0) -END -} -until postgres_ready; do - >&2 echo 'Waiting for celerybeat models to be migrated...' - sleep 1 -done ->&2 echo 'PostgreSQL is ready' celery -A config.celery_app beat -l INFO From d249158444d1cb0f32cbc9cdd6dc868dcc2210d7 Mon Sep 17 00:00:00 2001 From: Andrew-Chen-Wang Date: Thu, 16 Apr 2020 14:15:35 -0400 Subject: [PATCH 5/5] Fix .travis.yml indent alignment --- {{cookiecutter.project_slug}}/.travis.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/{{cookiecutter.project_slug}}/.travis.yml b/{{cookiecutter.project_slug}}/.travis.yml index fac60650..24f9f24a 100644 --- a/{{cookiecutter.project_slug}}/.travis.yml +++ b/{{cookiecutter.project_slug}}/.travis.yml @@ -15,7 +15,7 @@ jobs: - "flake8" - name: "Django Test" - {% if cookiecutter.use_docker == 'y' -%} + {%- if cookiecutter.use_docker == 'y' %} before_script: - docker-compose -v - docker -v