Compare commits
No commits in common. 'master' and 'drew/add-ci-setup' have entirely different histories.
master
...
drew/add-c
@ -1,30 +1,3 @@
|
|||||||
# cookiecutter-basic
|
# cookiecutter-basic
|
||||||
|
|
||||||
A basic [Cookiecutter](https://cookiecutter.readthedocs.io/en/stable) template for most Androiddrew style repos.
|
A basic cookiecutter template for most Androiddrew style repos.
|
||||||
|
|
||||||
## Usage
|
|
||||||
|
|
||||||
With [Cookiecutter installed](https://cookiecutter.readthedocs.io/en/stable/installation.html), execute the following command and provide your template parameters.
|
|
||||||
|
|
||||||
```
|
|
||||||
cookiecutter https://git.runcible.io/androiddrew/cookiecutter-basic
|
|
||||||
```
|
|
||||||
|
|
||||||
Once templated, you can change into the new project directory and execute the `scripts/bootstrap.sh` script.
|
|
||||||
|
|
||||||
```
|
|
||||||
cd <cookiecutter.project_slug>
|
|
||||||
./scripts/bootstrap.sh
|
|
||||||
```
|
|
||||||
|
|
||||||
This will create a Python virtual environment if one is not already enabled, install [pip-tools](https://github.com/jazzband/pip-tools/), compile the requirements.in and dev-requirements.in files, install the initial dependencies, initialize the project as a git repository, and install the [pre-commit hooks](https://pre-commit.com/).
|
|
||||||
|
|
||||||
## Licenses
|
|
||||||
|
|
||||||
Within a `pyproject.toml` file the license section expects the string identifier as determined by https://spdx.org/licenses/. This project supports a small subset of these licenses.
|
|
||||||
|
|
||||||
Example:
|
|
||||||
```
|
|
||||||
[project]
|
|
||||||
license = {text = "MIT"}
|
|
||||||
```
|
|
@ -1,18 +1,17 @@
|
|||||||
{
|
{
|
||||||
"full_name": "Drew Bednar",
|
"full_name": "Drew Bednar",
|
||||||
"email": "drew@runcible.io",
|
"email": "drew@androiddrew.com",
|
||||||
"github_username": "androiddrew",
|
"github_username": "androiddrew",
|
||||||
"project_name": "python_app",
|
"project_name": "python_app",
|
||||||
"project_slug": "{{ cookiecutter.project_name.lower().strip().replace(' ', '_').replace('-','_')}}",
|
"project_slug": "{{ cookiecutter.project_name.lower().strip().replace(' ', '_').replace('-','_')}}",
|
||||||
"initial_dependencies": "",
|
"initial_dependencies": "",
|
||||||
"description": "A templated Python project",
|
"description": "An templated Python project",
|
||||||
"image_registry": "registry.runcible.io",
|
|
||||||
"open_source_license": [
|
"open_source_license": [
|
||||||
"MIT",
|
"MIT license",
|
||||||
"BSD-3-Clause",
|
"BSD license",
|
||||||
"ISC",
|
"ISC license",
|
||||||
"Apache-2.0",
|
"Apache Software License 2.0",
|
||||||
"GPL-3.0-or-later",
|
"GNU General Public License v3",
|
||||||
"Not open source"
|
"Not open source"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -1,14 +0,0 @@
|
|||||||
./tests
|
|
||||||
./scripts
|
|
||||||
.ruff_cache
|
|
||||||
.coveragerc
|
|
||||||
.dockerignore
|
|
||||||
.git
|
|
||||||
.gitignore
|
|
||||||
.pre-commit-config.yaml
|
|
||||||
dev-requirements.in
|
|
||||||
dev-requirements.txt
|
|
||||||
.profile
|
|
||||||
Dockerfile
|
|
||||||
requirements.in
|
|
||||||
tasks.py
|
|
@ -0,0 +1,23 @@
|
|||||||
|
kind: pipeline
|
||||||
|
type: docker
|
||||||
|
name: CI Test Pipeline
|
||||||
|
|
||||||
|
steps:
|
||||||
|
- name: greeting
|
||||||
|
image: alpine
|
||||||
|
commands:
|
||||||
|
- echo "Welcome to drone\n"
|
||||||
|
|
||||||
|
- name: Unit Tests
|
||||||
|
image: python:3.11-bullseye
|
||||||
|
commands:
|
||||||
|
- bash -c './ci_scripts/run_unit_tests.sh'
|
||||||
|
|
||||||
|
trigger:
|
||||||
|
event:
|
||||||
|
- pull_request
|
||||||
|
- push
|
||||||
|
|
||||||
|
# Secrets used to pull private images
|
||||||
|
image_pull_secrets:
|
||||||
|
- dockerconfigjson
|
@ -1,44 +0,0 @@
|
|||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: CI Test/Lint Pipeline
|
|
||||||
|
|
||||||
steps:
|
|
||||||
- name: Unit Tests and Linters
|
|
||||||
# Bullseye because drone runner host OS is using older libseccomp2 causing issues
|
|
||||||
# with thread allocation. See: https://github.com/docker-library/python/issues/835
|
|
||||||
image: python:3.11-bullseye
|
|
||||||
commands:
|
|
||||||
- bash -xc './scripts/run_linters.sh'
|
|
||||||
- bash -xc './scripts/run_unit_tests.sh'
|
|
||||||
group: test-lint
|
|
||||||
|
|
||||||
trigger:
|
|
||||||
event:
|
|
||||||
- pull_request
|
|
||||||
- push
|
|
||||||
|
|
||||||
# Secrets used to pull private images
|
|
||||||
image_pull_secrets:
|
|
||||||
- dockerconfigjson
|
|
||||||
|
|
||||||
---
|
|
||||||
kind: pipeline
|
|
||||||
type: docker
|
|
||||||
name: Build Production Image
|
|
||||||
steps:
|
|
||||||
- name: Build {{ cookiecutter.project_slug }} Container Image
|
|
||||||
image: plugins/docker
|
|
||||||
settings:
|
|
||||||
username: automate
|
|
||||||
password:
|
|
||||||
from_secret: automate_password
|
|
||||||
dockerfile: Dockerfile
|
|
||||||
registry: {{ cookiecutter.image_registry }}
|
|
||||||
repo: {{ cookiecutter.image_registry }}/{{ cookiecutter.project_slug }}
|
|
||||||
tags:
|
|
||||||
- ${DRONE_COMMIT_SHA}
|
|
||||||
when:
|
|
||||||
branch:
|
|
||||||
- master
|
|
||||||
event:
|
|
||||||
- push
|
|
@ -1,55 +0,0 @@
|
|||||||
# syntax = docker/dockerfile:1.4
|
|
||||||
|
|
||||||
# Best practice: Choose a stable base image and tag.
|
|
||||||
FROM python:3.11-slim-bookworm
|
|
||||||
|
|
||||||
# Install security updates, and some useful packages.
|
|
||||||
#
|
|
||||||
# Best practices:
|
|
||||||
# * Make sure apt-get doesn't run in interactive mode.
|
|
||||||
# * Update system packages.
|
|
||||||
# * Pre-install some useful tools.
|
|
||||||
# * Minimize system package installation.
|
|
||||||
RUN export DEBIAN_FRONTEND=noninteractive && \
|
|
||||||
apt-get update && \
|
|
||||||
apt-get -y upgrade && \
|
|
||||||
apt-get install -y --no-install-recommends tini procps net-tools && \
|
|
||||||
apt-get -y clean && \
|
|
||||||
rm -rf /var/lib/apt/lists/*
|
|
||||||
|
|
||||||
# Install dependencies.
|
|
||||||
#
|
|
||||||
# Best practices:
|
|
||||||
# * `COPY` in files only when needed.
|
|
||||||
# * Reduce disk usage from `pip` installs.
|
|
||||||
COPY requirements.txt .
|
|
||||||
RUN pip install --no-cache-dir -r requirements.txt
|
|
||||||
|
|
||||||
# Create a new user to run as.
|
|
||||||
#
|
|
||||||
# Best practices: Don't run as root.
|
|
||||||
RUN useradd --create-home appuser
|
|
||||||
USER appuser
|
|
||||||
WORKDIR /home/appuser
|
|
||||||
|
|
||||||
# Copy in the code.
|
|
||||||
#
|
|
||||||
# Best practices: Avoid extra chowns.
|
|
||||||
COPY --chown=appuser . .
|
|
||||||
|
|
||||||
# Best practices: Prepare for C crashes.
|
|
||||||
ENV PYTHONFAULTHANDLER=1
|
|
||||||
ENV PYTHONUNBUFFERED=0
|
|
||||||
|
|
||||||
ARG COMMIT_SHA
|
|
||||||
|
|
||||||
LABEL io.runcible.repo-sha="${COMMIT_SHA}"
|
|
||||||
|
|
||||||
# Run the code when the image is run:
|
|
||||||
#
|
|
||||||
# Best practices:
|
|
||||||
# * Add an `init` process.
|
|
||||||
# * Make sure images shut down correctly (via ENTRYPOINT [] syntax).
|
|
||||||
# * '-g' option means killing the container kills all processes, not just the
|
|
||||||
# entrypoint shell.
|
|
||||||
ENTRYPOINT ["tini", "-g", "--", "./entrypoint.sh"]
|
|
@ -1,8 +0,0 @@
|
|||||||
#!/bin/bash
|
|
||||||
|
|
||||||
# Best practice: Bash strict mode.
|
|
||||||
set -euo pipefail
|
|
||||||
|
|
||||||
# Best practice: Make sure the image shuts down correctly by using `exec` in
|
|
||||||
# entry point shell scripts.
|
|
||||||
exec "$@"
|
|
@ -1,28 +0,0 @@
|
|||||||
#! /usr/bin/env bash
|
|
||||||
|
|
||||||
# shellcheck source=/dev/null
|
|
||||||
source "$(dirname "$0")/_common.sh"
|
|
||||||
|
|
||||||
if [ "${DRONE}" == "true" ]; then
|
|
||||||
_setup_env
|
|
||||||
pip install -r requirements.txt -r dev-requirements.txt
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Run linting commands and capture their return codes
|
|
||||||
"${VIRTUAL_ENV}/bin/python3" -m isort --check ./speech_collect ./tests ./tasks.py
|
|
||||||
ISORT_EXIT_CODE=$?
|
|
||||||
|
|
||||||
"${VIRTUAL_ENV}/bin/python3" -m black --check ./speech_collect ./tests ./tasks.py
|
|
||||||
BLACK_EXIT_CODE=$?
|
|
||||||
|
|
||||||
"${VIRTUAL_ENV}/bin/python3" -m ruff ./speech_collect ./tests ./tasks.py
|
|
||||||
RUFF_EXIT_CODE=$?
|
|
||||||
|
|
||||||
# Check if any linting command failed
|
|
||||||
if [ $ISORT_EXIT_CODE -ne 0 ] || [ $BLACK_EXIT_CODE -ne 0 ] || [ $RUFF_EXIT_CODE -ne 0 ]; then
|
|
||||||
echo "Some linting checks failed"
|
|
||||||
# Exit with a non-zero status, you can choose which error code to return
|
|
||||||
exit 1
|
|
||||||
fi
|
|
||||||
|
|
||||||
echo "All linting checks passed"
|
|
@ -1,52 +0,0 @@
|
|||||||
import os
|
|
||||||
|
|
||||||
from invoke import task
|
|
||||||
|
|
||||||
IMAGE_RESPOSITORY = os.environ.get("IMAGE_RESPOSITORY", "{{ cookiecutter.image_registry }}/{{ cookiecutter.project_slug }}")
|
|
||||||
|
|
||||||
|
|
||||||
@task
|
|
||||||
def update_deps(c):
|
|
||||||
"""Updates depenencies"""
|
|
||||||
c.run("pip-compile requirements.in", pty=True)
|
|
||||||
c.run("pip-compile dev-requirements.in", pty=True)
|
|
||||||
|
|
||||||
|
|
||||||
@task
|
|
||||||
def sync_deps(c):
|
|
||||||
"""Syncs local dependencies"""
|
|
||||||
c.run("pip-sync requirements.txt dev-requirements.txt")
|
|
||||||
|
|
||||||
|
|
||||||
@task
|
|
||||||
def lint(c):
|
|
||||||
"""Runs all linters against the project."""
|
|
||||||
c.run("./scripts/run_linters.sh", pty=True)
|
|
||||||
|
|
||||||
|
|
||||||
@task
|
|
||||||
def delint(c):
|
|
||||||
"""Applies automated linters to project"""
|
|
||||||
c.run("isort ./{{ cookiecutter.project_slug }} ./tests ./tasks.py", pty=True)
|
|
||||||
c.run("black ./{{ cookiecutter.project_slug }} ./tests ./tasks.py", pty=True)
|
|
||||||
|
|
||||||
|
|
||||||
@task
|
|
||||||
def build(c):
|
|
||||||
"""Builds the project as a Python package."""
|
|
||||||
c.run("python3 -m build")
|
|
||||||
|
|
||||||
|
|
||||||
@task
|
|
||||||
def build_image(c, dev=True, registry_user=None, registry_token=None, push=False, login=False):
|
|
||||||
"""Builds the {{ cookiecutter.project_slug }} container image."""
|
|
||||||
context_dir = c.run("pwd", hide=True).stdout.strip()
|
|
||||||
commit_sha = c.run("git rev-parse --short HEAD", hide=True).stdout.strip()
|
|
||||||
image_name = f"{IMAGE_RESPOSITORY}:{commit_sha}{'-dev' if dev else ''}"
|
|
||||||
c.run(f"docker build --build-arg='COMMIT_SHA={commit_sha}' -t {image_name} {context_dir}")
|
|
||||||
if login:
|
|
||||||
if registry_user is None or registry_token is None:
|
|
||||||
raise ValueError("--registry_user and --registry_token must be provided if using --login parameter")
|
|
||||||
c.run(f"docker login -u {registry_user} -p {registry_token}")
|
|
||||||
if push:
|
|
||||||
c.run(f"docker push {image_name}")
|
|
Loading…
Reference in New Issue