Merge branch 'chore/2406-compose-modularity-scope' into 'develop'

Chore: stabilise development environment manifests + documentation

Closes #2406

See merge request funkwhale/funkwhale!2897
This commit is contained in:
jon r 2025-04-10 10:50:30 +00:00
commit 0beee1bd05
26 changed files with 445 additions and 221 deletions

9
.editorconfig Normal file
View File

@ -0,0 +1,9 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true

View File

@ -1,16 +1,20 @@
# api + celeryworker COMPOSE_BAKE=true
# api + worker
DEBUG=True DEBUG=True
FORCE=True
DEFAULT_FROM_EMAIL=hello@funkwhale.test DEFAULT_FROM_EMAIL=hello@funkwhale.test
FUNKWHALE_DOMAIN=funkwhale.test FUNKWHALE_DOMAIN=funkwhale.test
FUNKWHALE_PROTOCOL=https FUNKWHALE_PROTOCOL=https
DJANGO_SECRET_KEY=dev DJANGO_SECRET_KEY=dev
DJANGO_ALLOWED_HOSTS=.funkwhale.test,nginx DJANGO_ALLOWED_HOSTS=.funkwhale.test,web
DJANGO_SETTINGS_MODULE=config.settings.local DJANGO_SETTINGS_MODULE=config.settings.local
DATABASE_URL=postgresql://postgres@postgres/postgres DATABASE_URL=postgresql://postgres@postgres/postgres
CACHE_URL=redis://redis:6379/0 CACHE_URL=redis://redis:6379/0
CELERY_BROKER_URL=redis://redis:6379/1
EMAIL_CONFIG=smtp://mailpit.funkwhale.test:1025 EMAIL_CONFIG=smtp://mailpit.funkwhale.test:1025
FORCE_HTTPS_URLS=True FORCE_HTTPS_URLS=True
@ -20,17 +24,18 @@ C_FORCE_ROOT=true
PYTHONDONTWRITEBYTECODE=true PYTHONDONTWRITEBYTECODE=true
PYTHONTRACEMALLOC=0 PYTHONTRACEMALLOC=0
# app
HOST=0.0.0.0
VUE_PORT=8080
# api # api
FUNKWHALE_SPA_HTML_ROOT=http://nginx/ FUNKWHALE_SPA_HTML_ROOT=http://app:${VUE_PORT}
LDAP_ENABLED=False LDAP_ENABLED=False
BROWSABLE_API_ENABLED=True BROWSABLE_API_ENABLED=True
# celeryworker # api + web
CELERYD_CONCURRENCY=0
# api + nginx
STATIC_ROOT=/staticfiles STATIC_ROOT=/staticfiles
MEDIA_ROOT=/data/media MEDIA_ROOT=/data/media
@ -38,19 +43,14 @@ MEDIA_ROOT=/data/media
# api + Typesense # api + Typesense
TYPESENSE_API_KEY=apikey TYPESENSE_API_KEY=apikey
# front # web
HOST=0.0.0.0
VUE_PORT=8080
# nginx
NGINX_MAX_BODY_SIZE=10G NGINX_MAX_BODY_SIZE=10G
FUNKWHALE_API_HOST=api FUNKWHALE_API_HOST=api
FUNKWHALE_API_PORT=5000 FUNKWHALE_API_PORT=5000
FUNKWHALE_FRONT_IP=front FUNKWHALE_FRONT_HOST=app
FUNKWHALE_FRONT_PORT=${VUE_PORT} FUNKWHALE_FRONT_PORT=${VUE_PORT}
# postgres # postgres

View File

@ -30,7 +30,7 @@ services:
- "FUNKWHALE_API_IP=host.docker.internal" - "FUNKWHALE_API_IP=host.docker.internal"
- "FUNKWHALE_API_HOST=host.docker.internal" - "FUNKWHALE_API_HOST=host.docker.internal"
- "FUNKWHALE_API_PORT=5000" - "FUNKWHALE_API_PORT=5000"
- "FUNKWHALE_FRONT_IP=host.docker.internal" - "FUNKWHALE_FRONT_HOST=host.docker.internal"
- "FUNKWHALE_FRONT_PORT=8080" - "FUNKWHALE_FRONT_PORT=8080"
- "FUNKWHALE_HOSTNAME=${FUNKWHALE_HOSTNAME-host.docker.internal}" - "FUNKWHALE_HOSTNAME=${FUNKWHALE_HOSTNAME-host.docker.internal}"
- "FUNKWHALE_PROTOCOL=https" - "FUNKWHALE_PROTOCOL=https"

View File

@ -1,8 +1,8 @@
# Exclude everything and allow only the necessary files # Exclude everything and allow only the necessary files
* *
!/docker/
!/config/ !/config/
!/funkwhale_api/ !/funkwhale_api/
!/entrypoint.sh
!/manage.py !/manage.py
!/poetry.lock !/poetry.lock
!/pyproject.toml !/pyproject.toml

