diff --git a/.dockerignore b/.dockerignore index 5176be8..f2185ce 100644 --- a/.dockerignore +++ b/.dockerignore @@ -1,4 +1,3 @@ fly.toml -.git/ *.sqlite3 .venv \ No newline at end of file diff --git a/.envrc b/.envrc index 5647e87..df70240 100644 --- a/.envrc +++ b/.envrc @@ -1,2 +1,2 @@ -uv sync +uv sync --python 3.13 --no-install-project source .venv/bin/activate \ No newline at end of file diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..39c6e98 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,62 @@ +name: build-image-from-tag +on: + workflow_dispatch: + push: + branches: [main] + pull_request: + branches: [main] +jobs: + build: + # Don't build the image if the registry credentials are not set, the ref is not a tag or it doesn't contain '-v' + if: ${{ vars.REGISTRY_USER != '' && secrets.REGISTRY_PASS != '' && startsWith(github.ref, 'refs/tags/') && contains(github.ref, '-v') }} + runs-on: docker + container: + image: git.badblocks.dev/oci/pkmntrade-club_web:latest + # Mount the dind socket on the container at the default location + options: -v /dind/docker.sock:/var/run/docker.sock + steps: + - name: Extract image name and tag from git and get registry name from env + id: job_data + run: | + echo "::set-output name=img_name::${GITHUB_REF_NAME%%-v*}" + echo "::set-output name=img_tag::${GITHUB_REF_NAME##*-v}" + echo "::set-output name=registry::$( + echo "${{ github.server_url }}" | sed -e 's%https://%%' + )" + echo "::set-output name=oci_registry_prefix::$( + echo "${{ github.server_url }}/oci" | sed -e 's%https://%%' + )" + - name: Checkout the repo + uses: actions/checkout@v4 + - name: Export build dir and Dockerfile + id: build_data + run: | + img="${{ steps.job_data.outputs.img_name }}" + build_dir="$(pwd)/${img}" + dockerfile="${build_dir}/Dockerfile" + if [ -f "$dockerfile" ]; then + echo "::set-output name=build_dir::$build_dir" + echo "::set-output name=dockerfile::$dockerfile" + else + echo "Couldn't find the Dockerfile for the '$img' image" + exit 1 + fi + - name: Login to the Container Registry + uses: docker/login-action@v3 + with: + registry: ${{ steps.job_data.outputs.registry }} + username: ${{ vars.REGISTRY_USER }} + password: ${{ secrets.REGISTRY_PASS }} + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + - name: Build and Push + uses: docker/build-push-action@v6 + with: + push: true + tags: | + ${{ steps.job_data.outputs.oci_registry_prefix }}/${{ steps.job_data.outputs.img_name }}:${{ steps.job_data.outputs.img_tag }} + ${{ steps.job_data.outputs.oci_registry_prefix }}/${{ steps.job_data.outputs.img_name }}:latest + context: ${{ steps.build_data.outputs.build_dir }} + file: ${{ steps.build_data.outputs.dockerfile }} + build-args: | + OCI_REGISTRY_PREFIX=${{ steps.job_data.outputs.oci_registry_prefix }}/ \ No newline at end of file diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..9ff93b5 --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,119 @@ +name: Build, Test, and Deploy + +on: + push: + branches: [main] + pull_request: + branches: [main] + +jobs: + + build: + + runs-on: ubuntu-latest + strategy: + max-parallel: 4 + matrix: + python-version: [ 3.13 ] + database-name: + - test_db + database-password: + - postgres + database-user: + - postgres + database-host: + - 127.0.0.1 + database-port: + - 5432 + + services: + postgres: + image: postgres:latest + env: + POSTGRES_DB: ${{ matrix.database-name }} + POSTGRES_USER: ${{ matrix.database-user }} + POSTGRES_PASSWORD: ${{ matrix.database-password }} + ports: + - 5432:5432 + # Set health checks to wait until postgres has started + options: + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + + + steps: + - uses: actions/checkout@v2.4.0 + - name: Set up Python ${{ matrix.python-version }} + uses: actions/setup-python@v2.3.1 + with: + python-version: ${{ matrix.python-version }} + - name: Install dependencies + run: | + python -m pip install --upgrade pip + pip install -r requirements.txt + - name: Format with black + run: | + pip install black + # format the files with black + black . + - name: Lint with flake8 + run: | + pip install flake8 + # stop the build if there are Python syntax errors or undefined names + flake8 . --count --select=E9,F63,F7,F82 --show-source --statistics + # exit-zero treats all errors as warnings. The GitHub editor is 127 chars wide + flake8 . --count --exit-zero --max-complexity=10 --max-line-length=127 --statistics + - name: Sort imports + run: | + pip install isort + # stop the build if there are Python syntax errors or undefined names + isort . + isort --check --diff . + - name: Setup test database + env: + POSTGRES_DB_NAME: ${{ matrix.database-name }} + POSTGRES_USER: ${{ matrix.database-user }} + POSTGRES_PASSWORD: ${{ matrix.database-password }} + POSTGRES_DB_HOST: ${{ matrix.database-host }} + POSTGRES_DB_PORT: ${{ matrix.database-port }} + POSTGRES_DB: ${{ matrix.database-name }} + run: | + export DATABASE_URL=postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }} + export SECRET_KEY=test-secret-key + export DEBUG=1 + - name: Run migrations + run: | + export DATABASE_URL=postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }} + export SECRET_KEY=test-secret-key + export DEBUG=1 + export ALLOWED_HOSTS=localhost + export GITHUB_WORKFLOW=True + export MODE=workflow + python manage.py makemigrations + python manage.py migrate + python manage.py migrate --run-syncdb + python manage.py check + - name: Run tests + run: | + python manage.py test + env: + DATABASE_URL: postgres://${{ matrix.database-user }}:${{ matrix.database-password }}@${{ matrix.database-host }}:${{ matrix.database-port }}/${{ matrix.database-name }} + SECRET_KEY: test-secret-key + DEBUG: 1 + ALLOWED_HOSTS: localhost + GITHUB_WORKFLOW: True + MODE: workflow + - uses: actions/checkout@v2.4.0 + - name: Build the images and start the containers + run: | + export GITHUB_WORKFLOW=True + export MODE="Test" + docker-compose -f docker-compose.yml build + docker-compose -f docker-compose.yml up -d + # run: docker-compose up -d --build + - name: Stop containers + if: always() + run: docker-compose -f "docker-compose.yml" down + diff --git a/Dockerfile b/Dockerfile index 5dd6f4c..db6d9bd 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,7 @@ # syntax=docker/dockerfile:1.9 ### Start build prep. ### This should be a separate build container for better reuse. -FROM python:3.13-slim-bookworm AS build +FROM mcr.microsoft.com/playwright/python:v1.52.0-noble AS build # The following does not work in Podman unless you build in Docker # compatibility mode: @@ -20,7 +20,7 @@ COPY --from=ghcr.io/astral-sh/uv:0.7.2 /uv /usr/local/bin/uv ENV UV_LINK_MODE=copy \ UV_COMPILE_BYTECODE=1 \ UV_PYTHON_DOWNLOADS=never \ - UV_PYTHON=python3.13 \ + UV_PYTHON=python3.12 \ UV_PROJECT_ENVIRONMENT=/app # Synchronize DEPENDENCIES without the application itself. @@ -40,28 +40,24 @@ RUN --mount=type=cache,target=/root/.cache \ # Now install the rest from `/src`: The APPLICATION w/o dependencies. # `/src` will NOT be copied into the runtime container. -# LEAVE THIS OUT if your application is NOT a proper Python package. -# COPY . /src -# WORKDIR /src -# RUN --mount=type=cache,target=/root/.cache \ -# uv sync \ -# --locked \ -# --no-dev \ -# --no-editable +COPY . /src +WORKDIR /src +RUN --mount=type=cache,target=/root/.cache \ + uv sync \ + --locked \ + --no-dev \ + --no-editable ### End build prep ########################################################################## -FROM python:3.13-slim-bookworm +FROM mcr.microsoft.com/playwright/python:v1.52.0-noble SHELL ["sh", "-exc"] -ARG ENV_FILE=.env.production - ENV PATH=/app/bin:$PATH ENV PYTHONPATH=/app ENV PYTHONUNBUFFERED=1 -ENV HOME=/code -ENV DJANGO_SETTINGS_MODULE=django_project.settings +ENV HOME=/app WORKDIR /app @@ -87,19 +83,6 @@ STOPSIGNAL SIGINT COPY --from=build --chown=app:app /app /app -RUN playwright install --with-deps - -# If your application is NOT a proper Python package that got -# pip-installed above, you need to copy your application into -# the container HERE and set the WORKDIR: -COPY --chown=app:app ./ /code -WORKDIR /code -ENV PYTHONPATH=/code -# WORKDIR /app # if python-packaged - -RUN rm -f .env -COPY ${ENV_FILE} .env - COPY --chown=app:app --chmod=700 /scripts/entrypoint.sh /entrypoint.sh COPY --chown=app:app --chmod=700 /scripts/deploy.sh /deploy.sh diff --git a/MANIFEST.in b/MANIFEST.in new file mode 100644 index 0000000..9eba189 --- /dev/null +++ b/MANIFEST.in @@ -0,0 +1,29 @@ +include README.md +include LICENSE + +graft accounts/templates +graft accounts/migrations + +graft cards/templates +graft cards/migrations + +graft home/migrations + +graft theme/templates +graft theme/templatetags +graft theme/static + +graft trades/templates +graft trades/migrations + +graft static + +recursive-include accounts *.py +recursive-include cards *.py +recursive-include common *.py +recursive-include django_project *.py +recursive-include home *.py +recursive-include theme *.py +recursive-include trades *.py + +global-exclude *.py[cod] __pycache__ .DS_Store .* diff --git a/accounts/migrations/0001_initial.py b/accounts/migrations/0001_initial.py index 55851d1..9273981 100644 --- a/accounts/migrations/0001_initial.py +++ b/accounts/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2025-04-17 23:12 +# Generated by Django 5.1 on 2025-05-09 01:49 import accounts.models import django.contrib.auth.models @@ -14,7 +14,7 @@ class Migration(migrations.Migration): initial = True dependencies = [ - ('auth', '0001_initial'), + ('auth', '0012_alter_user_first_name_max_length'), ] operations = [ diff --git a/cards/migrations/0001_initial.py b/cards/migrations/0001_initial.py index 0ea6926..1bbde40 100644 --- a/cards/migrations/0001_initial.py +++ b/cards/migrations/0001_initial.py @@ -1,4 +1,4 @@ -# Generated by Django 5.1.2 on 2025-04-17 23:12 +# Generated by Django 5.1 on 2025-05-09 01:49 import django.db.models.deletion from django.db import migrations, models diff --git a/common/apps.py b/common/apps.py deleted file mode 100644 index 5f2f078..0000000 --- a/common/apps.py +++ /dev/null @@ -1,5 +0,0 @@ -from django.apps import AppConfig - - -class CommonConfig(AppConfig): - name = 'common' diff --git a/docker-compose.yml b/docker-compose.yml index 9ef12fd..526c5d2 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -9,13 +9,13 @@ services: depends_on: - web web: - build: - context: . - dockerfile: Dockerfile - args: - ENV_FILE: .env.dev + build: . volumes: - - .:/code:z + #- ./seed:/seed:ro + - ./.env.dev:/.env:ro + #- ./src:/src + env_file: + - .env.dev labels: caddy: ":8000" caddy.reverse_proxy: "{{upstreams 8000}}" diff --git a/docker-compose_prod.yml b/docker-compose_prod.yml index 9dc8e53..fc8209a 100644 --- a/docker-compose_prod.yml +++ b/docker-compose_prod.yml @@ -9,11 +9,12 @@ services: depends_on: - web web: - build: - context: . - dockerfile: Dockerfile - args: - ENV_FILE: .env.dev + build: . + volumes: + - ./seed:/seed:ro + - ./.env.production:/.env:ro + env_file: + - .env.production deploy: mode: replicated replicas: 4 diff --git a/pyproject.toml b/pyproject.toml index 2b8a83f..c09624d 100644 --- a/pyproject.toml +++ b/pyproject.toml @@ -1,9 +1,31 @@ +[build-system] +requires = ["setuptools>=80", "setuptools-scm>=8", "wheel"] +build-backend = "setuptools.build_meta" + [project] name = "pkmntrade-club" version = "0.1.0" -description = "A website for trading Pokémon TCG Pocket Cards" +description = "A django project for trading Pokémon TCG Pocket Cards" readme = "README.md" -requires-python = ">=3.13" +requires-python = ">=3.12" +license = "MIT" +license-files = ["LICENSE"] +authors = [ + { name = "badblocks", email = "rob@badblocks.email" }, +] +classifiers = [ + "Environment :: Web Environment", + "Framework :: Django", + "Framework :: Django :: 5.1", + "Intended Audience :: Developers", + "Operating System :: OS Independent", + "Programming Language :: Python", + "Programming Language :: Python :: 3", + "Programming Language :: Python :: 3.12", + "Programming Language :: Python :: 3.13", + "Topic :: Internet :: WWW/HTTP", + "Topic :: Internet :: WWW/HTTP :: Dynamic Content", +] dependencies = [ "asgiref==3.8.1", "certifi==2022.12.7", @@ -12,13 +34,14 @@ dependencies = [ "crispy-tailwind==1.0.3", "cryptography==39.0.1", "defusedxml==0.7.1", - "django==5.1.2", + "django==5.1", "django-allauth==65.0.2", "django-browser-reload==1.17.0", "django-crispy-forms==2.3", "django-daisy==1.0.13", "django-debug-toolbar==4.4.6", "django-environ==0.12.0", + "django-linear-migrations>=2.17.0", "django-meta==2.4.2", "django-tailwind-4[reload]==0.1.4", "django-widget-tweaks==1.5.0", @@ -41,5 +64,22 @@ dependencies = [ "typing-extensions==4.9.0", "urllib3==1.26.14", "whitenoise==6.7.0", - "django-linear-migrations>=2.17.0", +] + +[project.scripts] +pkmntrade-manage = "manage:main" + +[project.urls] +Homepage = "https://pkmntrade.club" + +[tool.setuptools] +packages = [ + "accounts", + "cards", + "common", + "django_project", + "home", + "static", + "theme", + "trades" ] diff --git a/scripts/deploy.sh b/scripts/deploy.sh index c5bdf0f..26c3c87 100755 --- a/scripts/deploy.sh +++ b/scripts/deploy.sh @@ -1,5 +1,15 @@ #!/bin/bash -django-admin makemigrations --noinput --check && django-admin migrate --noinput -django-admin clear_cache -django-admin collectstatic -c --no-input \ No newline at end of file +echo "Running makemigrations --check to make sure migrations are up to date..." +django-admin makemigrations --noinput --check 2>&1 + +echo "Running migrate to apply migrations..." +django-admin migrate --noinput 2>&1 + +echo "Clearing django cache..." +django-admin clear_cache 2>&1 + +echo "Running collectstatic..." +django-admin collectstatic -c --no-input 2>&1 + +echo "Deployed successfully!" \ No newline at end of file diff --git a/scripts/entrypoint.sh b/scripts/entrypoint.sh index 77fedd7..0ea62df 100755 --- a/scripts/entrypoint.sh +++ b/scripts/entrypoint.sh @@ -1,13 +1,23 @@ #!/bin/bash -set -exc +set -ex + +# Load environment variables from .env file if it exists in /code/ +if [ -f /.env ]; then + echo "Loading environment variables from .env" + # Set allexport option to export all variables defined by sourcing the .env file. + set -a + source /.env + # Unset allexport option. + set +a +else + echo "Warning: Volume mount for /.env file not found. Make sure you are using env_file in docker-compose.yml or mounting it in the container." +fi -# check if the startup command has been provided if [ "$1" == "" ]; then echo "Startup command not set. Exiting" exit; fi -# check if the $DJANGO_SETTINGS_MODULE environment variable has been set if [ "$DJANGO_SETTINGS_MODULE" == "" ]; then echo "Environment variable 'DJANGO_SETTINGS_MODULE' not set. Exiting." exit; @@ -15,6 +25,7 @@ else export DJANGO_SETTINGS_MODULE=$DJANGO_SETTINGS_MODULE fi +echo "Running deploy.sh..." /deploy.sh echo "Enviroment is correct and deploy.sh has been run - executing command: '$@'" diff --git a/scripts/reset-db_make-migrations_seed-data.sh b/scripts/reset-db_make-migrations_seed-data.sh index c954abd..58cf0c6 100755 --- a/scripts/reset-db_make-migrations_seed-data.sh +++ b/scripts/reset-db_make-migrations_seed-data.sh @@ -14,6 +14,6 @@ echo "Waiting for the database to be ready..." sleep 10 echo "Loading seed data..." -docker compose exec -it web env DJANGO_SETTINGS_MODULE=django_project.settings django-admin loaddata /code/seed/0* +docker compose exec -it web bash -c "django-admin loaddata /seed/0*" echo "Done & Started!" diff --git a/static/__init__.py b/static/__init__.py new file mode 100644 index 0000000..e69de29 diff --git a/theme/templates/home/home.html b/theme/templates/home/home.html index f2eca0b..00de406 100644 --- a/theme/templates/home/home.html +++ b/theme/templates/home/home.html @@ -1,6 +1,10 @@ {% extends 'base.html' %} {% load static trade_offer_tags card_badge cache card_multiselect %} +{% block title %} +Welcome +{% endblock title %} + {% block content %}