Compare commits
20 Commits
drew/add-c
...
master
Author | SHA1 | Date |
---|---|---|
Drew Bednar | 4ddd52d37d | 1 year ago |
Drew Bednar | 4fa49fb720 | 1 year ago |
Drew Bednar | c685140890 | 1 year ago |
Drew Bednar | c48d5ba13f | 1 year ago |
Drew Bednar | 66f1df5809 | 1 year ago |
Drew Bednar | e87ba2f3f2 | 1 year ago |
Drew Bednar | 46040764bb | 1 year ago |
Drew Bednar | 8761dbff80 | 1 year ago |
Drew Bednar | cb137d792d | 1 year ago |
Drew Bednar | df128519c7 | 1 year ago |
Drew Bednar | 5544977c96 | 1 year ago |
Drew Bednar | 06dc9fd451 | 1 year ago |
Drew Bednar | 47cc8af0cb | 1 year ago |
Drew Bednar | 7490ed0d3f | 1 year ago |
Drew Bednar | 58763b4eab | 1 year ago |
Drew Bednar | fc6977cf46 | 1 year ago |
Drew Bednar | f72e0082d8 | 1 year ago |
Drew Bednar | 1d544043f8 | 1 year ago |
Drew Bednar | 4859f8a570 | 1 year ago |
Drew Bednar | 6cab703805 | 1 year ago |
@ -1,3 +1,30 @@
|
|||||||
# cookiecutter-basic
|
# cookiecutter-basic
|
||||||
|
|
||||||
A basic cookiecutter template for most Androiddrew style repos.
|
A basic [Cookiecutter](https://cookiecutter.readthedocs.io/en/stable) 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,17 +1,18 @@
|
|||||||
{
|
{
|
||||||
"full_name": "Drew Bednar",
|
"full_name": "Drew Bednar",
|
||||||
"email": "drew@androiddrew.com",
|
"email": "drew@runcible.io",
|
||||||
"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": "An templated Python project",
|
"description": "A templated Python project",
|
||||||
|
"image_registry": "registry.runcible.io",
|
||||||
"open_source_license": [
|
"open_source_license": [
|
||||||
"MIT license",
|
"MIT",
|
||||||
"BSD license",
|
"BSD-3-Clause",
|
||||||
"ISC license",
|
"ISC",
|
||||||
"Apache Software License 2.0",
|
"Apache-2.0",
|
||||||
"GNU General Public License v3",
|
"GPL-3.0-or-later",
|
||||||
"Not open source"
|
"Not open source"
|
||||||
]
|
]
|
||||||
}
|
}
|
@ -0,0 +1,14 @@
|
|||||||
|
./tests
|
||||||
|
./scripts
|
||||||
|
.ruff_cache
|
||||||
|
.coveragerc
|
||||||
|
.dockerignore
|
||||||
|
.git
|
||||||
|
.gitignore
|
||||||
|
.pre-commit-config.yaml
|
||||||
|
dev-requirements.in
|
||||||
|
dev-requirements.txt
|
||||||
|
.profile
|
||||||
|
Dockerfile
|
||||||
|
requirements.in
|
||||||
|
tasks.py
|
@ -1,23 +0,0 @@
|
|||||||
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
|
|
@ -0,0 +1,44 @@
|
|||||||
|
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
|
@ -0,0 +1,55 @@
|
|||||||
|
# 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"]
|
@ -0,0 +1,8 @@
|
|||||||
|
#!/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 "$@"
|
@ -0,0 +1,28 @@
|
|||||||
|
#! /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"
|
@ -0,0 +1,52 @@
|
|||||||
|
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