diff --git a/cookiecutter.json b/cookiecutter.json index beeac2e..349add2 100644 --- a/cookiecutter.json +++ b/cookiecutter.json @@ -6,6 +6,7 @@ "repo_name": "{{cookiecutter.__client_name_slug}}-{{cookiecutter.__project_name_slug}}", "ci": [ "GitLab", + "Github", "None" ], "jupytext": [ @@ -13,5 +14,8 @@ "Yes" ], "python_package_name": "{{ cookiecutter.__project_name_slug.replace('-', '_') }}", - "__package_name": "{{ cookiecutter.python_package_name }}" + "__package_name": "{{ cookiecutter.python_package_name }}", + "_copy_without_render": [ + ".github/workflows/*.yml" + ] } \ No newline at end of file diff --git a/hooks/post_gen_project.py b/hooks/post_gen_project.py index 4a9635b..37a1eda 100644 --- a/hooks/post_gen_project.py +++ b/hooks/post_gen_project.py @@ -1,5 +1,7 @@ from pathlib import Path import stat +import shutil + print("Running post generation...") ci = "{{ cookiecutter.ci }}" @@ -7,13 +9,22 @@ REMOVE_PATHS = [] gitlab_files = [ - ".gitlab-ci.yml" + ".gitlab-ci.yml", + "docker/precommit" +] + +github_files = [ + ".github/", ] {% if cookiecutter.ci != "GitLab" %} REMOVE_PATHS.extend(gitlab_files) {% endif %} +{% if cookiecutter.ci != "Github" %} +REMOVE_PATHS.extend(github_files) +{% endif %} + {% if cookiecutter.jupytext != "Yes" %} REMOVE_PATHS.extend(["notebooks/example.py"]) {% endif %} @@ -24,10 +35,13 @@ if path.exists() and path.is_file(): print(f"Clean up file: '{path}'") path.unlink() + elif path.exists() and path.is_dir(): + print(f"Clean up directory: '{path}'") + shutil.rmtree(path) -#Solves problems when template fails to keep linux permissions. (e.g. after zipping template) +# Solves problems when template fails to keep linux permissions. (e.g. after zipping template) print("Updating permissions... 🚀") for path in Path("").rglob("*.sh"): path.chmod(path.stat().st_mode | stat.S_IXUSR) - + print("DONE 🎆") \ No newline at end of file diff --git a/tests/test_template.py b/tests/test_template.py index 5c7757d..dbfdeeb 100644 --- a/tests/test_template.py +++ b/tests/test_template.py @@ -113,6 +113,33 @@ def test_template_project_with_gitlab(cookies): assert (rpath / ".gitlab-ci.yml").exists() is True +def test_template_project_no_github(cookies): + result = cookies.bake(extra_context={ + "client_name": "no", + "project_name": "gitlab", + "ci": "None" + }) + assert result.exit_code == 0 + assert result.exception is None + + rpath: Path = result.project_path + assert (rpath / ".github").exists() is False + + +def test_template_project_with_github(cookies): + result = cookies.bake(extra_context={ + "client_name": "with", + "project_name": "gitlab", + "ci": "Github" + }) + assert result.exit_code == 0 + assert result.exception is None + + rpath: Path = result.project_path + assert (rpath / ".gitlab-ci.yml").exists() is False + assert (rpath / ".github").exists() is True + + def test_template_project_with_jupytext(cookies): result = cookies.bake(extra_context={ "client_name": "no", diff --git a/{{ cookiecutter.repo_name }}/.github/workflows/ci.yml b/{{ cookiecutter.repo_name }}/.github/workflows/ci.yml new file mode 100644 index 0000000..f8dc9d9 --- /dev/null +++ b/{{ cookiecutter.repo_name }}/.github/workflows/ci.yml @@ -0,0 +1,127 @@ +name: Continuous Integration + +on: + push: + branches: [main, master] + pull_request: + +jobs: + lints: + name: Run linters + runs-on: ubuntu-latest + timeout-minutes: 10 + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v4 + with: + python-version: "3.11" + + - name: Cache pre-commit + uses: actions/cache@v3 + with: + path: ~/.cache/pre-commit + key: pre-commit-3|${{ env.pythonLocation }}|${{ hashFiles('.pre-commit-config.yaml') }} + + - name: Install pre-commit + run: pip3 install pre-commit + + - name: Run pre-commit checks + run: pre-commit run --all-files --show-diff-on-failure --color always + + - name: Run Trivy vulnerability scanner + uses: aquasecurity/trivy-action@master + with: + scan-type: "fs" + ignore-unfixed: true + format: "table" + severity: "CRITICAL,HIGH" + output: "trivy-scanning-results.txt" + + - name: Create venv + run: . ./setup_dev_env.sh + + - name: Check licenses + run: ./check_licenses.sh + + - name: Generate pip freeze + run: | + source venv/bin/activate + pip freeze > requirements-freeze.txt + + - name: Publish Artefacts + uses: actions/upload-artifact@v3 + if: always() + with: + name: results + path: | + requirements-freeze.txt + licenses.txt + trivy-scanning-results.txt + retention-days: 30 + + - name: Publish Test Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: test-report + path: report.xml + retention-days: 10 + + - name: Validate package build + run: | + source venv/bin/activate + python -m pip install -U build + python -m build + + - name: Publish Package + uses: actions/upload-artifact@v3 + if: always() + with: + name: packages + path: dist/** + retention-days: 10 + + tests: + name: Run tests + runs-on: ubuntu-latest + timeout-minutes: 10 + strategy: + fail-fast: false # do not stop all jobs if one fails + matrix: + include: + - python-version: "3.11" + steps: + - uses: actions/checkout@v4 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v4 + with: + python-version: ${{ matrix.python-version }} + + - name: Cache Dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + - name: Install Dependencies + run: pip install -r requirements-dev.txt + + - name: Run Tests + run: pytest -v -p no:warnings --junitxml=report.xml tests/ + + - name: Test Report + uses: mikepenz/action-junit-report@v4 + if: always() + with: + report_paths: 'report.xml' + + - name: Publish Test Report + uses: actions/upload-artifact@v3 + if: always() + with: + name: test-report + path: report.xml + retention-days: 10 + diff --git a/{{ cookiecutter.repo_name }}/.github/workflows/documentation.yml b/{{ cookiecutter.repo_name }}/.github/workflows/documentation.yml new file mode 100644 index 0000000..417d0c1 --- /dev/null +++ b/{{ cookiecutter.repo_name }}/.github/workflows/documentation.yml @@ -0,0 +1,35 @@ +name: Build documentation + +on: + push: + branches: [main, master] + +jobs: + pages: + runs-on: ubuntu-latest + container: python:3.11 + + steps: + - uses: actions/checkout@v4 + + - name: Cache Dependencies + uses: actions/cache@v3 + with: + path: ~/.cache/pip + key: ${{ runner.os }}-pip-${{ hashFiles('**/requirements-dev.txt') }} + restore-keys: | + ${{ runner.os }}-pip- + + # for best results, it is better to generate + # documentation within development environment + - name: Create venv + run: . ./setup_dev_env.sh + + - name: Build docs + run: ./build_docs.sh + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: ./public \ No newline at end of file diff --git a/{{ cookiecutter.repo_name }}/README.md b/{{ cookiecutter.repo_name }}/README.md index 5f85c22..2aaa1db 100644 --- a/{{ cookiecutter.repo_name }}/README.md +++ b/{{ cookiecutter.repo_name }}/README.md @@ -75,9 +75,23 @@ Only people with repository access can view it. Please read more about it [here](https://docs.gitlab.com/ee/user/project/pages/index.html). -{% endif %} +{%- endif -%} + +{%- if cookiecutter.ci == "Github" %} + +### Github Actions Documentation + +By default **Github Actions** pipelines have `documentation` workflow which will build sphinx documentation automatically on main branch - and it will push it to a branch - it can be hosted on **Github Pages** if you enable it. + +To access it, you need to enable it, on **Github repository -> Settings -> Pages** page select **Deploy from a branch** and select **gh-pages**. Link will appear here after deployment. -{% if cookiecutter.jupytext == "Yes" %} +**WARNING:** Only on Github Enterprise you can make it private so only people with repository access can view it. + +Please read more about it [here](https://docs.github.com/en/pages/quickstart). + +{%- endif -%} + +{%- if cookiecutter.jupytext == "Yes" %} # Jupyter notebooks and jupytext @@ -88,7 +102,7 @@ There is pre-commit hook which automatically generates and syncs notebooks with Please ensure you do not edit/modify manually or by other means generated py:percent files as they will conflict with jupytext change detection and lead to endless loop. Treat them as read-only files and edit only notebooks. -{% endif %} +{%- endif -%} # Semantic version bump