View File

@ -103,6 +103,7 @@ ARG PIP_NO_CACHE_DIR=1
RUN set -eux; \ RUN set -eux; \
apk add --no-cache \ apk add --no-cache \
bash \ bash \
curl \
ffmpeg \ ffmpeg \
gettext \ gettext \
jpeg-dev \ jpeg-dev \
@ -132,6 +133,5 @@ RUN --mount=type=cache,target=~/.cache/pip; \
set -eux; \ set -eux; \
pip3 install --no-deps --editable . pip3 install --no-deps --editable .
ENV IS_DOCKER_SETUP=true ADD --chown=0:0 --chmod=0755 ./entrypoint.sh /
ENTRYPOINT [ "/entrypoint.sh" ]
CMD ["./docker/server.sh"]

View File

@ -51,6 +51,7 @@ ENV PATH="/venv/bin:$PATH"
RUN --mount=type=cache,target=/var/lib/apt/lists \ RUN --mount=type=cache,target=/var/lib/apt/lists \
apt update; \ apt update; \
apt install -y \ apt install -y \
curl \
ffmpeg \ ffmpeg \
gettext \ gettext \
libjpeg-dev \ libjpeg-dev \
@ -68,4 +69,5 @@ WORKDIR /app
COPY . /app COPY . /app
RUN poetry install --extras typesense RUN poetry install --extras typesense
CMD ["./docker/server.sh"] ADD --chown=0:0 --chmod=0755 ./entrypoint.sh /
ENTRYPOINT [ "/entrypoint.sh" ]

View File

@ -1,6 +1,5 @@
import logging.config import logging.config
import sys import sys
import warnings
from collections import OrderedDict from collections import OrderedDict
from urllib.parse import urlparse, urlsplit from urllib.parse import urlparse, urlsplit
@ -36,8 +35,8 @@ Available levels:
""" """
IS_DOCKER_SETUP = env.bool("IS_DOCKER_SETUP", False) # allows makemigrations and superuser creation
FORCE = env.bool("FORCE", False)
if env("FUNKWHALE_SENTRY_DSN", default=None) is not None: if env("FUNKWHALE_SENTRY_DSN", default=None) is not None:
import sentry_sdk import sentry_sdk
@ -393,31 +392,6 @@ vars().update(EMAIL_CONFIG)
# ------------------------------------------------------------------------------ # ------------------------------------------------------------------------------
# See: https://docs.djangoproject.com/en/dev/ref/settings/#databases # See: https://docs.djangoproject.com/en/dev/ref/settings/#databases
# The `_database_url_docker` variable will only by used as default for DATABASE_URL
# in the context of a docker deployment.
_database_url_docker = None
if IS_DOCKER_SETUP and env.str("DATABASE_URL", None) is None:
warnings.warn(
DeprecationWarning(
"the automatically generated 'DATABASE_URL' configuration in the docker "
"setup is deprecated, please configure either the 'DATABASE_URL' "
"environment variable or the 'DATABASE_HOST', 'DATABASE_USER' and "
"'DATABASE_PASSWORD' environment variables instead"
)
)
_DOCKER_DATABASE_HOST = "postgres"
_DOCKER_DATABASE_PORT = 5432
_DOCKER_DATABASE_USER = env.str("POSTGRES_ENV_POSTGRES_USER", "postgres")
_DOCKER_DATABASE_PASSWORD = env.str("POSTGRES_ENV_POSTGRES_PASSWORD", "")
_DOCKER_DATABASE_NAME = _DOCKER_DATABASE_USER
_database_url_docker = (
f"postgres:"
f"//{_DOCKER_DATABASE_USER}:{_DOCKER_DATABASE_PASSWORD}"
f"@{_DOCKER_DATABASE_HOST}:{_DOCKER_DATABASE_PORT}"
f"/{_DOCKER_DATABASE_NAME}"
)
DATABASE_HOST = env.str("DATABASE_HOST", "localhost") DATABASE_HOST = env.str("DATABASE_HOST", "localhost")
""" """
The hostname of the PostgreSQL server. Defaults to ``localhost``. The hostname of the PostgreSQL server. Defaults to ``localhost``.
@ -440,8 +414,7 @@ The name of the PostgreSQL database. Defaults to ``funkwhale``.
""" """
DATABASE_URL = env.db( DATABASE_URL = env.db(
"DATABASE_URL", "DATABASE_URL",
_database_url_docker # This is only set in the context of a docker deployment. (
or (
f"postgres:" f"postgres:"
f"//{DATABASE_USER}:{DATABASE_PASSWORD}" f"//{DATABASE_USER}:{DATABASE_PASSWORD}"
f"@{DATABASE_HOST}:{DATABASE_PORT}" f"@{DATABASE_HOST}:{DATABASE_PORT}"
@ -838,11 +811,7 @@ if AUTH_LDAP_ENABLED:
# SLUGLIFIER # SLUGLIFIER
AUTOSLUG_SLUGIFY_FUNCTION = "slugify.slugify" AUTOSLUG_SLUGIFY_FUNCTION = "slugify.slugify"
CACHE_URL_DEFAULT = "redis://127.0.0.1:6379/0" CACHE_URL = env.str("CACHE_URL", default="redis://127.0.0.1:6379/0")
if IS_DOCKER_SETUP:
CACHE_URL_DEFAULT = "redis://redis:6379/0"
CACHE_URL = env.str("CACHE_URL", default=CACHE_URL_DEFAULT)
""" """
The URL of your redis server. For example: The URL of your redis server. For example:
@ -1527,10 +1496,7 @@ TYPESENSE_PROTOCOL = env("TYPESENSE_PROTOCOL", default="http")
"""Typesense listening protocol""" """Typesense listening protocol"""
TYPESENSE_HOST = env( TYPESENSE_HOST = env(
"TYPESENSE_HOST", "TYPESENSE_HOST",
default="typesense" if IS_DOCKER_SETUP else "localhost", default="localhost",
) )
""" """Typesense hostname. Defaults to `localhost`."""
Typesense hostname. Defaults to `localhost` on non-Docker deployments and to `typesense` on
Docker deployments.
"""
TYPESENSE_NUM_TYPO = env("TYPESENSE_NUM_TYPO", default=5) TYPESENSE_NUM_TYPO = env("TYPESENSE_NUM_TYPO", default=5)

View File

@ -152,6 +152,3 @@ REST_FRAMEWORK.update(
], ],
} }
) )
# allows makemigrations and superuser creation
FORCE = env("FORCE", default=1)

