diff --git a/cookiecutter.json b/cookiecutter.json index 68206147..4a580036 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -56,7 +56,8 @@ "ci_tool": [ "None", "Travis", - "Gitlab" + "Gitlab", + "Github" ], "keep_local_envs_in_vcs": "y", "debug": "n" diff --git a/docs/project-generation-options.rst b/docs/project-generation-options.rst index ec52e218..8a81e19d 100644 --- a/docs/project-generation-options.rst +++ b/docs/project-generation-options.rst @@ -119,6 +119,7 @@ ci_tool: 1. None 2. `Travis CI`_ 3. `Gitlab CI`_ + 4. `Github Actions`_ keep_local_envs_in_vcs: Indicates whether the project's ``.envs/.local/`` should be kept in VCS @@ -176,3 +177,4 @@ debug: .. _GitLab CI: https://docs.gitlab.com/ee/ci/ +.. _Github Actions: https://docs.github.com/en/actions diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index fae73e11..ede14c32 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -123,6 +123,10 @@ def remove_dotgitlabciyml_file(): os.remove(".gitlab-ci.yml") +def remove_dotgithub_folder(): + shutil.rmtree(".github") + + def append_to_project_gitignore(path): gitignore_file_path = ".gitignore" with open(gitignore_file_path, "a") as gitignore_file: @@ -395,6 +399,9 @@ def main(): if "{{ cookiecutter.ci_tool }}".lower() != "gitlab": remove_dotgitlabciyml_file() + if "{{ cookiecutter.ci_tool }}".lower() != "github": + remove_dotgithub_folder() + if "{{ cookiecutter.use_drf }}".lower() == "n": remove_drf_starter_files() diff --git a/tests/test_bare.sh b/tests/test_bare.sh index 28f9b7bf..cc1f1c36 100755 --- a/tests/test_bare.sh +++ b/tests/test_bare.sh @@ -1,7 +1,7 @@ #!/bin/sh # this is a very simple script that tests the docker configuration for cookiecutter-django # it is meant to be run from the root directory of the repository, eg: -# sh tests/test_docker.sh +# sh tests/test_bare.sh set -o errexit diff --git a/tests/test_cookiecutter_generation.py b/tests/test_cookiecutter_generation.py index f9bfcd53..af6e4588 100755 --- a/tests/test_cookiecutter_generation.py +++ b/tests/test_cookiecutter_generation.py @@ -96,6 +96,7 @@ SUPPORTED_COMBINATIONS = [ {"ci_tool": "None"}, {"ci_tool": "Travis"}, {"ci_tool": "Gitlab"}, + {"ci_tool": "Github"}, {"keep_local_envs_in_vcs": "y"}, {"keep_local_envs_in_vcs": "n"}, {"debug": "y"}, @@ -138,6 +139,7 @@ def check_paths(paths): @pytest.mark.parametrize("context_override", SUPPORTED_COMBINATIONS, ids=_fixture_id) def test_project_generation(cookies, context, context_override): """Test that project is generated and fully rendered.""" + result = cookies.bake(extra_context={**context, **context_override}) assert result.exit_code == 0 assert result.exception is None @@ -225,6 +227,42 @@ def test_gitlab_invokes_flake8_and_pytest( pytest.fail(e) +@pytest.mark.parametrize( + ["use_docker", "expected_test_script"], + [ + ("n", "pytest"), + ("y", "docker-compose -f local.yml exec -T django pytest"), + ], +) +def test_github_invokes_flake8_and_pytest( + cookies, context, use_docker, expected_test_script +): + context.update({"ci_tool": "Github", "use_docker": use_docker}) + result = cookies.bake(extra_context=context) + + assert result.exit_code == 0 + assert result.exception is None + assert result.project.basename == context["project_slug"] + assert result.project.isdir() + + with open(f"{result.project}/.github/workflows/ci.yml", "r") as github_yml: + try: + github_config = yaml.safe_load(github_yml) + flake8_present = False + for action_step in github_config["jobs"]["flake8"]["steps"]: + if action_step.get("run") == "flake8": + flake8_present = True + assert flake8_present + + expected_test_script_present = False + for action_step in github_config["jobs"]["pytest"]["steps"]: + if action_step.get("run") == expected_test_script: + expected_test_script_present = True + assert expected_test_script_present + except yaml.YAMLError as e: + pytest.fail(e) + + @pytest.mark.parametrize("slug", ["project slug", "Project_Slug"]) def test_invalid_slug(cookies, context, slug): """Invalid slug should failed pre-generation hook.""" diff --git a/{{cookiecutter.project_slug}}/.github/dependabot.yml b/{{cookiecutter.project_slug}}/.github/dependabot.yml new file mode 100644 index 00000000..8e8ac866 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/dependabot.yml @@ -0,0 +1,7 @@ +version: 2 +updates: + # Update Github actions in workflows + - package-ecosystem: "github-actions" + directory: "/" + schedule: + interval: "daily" diff --git a/{{cookiecutter.project_slug}}/.github/workflows/ci.yml b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml new file mode 100644 index 00000000..52818e18 --- /dev/null +++ b/{{cookiecutter.project_slug}}/.github/workflows/ci.yml @@ -0,0 +1,95 @@ +name: CI + +# Enable Buildkit and let compose use it to speed up image building +env: + DOCKER_BUILDKIT: 1 + COMPOSE_DOCKER_CLI_BUILD: 1 + +on: + pull_request: + branches: [ "master" ] + paths-ignore: [ "docs/**" ] + + push: + branches: [ "master" ] + paths-ignore: [ "docs/**" ] + + +jobs: + flake8: + runs-on: ubuntu-latest + steps: + + - name: Checkout Code Repository + uses: actions/checkout@v2 + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Install flake8 + run: | + python -m pip install --upgrade pip + pip install flake8 + + - name: Lint with flake8 + run: flake8 + +# With no caching at all the entire ci process takes 4m 30s to complete! + pytest: + runs-on: ubuntu-latest + steps: + + - name: Checkout Code Repository + uses: actions/checkout@v2 + {% if cookiecutter.use_docker == 'y' -%} + + - name: Build the Stack + run: docker-compose -f local.yml build + + - name: Make DB Migrations + run: docker-compose -f local.yml run --rm django python manage.py migrate + + - name: Run the Stack + run: docker-compose -f local.yml up -d + + - name: Run Django Tests + run: docker-compose -f local.yml exec -T django pytest + + - name: Tear down the Stack + run: docker-compose down + + {%- else %} + + - name: Set up Python 3.8 + uses: actions/setup-python@v2 + with: + python-version: 3.8 + + - name: Get pip cache dir + id: pip-cache-location + run: | + echo "::set-output name=dir::$(pip cache dir)" + + {% raw %} + - name: Cache pip Project Dependencies + uses: actions/cache@v2 + with: + # Get the location of pip cache dir + path: ${{ steps.pip-cache-location.outputs.dir }} + # Look to see if there is a cache hit for the corresponding requirements file + key: ${{ runner.os }}-pip-${{ hashFiles('**/local.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + {% endraw %} + + - name: Install Dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements/local.txt + + - name: Test with pytest + run: pytest + + {%- endif %}