Skip to content

Commit

Permalink
Add Dockerization (#43)
Browse files Browse the repository at this point in the history
* Restructure repository files
* Add docker setup
* Add github action
* Use env variables 
* Add Intellij IDEA dir to .gitignore
  • Loading branch information
Freddo3000 authored Oct 15, 2023
1 parent 085a63d commit 2c04535
Show file tree
Hide file tree
Showing 42 changed files with 255 additions and 32 deletions.
100 changes: 100 additions & 0 deletions .github/workflows/docker-publish.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
name: Docker

# This workflow uses actions that are not certified by GitHub.
# They are provided by a third-party and are governed by
# separate terms of service, privacy policy, and support
# documentation.

on:
schedule:
- cron: '36 21 * * *'
push:
branches: [ "main" ]
# Publish semver tags as releases.
tags: [ 'v*.*.*' ]
pull_request:
branches: [ "main" ]
workflow_dispatch:

env:
# Use docker.io for Docker Hub if empty
REGISTRY: ghcr.io
# github.repository as <account>/<repo>
IMAGE_NAME: ${{ github.repository }}


jobs:
build:

runs-on: ubuntu-latest
permissions:
contents: read
packages: write
# This is used to complete the identity challenge
# with sigstore/fulcio when running outside of PRs.
id-token: write

steps:
- name: Checkout repository
uses: actions/checkout@v3
with:
submodules: recursive

# Install the cosign tool except on PR
# https://github.com/sigstore/cosign-installer
- name: Install cosign
if: github.event_name != 'pull_request'
uses: sigstore/cosign-installer@6e04d228eb30da1757ee4e1dd75a0ec73a653e06 #v3.1.1
with:
cosign-release: 'v2.1.1'

# Workaround: https://github.com/docker/build-push-action/issues/461
- name: Setup Docker buildx
uses: docker/setup-buildx-action@79abd3f86f79a9d68a23c75a09a9a85889262adf

# Login against a Docker registry except on PR
# https://github.com/docker/login-action
- name: Log into registry ${{ env.REGISTRY }}
if: github.event_name != 'pull_request'
uses: docker/login-action@28218f9b04b4f3f62068d7b6ce6ca5b26e35336c
with:
registry: ${{ env.REGISTRY }}
username: ${{ github.actor }}
password: ${{ secrets.GITHUB_TOKEN }}

# Extract metadata (tags, labels) for Docker
# https://github.com/docker/metadata-action
- name: Extract Docker metadata
id: meta
uses: docker/metadata-action@98669ae865ea3cffbcbaa878cf57c20bbf1c6c38
with:
images: ${{ env.REGISTRY }}/${{ env.IMAGE_NAME }}

# Build and push Docker image with Buildx (don't push on PR)
# https://github.com/docker/build-push-action
- name: Build and push Docker image
id: build-and-push
uses: docker/build-push-action@ac9327eae2b366085ac7f6a2d02df8aa8ead720a
with:
context: .
push: ${{ github.event_name != 'pull_request' }}
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
cache-from: type=gha
cache-to: type=gha,mode=max


# Sign the resulting Docker image digest except on PRs.
# This will only write to the public Rekor transparency log when the Docker
# repository is public to avoid leaking data. If you would like to publish
# transparency data even for private images, pass --force to cosign below.
# https://github.com/sigstore/cosign
- name: Sign the published Docker image
if: ${{ github.event_name != 'pull_request' }}
env:
# https://docs.github.com/en/actions/security-guides/security-hardening-for-github-actions#using-an-intermediate-environment-variable
TAGS: ${{ steps.meta.outputs.tags }}
DIGEST: ${{ steps.build-and-push.outputs.digest }}
# This step uses the identity token to provision an ephemeral certificate
# against the sigstore community Fulcio instance.
run: echo "${TAGS}" | xargs -I {} cosign sign --yes {}@${DIGEST}
6 changes: 4 additions & 2 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
__pycache__
*.pyc
/*.egg-info
/build
/dist
/src/build
/src/dist
/db
*.log
.idea
22 changes: 11 additions & 11 deletions .gitmodules
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
[submodule "bootstrap"]
path = bootstrap
[submodule "src/bootstrap"]
path = src/bootstrap
url = https://github.com/twbs/bootstrap.git
[submodule "EncryptedSession"]
path = EncryptedSession
url = https://github.com/SaintFlipper/EncryptedSession.git
[submodule "gnucash"]
path = gnucash
[submodule "src/EncryptedSession"]
path = src/EncryptedSession
url = https://github.com/SaintFlipper/EncryptedSession.git
[submodule "src/gnucash"]
path = src/gnucash
url = https://github.com/Gnucash/gnucash
[submodule "selectize"]
path = selectize
[submodule "src/selectize"]
path = src/selectize
url = https://github.com/selectize/selectize.js.git
[submodule "bootstrap-icons"]
path = bootstrap-icons
[submodule "src/bootstrap-icons"]
path = src/bootstrap-icons
url = https://github.com/twbs/icons.git
38 changes: 38 additions & 0 deletions Dockerfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
FROM python:alpine AS builder
LABEL authors="freddo"

WORKDIR /srv/

RUN apk add \
gcc \
musl-dev \
mariadb-dev \
python3-dev \
libpq-dev

RUN pip wheel --no-cache-dir --wheel-dir /wheels mysql pycryptodome psycopg2

FROM python:alpine

WORKDIR /srv/

COPY ./src/ ./
COPY ./README.md ./
COPY --from=builder /wheels /wheels

RUN apk add libpq mariadb-client
RUN pip install -v --no-cache /wheels/* .[pgsql,mysql] gunicorn
RUN rm -r /wheels

EXPOSE 8000

ENV SECRET_KEY='00000000000000000000000000000000'
ENV LOG_LEVEL='WARN'
ENV DB_DRIVER='sqlite'
ENV DB_NAME='/gnucash.sqlite'
ENV DB_HOST='localhost'
ENV AUTH_MECHANISM=''
ENV TRANSACTION_PAGE_LENGTH=25
ENV PRESELECTED_CONTRA_ACCOUNT=''

ENTRYPOINT ["gunicorn", "-b", "0.0.0.0", "gnucash_web.wsgi:app"]
24 changes: 24 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -71,6 +71,8 @@ Create a config file in `/etc/gnucash_web/config.py` or in
override those from the former. Set the environment variable `GNUCASH_WEB_CONFIG` to load
a different config file. If set, no other config files are read.

Config variables can also be set via environment variables, with the same available options.

The config file is a python script. The following example illustrates possible values for
all available options. This is the normal Flask configuration file, so all [standard
configuration
Expand Down Expand Up @@ -130,6 +132,28 @@ chmod-socket = 660
vacuum = true
```

#### Docker

*GnuCash Web* can be run using [Docker](https://www.docker.com/), either using the published
[packages](https://github.com/joshuabach/gnucash-web/packages), DockerFile, or using [docker compose](https://docs.docker.com/compose/)
with the provided sample `docker-compose.yml` files. This uses a simple SQLite backend,
though if you want to use a dedicated database backend then `docker-compose-postgres.yml`
is also provided, running [PostgreSQL](https://www.postgresql.org/) as the name implies.

In either case, configuration is done via environment variables in the compose file instead
of the default configuration file. The same options are available.

If you're running a dedicated backend as part of docker compose, then in order to
[initialise the database](#initialising-database) you'll need to
[expose the respective ports](https://docs.docker.com/compose/networking/).
Keep security in mind when exposing ports, such as using a strong password,
as exposing ports may grant any user on the internet access to your database.

If you want to quickly spin up and test gnucash-web, then a sample GnuCash database is
[provided](sample/sample.sqlite) for the SQLite version.

The Docker version runs the [gunicorn](https://gunicorn.org/) WSGI server.

### Initialising database

*GnuCash Web* only works on a preexisting database. It is also currently not possible
Expand Down
32 changes: 32 additions & 0 deletions docker-compose-postgres.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
version: '3.11'

services:
db:
image: postgres:15-alpine
environment:
POSTGRES_USER: 'user'
POSTGRES_DB: 'gnucash'
POSTGRES_PASSWORD: 'example'
volumes:
- db-data:/var/lib/postgresql/data
app:
image: ghcr.io/joshuabach/gnucash-web:main
environment:
SECRET_KEY: '00000000000000000000000000000000'

LOG_LEVEL: 'WARN'

DB_DRIVER: 'postgres'
DB_NAME: 'gnucash'
DB_HOST: 'db'

AUTH_MECHANISM: 'passthrough'

TRANSACTION_PAGE_LENGTH: 25
PRESELECTED_CONTRA_ACCOUNT:

ports:
- "8000:8000"

volumes:
db-data:
22 changes: 22 additions & 0 deletions docker-compose.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
version: '3.11'

services:
app:
image: ghcr.io/joshuabach/gnucash-web:main
environment:
SECRET_KEY: '00000000'

LOG_LEVEL: 'WARN'

DB_DRIVER: 'sqlite'
DB_NAME: '/gnucash.sqlite'

AUTH_MECHANISM:

TRANSACTION_PAGE_LENGTH: 25
PRESELECTED_CONTRA_ACCOUNT:

ports:
- "8000:8000"
volumes:
- ./sample/sample.sqlite:/gnucash.sqlite
14 changes: 0 additions & 14 deletions gnucash_web/config/default.py

This file was deleted.

Binary file added sample/sample.sqlite
Binary file not shown.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
16 changes: 16 additions & 0 deletions src/gnucash_web/config/default.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
"""Default GnuCash Web configuration."""
import logging
import os

SECRET_KEY = bytes.fromhex(os.getenv('SECRET_KEY', '00000000'))

LOG_LEVEL = logging.getLevelName(os.getenv('LOG_LEVEL', 'WARN'))

DB_DRIVER = os.getenv('DB_DRIVER', 'sqlite')
DB_NAME = os.getenv('DB_NAME', 'db/gnucash.sqlite')
DB_HOST = os.getenv('DB_HOST', 'localhost')

AUTH_MECHANISM = os.getenv('AUTH_MECHANISM')

TRANSACTION_PAGE_LENGTH = int(os.getenv('TRANSACTION_PAGE_LENGTH', 25))
PRESELECTED_CONTRA_ACCOUNT = os.getenv('PRESELECTED_CONTRA_ACCOUNT')
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
File renamed without changes.
2 changes: 1 addition & 1 deletion requirements.txt → src/requirements.txt
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
Flask==2.0.2
mysql==0.0.3
piecash==1.2.0
pycryptodome==3.12.0
pycryptodome==3.18.0
babel==2.9.1
requests==2.27.1
11 changes: 7 additions & 4 deletions setup.py → src/setup.py
Original file line number Diff line number Diff line change
@@ -1,13 +1,16 @@
#!python
from setuptools import setup, find_packages
from os.path import exists

readme = open('README.md') if exists('README.md') else open('../README.md')

setup(
name='GnuCash Web',
version='0.1.0',
author='Joshua Bachmeier',
author_email='joshua@bachmeier.cc',
description='A simple, easy to use, mobile-friendly webinterface for GnuCash intended for self-hosting',
long_description=open('README.md').read(),
long_description=readme.read(),
long_description_content_type='text/markdown; charset=UTF-8; variant=GFM',
url='https://github.com/joshuabach/gnucash-web',
project_urls={
Expand Down Expand Up @@ -49,15 +52,15 @@
python_requires=">=3.8",

install_requires=[
'Flask>=2.0.2',
'Flask==2.0.2',
'piecash>=1.2.0',
'pycryptodome>=3.12.0',
'babel>=2.9.1',
'requests>=2.27.1',
],
extras_require={
'PostgreSQL backend': 'psycopg2',
'MySQL / MariaDB backend': 'mysql',
'pgsql': 'psycopg2',
'mysql': 'mysql',
},

entry_points={
Expand Down

0 comments on commit 2c04535

Please sign in to comment.