View File

@ -1,13 +0,0 @@
#!/bin/sh
set -eux
funkwhale-manage collectstatic --noinput
funkwhale-manage migrate
# shellcheck disable=SC2086
exec gunicorn config.asgi:application \
--workers "${FUNKWHALE_WEB_WORKERS-1}" \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:"${FUNKWHALE_API_PORT}" \
${GUNICORN_ARGS-}

49
api/entrypoint.sh Executable file
View File

@ -0,0 +1,49 @@
#!/usr/bin/env sh
set -eux
case "${1}" in
gunicorn)
# shellcheck disable=SC2086
exec gunicorn config.asgi:application \
--workers "${FUNKWHALE_WEB_WORKERS:-1}" \
--worker-class uvicorn.workers.UvicornWorker \
--bind 0.0.0.0:"${FUNKWHALE_API_PORT}" \
${GUNICORN_ARGS:-}
;;
migrate)
funkwhale-manage migrate
;;
collectstatic)
funkwhale-manage collectstatic --noinput
;;
uvicorn)
exec uvicorn \
--reload config.asgi:application \
--host 0.0.0.0 \
--port 5000 \
--reload-dir config/ \
--reload-dir funkwhale_api/
;;
develop)
${0} migrate
${0} collectstatic
${0} uvicorn
;;
develop-worker)
export CELERYD_CONCURRENCY=0
watchmedo auto-restart \
--patterns="*.py" \
--recursive \
-- \
celery \
-A funkwhale_api.taskapp worker \
-B \
-l debug \
-s /tmp/celerybeat-schedule \
--concurrency=${CELERYD_CONCURRENCY}
;;
*)
exec "${@}"
;;
esac

View File

@ -0,0 +1 @@
Chore: refactor development environment manifests (#2406)

View File

@ -1,4 +1,11 @@
name: funkwhale-net name: funkwhale-net
networks:
web:
external: true
x-networks: &networks
- web
include: include:
- path: compose/net.dnsmasq.yml - path: compose/net.dnsmasq.yml
- path: compose/net.traefik.yml - path: compose/net.traefik.yml
@ -12,4 +19,14 @@ include:
# Comment out the following line if you have other containers # Comment out the following line if you have other containers
# present on the docker0 network. # present on the docker0 network.
- path: compose/net.helpers.docker0.yml - path: compose/net.helpers.docker0.yml
- path: compose/net.verify.yml services:
verify-external-connectivity:
extends:
file: compose/net.verify.yml
service: verify-external-connectivity
networks: *networks
verify-internal-connectivity:
extends:
file: compose/net.verify.yml
service: verify-internal-connectivity
networks: *networks

View File

@ -4,59 +4,19 @@ networks:
external: true external: true
x-django: &django x-django: &django
environment: depends_on:
- DEBUG postgres:
- DEFAULT_FROM_EMAIL condition: service_healthy
redis:
- DJANGO_SETTINGS_MODULE condition: service_healthy
- DJANGO_SECRET_KEY
- EXTERNAL_REQUESTS_VERIFY_SSL
- "FORCE_HTTPS_URLS=${FORCE_HTTPS_URLS:-False}"
- FUNKWHALE_PROTOCOL
- "FUNKWHALE_HOSTNAME=${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}"
- DATABASE_URL
- CACHE_URL
- EMAIL_CONFIG
- TYPESENSE_API_KEY
- "STATIC_URL=${FUNKWHALE_PROTOCOL}://${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}/static/"
- "MEDIA_URL=${FUNKWHALE_PROTOCOL}://${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}/media/"
- STATIC_ROOT
- MEDIA_ROOT
- FUNKWHALE_SPA_HTML_ROOT
- LDAP_ENABLED
- BROWSABLE_API_ENABLED
- "MUSIC_DIRECTORY_PATH=${MUSIC_DIRECTORY_PATH:-/music}"
- C_FORCE_ROOT
- PYTHONDONTWRITEBYTECODE
- PYTHONTRACEMALLOC
dns: 172.17.0.1 dns: 172.17.0.1
dns_search: funkwhale.test dns_search: funkwhale.test
services: services:
front: app:
build: extends:
context: ./front file: ./compose/app.vue.yml
dockerfile: Dockerfile.dev service: app
ports:
- "${VUE_PORT:-8080}:${VUE_PORT:-8080}"
environment:
- HOST
- VUE_PORT
volumes:
- "./front:/app"
- "/app/node_modules"
- "./po:/po"
networks:
- internal
command: "yarn dev --host"
api: api:
extends: extends:
@ -64,41 +24,30 @@ services:
service: api service: api
<<: *django <<: *django
celeryworker: worker:
extends: extends:
file: ./compose/app.django.yml file: ./compose/app.django.yml
service: celeryworker service: worker
<<: *django <<: *django
nginx: web:
extends: extends:
file: ./compose/app.nginx.yml file: ./compose/app.nginx.yml
service: nginx service: web
environment: depends_on:
- "MUSIC_DIRECTORY_PATH=${MUSIC_DIRECTORY_PATH:-/music}" app:
- "FUNKWHALE_HOSTNAME=${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}" condition: service_healthy
api:
- FUNKWHALE_PROTOCOL condition: service_healthy
- FUNKWHALE_API_HOST
- FUNKWHALE_API_PORT
- FUNKWHALE_FRONT_IP
- FUNKWHALE_FRONT_PORT
- NGINX_MAX_BODY_SIZE
- STATIC_ROOT
- "MEDIA_ROOT=${MEDIA_ROOT:-/data/media}"
networks: networks:
- web - web
- internal - internal
labels: labels:
- "traefik.enable=true" - "traefik.enable=true"
- "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-web.rule=Host(`${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}`)" - "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-web.rule=Host(`${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}`)"
- "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-web.entrypoints=web" - "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-web.entrypoints=web"
- "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-web.middlewares=redirect-scheme@file"
- "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-webs.rule=Host(`${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}`)" - "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-webs.rule=Host(`${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}`)"
- "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-webs.entrypoints=webs" - "traefik.http.routers.test-funkwhale-${COMPOSE_PROJECT_NAME:-funkwhale}-webs.entrypoints=webs"

View File

@ -1,20 +1,49 @@
x-django: &django x-django: &django
image: funkwhale-api image: funkwhale-api
init: true
networks:
- internal
volumes: volumes:
- ../api:/app - ../api:/app
- ../.env:/app/.env
- "${MUSIC_DIRECTORY_SERVE_PATH:-../.state/music}:/music:ro" - "${MUSIC_DIRECTORY_SERVE_PATH:-../.state/music}:/music:ro"
- "../.state/plugins:/srv/funkwhale/plugins" - "../.state/plugins:/srv/funkwhale/plugins"
- "../.state/staticfiles:/staticfiles" - "../.state/staticfiles:/staticfiles"
- "../.state/media:/protected/media" - "../.state/media:/protected/media"
- "../.state/${COMPOSE_PROJECT_NAME:-funkwhale}/media:/data/media" - "../.state/${COMPOSE_PROJECT_NAME:-funkwhale}/media:/data/media"
depends_on: environment:
postgres: - DEBUG
condition: service_healthy - FORCE
redis: - DEFAULT_FROM_EMAIL
condition: service_healthy
networks: - DJANGO_SETTINGS_MODULE
- internal - DJANGO_SECRET_KEY
- EXTERNAL_REQUESTS_VERIFY_SSL
- "FORCE_HTTPS_URLS=${FORCE_HTTPS_URLS:-False}"
- FUNKWHALE_PROTOCOL
- "FUNKWHALE_HOSTNAME=${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}"
- DATABASE_URL
- CACHE_URL
- CELERY_BROKER_URL
- EMAIL_CONFIG
- TYPESENSE_API_KEY
- "STATIC_URL=${FUNKWHALE_PROTOCOL}://${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}/static/"
- "MEDIA_URL=${FUNKWHALE_PROTOCOL}://${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}/media/"
- STATIC_ROOT
- MEDIA_ROOT
- FUNKWHALE_SPA_HTML_ROOT
- LDAP_ENABLED
- BROWSABLE_API_ENABLED
- "MUSIC_DIRECTORY_PATH=${MUSIC_DIRECTORY_PATH:-/music}"
- C_FORCE_ROOT
- PYTHONDONTWRITEBYTECODE
- PYTHONTRACEMALLOC
services: services:
api: api:
@ -22,28 +51,28 @@ services:
build: build:
context: ../api context: ../api
dockerfile: Dockerfile.debian dockerfile: Dockerfile.debian
command: develop
healthcheck: healthcheck:
test: test:
[ [
"CMD-SHELL", "CMD-SHELL",
'docker compose logs api | grep -q "Uvicorn running on" || exit 0', "curl -o /dev/null -s -w '%{http_code}' http://localhost:5000/api/v1 | grep '301' || exit 1",
] ]
interval: 3s interval: 10s
timeout: 5s timeout: 5s
retries: 3 retries: 3
command: > start_period: 60s
sh -c "
funkwhale-manage collectstatic --no-input &&
uvicorn --reload config.asgi:application --host 0.0.0.0 --port 5000 --reload-dir config/ --reload-dir funkwhale_api/
"
celeryworker: worker:
<<: *django <<: *django
command: > command: develop-worker
sh -c ' healthcheck:
pip install watchdog[watchmedo] && test:
watchmedo auto-restart --patterns="*.py" --recursive -- celery -A funkwhale_api.taskapp worker -l debug -B --concurrency=${CELERYD_CONCURRENCY} [
' "CMD-SHELL",
depends_on: "celery -A funkwhale_api.taskapp status | grep 'OK' || exit 1",
api: ]
condition: service_healthy interval: 10s
timeout: 5s
retries: 3
start_period: 30s

View File

@ -1,9 +1,24 @@
services: services:
nginx: web:
image: nginx image: nginx
depends_on: networks:
- api - internal
- front environment:
- "MUSIC_DIRECTORY_PATH=${MUSIC_DIRECTORY_PATH:-/music}"
- "FUNKWHALE_HOSTNAME=${COMPOSE_PROJECT_NAME:-funkwhale}.${FUNKWHALE_DOMAIN}"
- FUNKWHALE_PROTOCOL
- FUNKWHALE_API_HOST
- FUNKWHALE_API_PORT
- FUNKWHALE_FRONT_HOST
- FUNKWHALE_FRONT_PORT
- NGINX_MAX_BODY_SIZE
- STATIC_ROOT
- "MEDIA_ROOT=${MEDIA_ROOT:-/data/media}"
volumes: volumes:
- "${MUSIC_DIRECTORY_SERVE_PATH:-../.state/music}:${MUSIC_DIRECTORY_PATH:-/music}:ro" - "${MUSIC_DIRECTORY_SERVE_PATH:-../.state/music}:${MUSIC_DIRECTORY_PATH:-/music}:ro"
@ -14,5 +29,8 @@ services:
- ../.state/staticfiles:/usr/share/nginx/html/staticfiles:ro - ../.state/staticfiles:/usr/share/nginx/html/staticfiles:ro
- ../.state/media:/protected/media:ro - ../.state/media:/protected/media:ro
- ../.state/${COMPOSE_PROJECT_NAME:-funkwhale}/media:/data/media:ro - ../.state/${COMPOSE_PROJECT_NAME:-funkwhale}/media:/data/media:ro
networks: healthcheck:
- internal test: 'curl -o /dev/null -s -w "%{http_code}" http://localhost:80/ | grep "200" || exit 1'
interval: 5s
timeout: 3s
retries: 3

View File

@ -2,9 +2,20 @@ services:
typesense: typesense:
environment: environment:
- TYPESENSE_API_KEY - TYPESENSE_API_KEY
image: typesense/typesense:27.1 image: typesense/typesense:28.0
networks: networks:
- internal - internal
volumes: volumes:
- ../.state/${COMPOSE_PROJECT_NAME:-funkwhale}/typesense/data:/data - ../.state/${COMPOSE_PROJECT_NAME:-funkwhale}/typesense/data:/data
command: --data-dir /data --api-key=$${TYPESENSE_API_KEY} --enable-cors command: --data-dir /data --api-key=$${TYPESENSE_API_KEY} --enable-cors
healthcheck:
test:
[
"CMD",
"bash",
"-c",
"exec 3<>/dev/tcp/localhost/8108 && printf 'GET /health HTTP/1.1\\r\\nConnection: close\\r\\n\\r\\n' >&3 && head -n1 <&3 | grep '200' && exec 3>&-",
]
interval: 10s
timeout: 5s
retries: 3

17
compose/app.vue.yml Normal file
View File

@ -0,0 +1,17 @@
services:
app:
image: funkwhale-app
build:
context: ../front
dockerfile: Dockerfile.dev
command: "yarn dev --host"
ports:
- "${VUE_PORT:-8080}:${VUE_PORT:-8080}"
networks:
- internal
volumes:
- "../front:/app"
- "/app/node_modules"
environment:
- HOST
- VUE_PORT

View File

@ -4,7 +4,7 @@ upstream funkwhale-api {
} }
upstream funkwhale-front { upstream funkwhale-front {
server ${FUNKWHALE_FRONT_IP}:${FUNKWHALE_FRONT_PORT}; server ${FUNKWHALE_FRONT_HOST}:${FUNKWHALE_FRONT_PORT};
} }
# Required for websocket support. # Required for websocket support.

View File

@ -0,0 +1,6 @@
http:
middlewares:
redirect-scheme:
redirectScheme:
scheme: https
permanent: false

View File

@ -18,6 +18,6 @@ services:
depends_on: depends_on:
minio: {} minio: {}
celeryworker: worker:
depends_on: depends_on:
minio: {} minio: {}

View File

@ -1,7 +1,6 @@
x-verify: &verify x-verify: &verify
init: true init: true
image: "busybox" image: "busybox"
network_mode: bridge
dns: 172.17.0.1 dns: 172.17.0.1
dns_search: funkwhale.test dns_search: funkwhale.test

View File

@ -33,10 +33,13 @@ Funkwhale can be run in Docker containers for local development. You can work on
:::: ::::
6. Activate the pre-commit hook: 6. Activate the pre-commit hook:
```sh ```sh
pre-commit install pre-commit install
``` ```
7. Finally, initialise the environment: 7. Finally, initialise the environment:
```sh ```sh
cp .env.example .env cp .env.example .env
``` ```
@ -50,16 +53,18 @@ Funkwhale provides a `compose.yml` file following the default file naming conven
To set up your Docker environment: To set up your Docker environment:
1. Create a network for federation support via the web proxy: 1. Create a network for federation support via the web proxy:
```sh ```sh
docker network create web docker network create web
``` ```
2. Then build the application containers. Run this command any time there are upstream changes or dependency changes to ensure you're up-to-date. 2. Then build the application containers. Run this command any time there are upstream changes or dependency changes to ensure you're up-to-date.
```sh ```sh
docker compose build docker compose build
``` ```
## Set up auxiliary services ## Set up network services
To support ActivityPub in the local development environment, we use a To support ActivityPub in the local development environment, we use a
combination of auxiliary services that provide DNS-based discovery, local email delivery and web/TLS termination. This also has the benefit that we can talk to combination of auxiliary services that provide DNS-based discovery, local email delivery and web/TLS termination. This also has the benefit that we can talk to
@ -85,9 +90,11 @@ The services bind to the following ports on the default Docker bridge network:
1. Create a wildcard certificate for the Common Name (CN) `funkwhale.test` and 1. Create a wildcard certificate for the Common Name (CN) `funkwhale.test` and
the Subject Alternative Name (SAN) `*.funkwhale.test` which will be the Subject Alternative Name (SAN) `*.funkwhale.test` which will be
installed into your system and browser trust stores with: installed into your system and browser trust stores with:
```sh ```sh
mkcert -install -cert-file compose/var/test.crt -key-file compose/var/test.key "funkwhale.test" "*.funkwhale.test" mkcert -install -cert-file compose/var/test.crt -key-file compose/var/test.key "funkwhale.test" "*.funkwhale.test"
``` ```
It will be used by Træefik to secure connections, which is needed for It will be used by Træefik to secure connections, which is needed for
ActivityPub to work locally. ActivityPub to work locally.
@ -257,43 +264,113 @@ Review the configuration:
docker compose config docker compose config
``` ```
### Set up local data for development ## Set up example data for development
You can create local data to mimic a live environment. You can create local data to simulate a live environment. We are providing a procedure to create fake data to populate the database. The following command creates 25 artists with random albums, tracks, and metadata.
Add some fake data to populate the database. The following command creates 25 artists with random albums, tracks, and metadata.
```sh ```sh
command="from funkwhale_api.music import fake_data; fake_data.create_data()" docker compose run --rm -T api \
echo $command | docker compose run --rm -T api funkwhale-manage shell -i python funkwhale-manage shell -i python \
<<< \
"from funkwhale_api.music import fake_data; fake_data.create_data()"
``` ```
This will launch a development funkwhale instance with a super user having `COMPOSE_PROJECT_NAME` as username and `funkwhale` as password. Libraries, listenings and music data will be associated with the superuser : The generated tracks do not contain any audio and are here for testing purposes of metadata handling only.
## Set up local documentation
To build the documentation locally run:
```sh ```sh
export COMPOSE_PROJECT_NAME=node1 ; export VUE_PORT=8882 ; docker compose run --rm api funkwhale-manage migrate ; echo "from funkwhale_api.music import fake_data; fake_data.create_data(super_user_name=\"$COMPOSE_PROJECT_NAME\")" | docker compose run --rm -T api funkwhale-manage shell -i python docker compose -f compose.docs.yml up -d
``` ```
```{note} The documentation is then accessible at <https://docs.funkwhale.test>. The OpenAPI schema is available at <https://openapi.funkwhale.test>. The UI component library will be served at <https://ui.funkwhale.test>.
Username `funkwhale` is not permitted. You need to export COMPOSE_PROJECT_NAME to make sure it's different from `funkwhale`
Fallback ports are available for the documentation at <http://localhost:8001/>, for the OpenAPI schema at <http://localhost:8002/> and for the UI component library at <http://localhost:8003/>.
Maintain their life cycle with similar commands to those used to
[set up network services (point 2.)](#set-up-network-services).
## Lifecycle
Your local Funkwhale development environment will undergo various lifecycles
during its existence. This is due to the immutable nature of containers and the
way how we selectively apply state to them.
Make yourself familiar with the following lifecycle commands to get an
impression of the phase changes that happen in your local deployment.
::::{tab-set}
:::{tab-item} Application lifecycle
Build the application images, which will be used to create containers later.
This is often also needed after switching a branch or pulling new commits.
```sh
docker compose build
``` ```
### Lifecycle > Selectively rebuild container images.
>
> ```sh
> docker compose build api
> ```
Start the whole composition detached (`-d`) in the background:
```sh
docker compose up -d
```
List running containers:
```sh
docker compose ps
```
> You can use the `-f` flag behind the `compose` command to target alternative
> compositions:
>
> ```sh
> docker compose -f compose.net.yml ps
> docker compose -f compose.docs.yml ps -a
> ```
Recycle individual containers: Recycle individual containers:
```sh ```sh
docker compose rm -sf api celeryworker; docker compose up -d api celeryworker docker compose up -d --force-recreate app api
``` ```
Once you're done with the containers, you can stop them all: > Alternative, if the previous does not work as expected:
>
> ```sh
> docker compose rm -sf api worker; docker compose up -d api worker
> ```
Once you're done for the day, you can stop them all:
```sh ```sh
docker compose stop docker compose stop
``` ```
If you want to destroy your containers, run the following: List all containers, including expectedly or unexpectedly exited ones:
```sh
docker compose ps -a
```
**The following commands are destructive.**
Stop running containers and remove all of them, but keep the network:
```sh
docker compose rm -sf
```
If you want to destroy your containers and the network, run:
```sh ```sh
docker compose down docker compose down
@ -305,22 +382,62 @@ Destroy all state of your containers:
docker compose down --volumes docker compose down --volumes
``` ```
Remove all state of all Funkwhale-related containers, incl. from additional **Remove all state of all Funkwhale containers**, incl. from any additional
instances: instance:
```sh ```sh
rm -rf .state/ rm -rf .state/
``` ```
### Running multiple instances :::
:::{tab-item} Container engine lifecycle
Your container engine, often Docker or Podman, over time will accumulate state.
This state comes in form of images, networks and containers. Due to the nature
of immutable infrastructure, assets are often replaced with newer generations.
For images this means that old ones pile up, while new ones are being used with
current containers.
Delete dangling images regularly.
```sh
docker image prune
```
Delete all unused images, which are not used by any container, regularly.
```sh
docker image prune -a
```
Delete all unused networks regularly.
```sh
docker network prune
```
In case you run into trouble, as a last resort you can always reset the state of
your container engine completely.
```sh
docker system prune -a
```
:::
::::
## Running multiple instances
Set up as many different projects as you need. Make sure the Set up as many different projects as you need. Make sure the
`COMPOSE_PROJECT_NAME` and `VUE_PORT` variables are unique per instance. `COMPOSE_PROJECT_NAME` and `VUE_PORT` variables are unique per instance.
```sh ```sh
export COMPOSE_PROJECT_NAME=node2 export COMPOSE_PROJECT_NAME=node1
# VUE_PORT this has to be unique for each instance # VUE_PORT this has to be unique for each instance
export VUE_PORT=1234 export VUE_PORT=8081
docker compose run --rm api funkwhale-manage fw users create --superuser docker compose run --rm api funkwhale-manage fw users create --superuser
docker compose up -d docker compose up -d
``` ```
@ -333,23 +450,29 @@ You can access your project at `https://{COMPOSE_PROJECT_NAME}.funkwhale.test`.
You may as well address the different Compose projects by using ad hoc You may as well address the different Compose projects by using ad hoc
environment variables: environment variables:
``` ```sh
COMPOSE_PROJECT_NAME=node1 VUE_PORT=1234 docker compose run --rm api funkwhale-manage fw users create --superuser COMPOSE_PROJECT_NAME=node1 VUE_PORT=8081 docker compose run --rm api funkwhale-manage fw users create --superuser
COMPOSE_PROJECT_NAME=node1 VUE_PORT=1234 docker compose up -d COMPOSE_PROJECT_NAME=node1 VUE_PORT=8081 docker compose up -d
``` ```
The `node1` instance will be available at [https://node1.funkwhale.test](https://node1.funkwhale.test). The `node1` instance will be available at [https://node1.funkwhale.test](https://node1.funkwhale.test).
``` ```sh
COMPOSE_PROJECT_NAME=node2 VUE_PORT=1235 docker compose run --rm api funkwhale-manage fw users create --superuser COMPOSE_PROJECT_NAME=node2 VUE_PORT=8082 docker compose run --rm api funkwhale-manage fw users create --superuser
COMPOSE_PROJECT_NAME=node2 VUE_PORT=1235 docker compose up -d COMPOSE_PROJECT_NAME=node2 VUE_PORT=8082 docker compose up -d
``` ```
The `node2` instance will be available at [https://node2.funkwhale.test](https://node2.funkwhale.test). The `node2` instance will be available at [https://node2.funkwhale.test](https://node2.funkwhale.test).
Proceed freely with different sets of values for `COMPOSE_PROJECT_NAME` and Proceed freely with different sets of non-overlapping values for `COMPOSE_PROJECT_NAME` and
`VUE_PORT`. `VUE_PORT`.
As a rule of thumb, remember to:
- Prepend `COMPOSE_PROJECT_NAME=node1 VUE_PORT=8081` to the lifecycle commands `up` and `run` to execute containers of additional instances.
- Prepend `COMPOSE_PROJECT_NAME=node1` to any other `docker compose` command to work with the indicated instance.
By example, this mechanic also applies to the [set up of example data for development](#set-up-example-data-for-development) above.
::: :::
::::{tab-set} ::::{tab-set}
@ -408,19 +531,60 @@ to learn how else you may interact directly with containers, when needed.
:::: ::::
## Local documentation ## Updating local environments
To build the documentation locally run: During development you will find yourself switching between branches and pulling new configuration from your remotes, at least from `develop` and your feature branches.
If the `.env.example` file changed, you need to make sure all are present in your current environment `.env`.
```sh ```sh
docker compose -f compose.docs.yml up -d diff .env .env.example
``` ```
The documentation is then accessible at [https://docs.funkwhale.test](https://docs.funkwhale.test). The OpenAPI schema is available at [https://openapi.funkwhale.test](https://openapi.funkwhale.test). In most cases when (a) changes are present and (b) you did not customise or modify the setup, then you are able to simply copy the new version.
Fallback ports are available for the documentation at ```sh
[http://localhost:8001/](http://localhost:8001/) and for the OpenAPI schema at cp .env.example .env
[http://localhost:8002/](http://localhost:8002/). ```
Maintain their life cycle with similar commands to those used to In presence of customisations, you need to adapt the values manually to the example.
[set up auxiliary services (point 2.)](#set-up-auxiliary-services).
If any of the `Dockerfile` manifests changed, you need to rebuild the (affected) containers.
```sh
docker compose build
```
Then recreate the application containers.
```sh
docker compose up -d --force-recreate
```
For the additional instances, this reads:
```sh
COMPOSE_PROJECT_NAME=node1 VUE_PORT=8081 docker compose up -d --force-recreate
```
The build images from the primary instance will be reused here, since they
carry the same name and are expected to be at the same revision.
## Seeding additional instances
We provide a convenience method to initialise the additional Funkwhale instances with fake seed data altogether with a super user having `COMPOSE_PROJECT_NAME` as username and `funkwhale` as password. Libraries, listenings and music data will be associated to that superuser.
```sh
COMPOSE_PROJECT_NAME=node1 docker compose run --rm -T api \
funkwhale-manage shell -i python \
<<< \
"from os import getenv; from funkwhale_api.music import fake_data; fake_data.create_data(super_user_name=getenv('FUNKWHALE_HOSTNAME').split('.')[0])"
```
```{note}
The username `funkwhale` is not permitted, since it violates the password constraint of not being equal to the password. Therefore you need to export the `COMPOSE_PROJECT_NAME` to make sure the method is only run in cases where it will be different from `funkwhale`.
This step does not apply to the default instance when running `docker compose` without specifying a `COMPOSE_PROJECT_NAME`.
In this case follow the manual steps from above. First create a super user as described in [set up application services](#set-up-application-services) and then continue with the [set up of example data for development](#set-up-example-data-for-development).
```

View File

@ -11,4 +11,7 @@ RUN yarn install
COPY . . COPY . .
CMD ["yarn", "serve"] CMD [ "yarn", "serve" ]
HEALTHCHECK --start-period=30s --interval=10s --timeout=5s \
CMD wget --no-verbose --tries=1 --spider http://localhost:${VUE_PORT}/ || exit 1

View File

@ -19,7 +19,7 @@ upstream funkwhale-api {
{% if config.proxy_frontend %} {% if config.proxy_frontend %}
upstream funkwhale-front { upstream funkwhale-front {
server ${FUNKWHALE_FRONT_IP}:${FUNKWHALE_FRONT_PORT}; server ${FUNKWHALE_FRONT_HOST}:${FUNKWHALE_FRONT_PORT};
} }
{% endif %} {% endif %}