diff --git a/.gitignore b/.gitignore index e62b1ce62..b1a3b230b 100644 --- a/.gitignore +++ b/.gitignore @@ -95,4 +95,4 @@ _build front/src/translations.json front/src/translations/*.json front/locales/en_US/LC_MESSAGES/app.po -*.prof +*.prof \ No newline at end of file diff --git a/.gitpod.yml b/.gitpod.yml index 31b344fab..2d68532e6 100644 --- a/.gitpod.yml +++ b/.gitpod.yml @@ -1,67 +1,49 @@ +image: + file: .gitpod/Dockerfile + tasks: - - name: Docker + - name: Backend env: - COMPOSE_FILE: dev.yml + ENV_FILE: /workspace/funkwhale/.gitpod/.env + COMPOSE_FILE: /workspace/funkwhale/.gitpod/docker-compose.yml + before: | + cp .gitpod/gitpod.env .gitpod/.env + cd api init: | - # Install frontend depencencies locally - cd front - yarn install - cd .. + mkdir -p ../data/media/attachments ../data/music ../data/staticfiles + docker-compose up -d - # Prepare prebuild .env - echo "# Gitpod Environment Variables" > .env + poetry env use python + poetry install - # Prepare docker - docker network create federation - docker-compose pull - docker-compose build - docker-compose up -d postgres redis - sleep 10 # allow postgres and redis to initialize + gp ports await 5432 - # Prepare backend - docker-compose run --rm api python manage.py migrate - docker-compose run --rm api python manage.py createsuperuser --no-input --username gitpod --email gitpod@example.com - docker-compose run --rm api python manage.py fw users set --password "gitpod" gitpod --no-input - - # Compile frontend locales - docker-compose run --rm front yarn run i18n-compile - - # Start API to let script create an actor - docker-compose up -d nginx - gp ports await 8000 - - # Clone music repo - git clone https://dev.funkwhale.audio/funkwhale/catalog.git - sudo mv catalog/music data - sudo chown -R root:root data/music - rm -rf catalog - - # Login with cURL to create actor - python .gitpod/init_actor.py - - # Import music - docker-compose down - LIBRARY_ID=`cat .gitpod/create_library.py | docker-compose run --rm -T api python manage.py shell -i python` - docker-compose run --rm api python manage.py import_files $LIBRARY_ID "/music/" --recursive --noinput --in-place - - # Stop docker - docker-compose stop + poetry run python manage.py migrate + poetry run python manage.py gitpod init command: | - # Prepare workspace .env - echo "MEDIA_URL=`gp url 8000`/media/" >> .env - echo "STATIC_URL=`gp url 8000`/staticfiles/" >> .env - echo "FUNKWHALE_HOSTNAME=`gp url 8000 | sed 's#https://##'`" >> .env - echo "FUNKWHALE_PROTOCOL=https" >> .env - echo "GITPOD_WORKSPACE_URL=$GITPOD_WORKSPACE_URL" >> .env - echo "HMR_PORT=8000" >> .env - echo "VUE_APP_INSTANCE_URL=$VUE_APP_INSTANCE_URL" >> .env + echo "MEDIA_URL=`gp url 8000`/media/" >> ../.gitpod/.env + echo "STATIC_URL=`gp url 8000`/staticfiles/" >> ../.gitpod/.env + echo "FUNKWHALE_HOSTNAME=`gp url 8000 | sed 's#https://##'`" >> ../.gitpod/.env + echo "FUNKWHALE_PROTOCOL=https" >> ../.gitpod/.env - # Start app - docker-compose up front api nginx + docker-compose up -d + gp ports await 5432 + poetry run python manage.py collectstatic --no-input + poetry run python manage.py gitpod dev + + - name: Frontend + env: + HMR_PORT: 8000 + before: cd front + init: | + yarn install + yarn run i18n-compile + command: yarn dev --host 0.0.0.0 --base /front/ - name: Welcome to Funkwhale development! env: - COMPOSE_FILE: dev.yml + COMPOSE_FILE: /workspace/funkwhale/.gitpod/docker-compose.yml + ENV_FILE: /workspace/funkwhale/.gitpod/.env command: | clear echo "" @@ -78,12 +60,33 @@ ports: visibility: public onOpen: notify + - port: 5000 + visibility: private + onOpen: ignore + + - port: 5432 + visibility: private + onOpen: ignore + + - port: 5678 + visibility: private + onOpen: ignore + + - port: 6379 + visibility: private + onOpen: ignore + + - port: 8080 + visibility: private + onOpen: ignore + vscode: extensions: - lukashass.volar - - lextudio.restructuredtext - - trond-snekvik.simple-rst - ms-python.python - ms-toolsai.jupyter - ms-toolsai.jupyter-keymap - ms-toolsai.jupyter-renderers + - hbenl.vscode-test-explorer + - hbenl.test-adapter-converter + - littlefoxteam.vscode-python-test-adapter diff --git a/.gitpod/Dockerfile b/.gitpod/Dockerfile new file mode 100644 index 000000000..00573fd1b --- /dev/null +++ b/.gitpod/Dockerfile @@ -0,0 +1,9 @@ +FROM gitpod/workspace-full +USER gitpod + +RUN sudo apt update -y \ + && sudo apt install libsasl2-dev libldap2-dev libssl-dev ffmpeg -y + +RUN pip install poetry \ + && poetry config virtualenvs.create true \ + && poetry config virtualenvs.in-project true \ No newline at end of file diff --git a/.gitpod/create_library.py b/.gitpod/create_library.py deleted file mode 100644 index 7991417f7..000000000 --- a/.gitpod/create_library.py +++ /dev/null @@ -1,17 +0,0 @@ -from funkwhale_api.music.models import Library -from django.contrib.auth import get_user_model - -actor = get_user_model().objects.get(username='gitpod').actor - -try: - library = Library.objects.get(actor=actor) -except: - # Create library - library = Library() - library.actor = actor - library.description = 'Libre music to build a starter catalog for your instance' - library.name = 'funkwhale/catalog' - library.privacy_level = 'everyone' - library.save() - -print(str(library.uuid)) \ No newline at end of file diff --git a/.gitpod/docker-compose.yml b/.gitpod/docker-compose.yml new file mode 100644 index 000000000..2fe85a9a5 --- /dev/null +++ b/.gitpod/docker-compose.yml @@ -0,0 +1,43 @@ +version: '3' + +services: + postgres: + image: postgres:14-alpine + environment: + - "POSTGRES_HOST_AUTH_METHOD=trust" + volumes: + - "../data/postgres:/var/lib/postgresql/data" + ports: + - 5432:5432 + + redis: + image: redis:7-alpine + volumes: + - "../data/redis:/data" + ports: + - 6379:6379 + + nginx: + command: /entrypoint.sh + env_file: + - ./.env + image: nginx + ports: + - 8000:80 + extra_hosts: + - host.docker.internal:host-gateway + environment: + - "NGINX_MAX_BODY_SIZE=100M" + - "FUNKWHALE_API_IP=host.docker.internal" + - "FUNKWHALE_API_PORT=5000" + - "FUNKWHALE_FRONT_IP=host.docker.internal" + - "FUNKWHALE_FRONT_PORT=8080" + - "FUNKWHALE_HOSTNAME=${FUNKWHALE_HOSTNAME-host.docker.internal}" + volumes: + - ../data/media:/protected/media:ro + - ../data/music:/music:ro + - ../data/staticfiles:/staticfiles:ro + - ../deploy/funkwhale_proxy.conf:/etc/nginx/funkwhale_proxy.conf:ro + - ../docker/nginx/conf.dev:/etc/nginx/nginx.conf.template:ro + - ../docker/nginx/entrypoint.sh:/entrypoint.sh:ro + - ../front:/frontend:ro \ No newline at end of file diff --git a/.gitpod/gitpod.env b/.gitpod/gitpod.env new file mode 100644 index 000000000..cda36d82a --- /dev/null +++ b/.gitpod/gitpod.env @@ -0,0 +1,25 @@ +# Dev Environment Variables +DJANGO_ALLOWED_HOSTS=.funkwhale.test,localhost,nginx,0.0.0.0,127.0.0.1,.gitpod.io +DJANGO_SETTINGS_MODULE=config.settings.local +C_FORCE_ROOT=true +BROWSABLE_API_ENABLED=True +FORWARDED_PROTO=http +LDAP_ENABLED=False +FUNKWHALE_SPA_HTML_ROOT=http://localhost:8000/front/ +FUNKWHALE_URL=http://localhost:8000/ +MUSIC_DIRECTORY_PATH=/workspace/funkwhale/data/music +STATIC_ROOT=/workspace/funkwhale/data/staticfiles/ +MEDIA_ROOT=/workspace/funkwhale/data/media/ + +PYTHONTRACEMALLOC=0 +PYTHONDONTWRITEBYTECODE=true + +POSTGRES_VERSION=14 +DEBUG=true + + +# Django Environment Variables +DATABASE_URL=postgresql://postgres@localhost:5432/postgres +DJANGO_SECRET_KEY=gitpod + +# Gitpod Environment Variables diff --git a/.gitpod/init_actor.py b/.gitpod/init_actor.py deleted file mode 100644 index 7dde3945e..000000000 --- a/.gitpod/init_actor.py +++ /dev/null @@ -1,21 +0,0 @@ -import requests - -# Login to initialize user actor -req = requests.Session() - -res = req.get('http://localhost:8000/login') -print(res.status_code, res.cookies) -token = res.cookies['csrftoken'] - -res = req.post('http://localhost:8000/api/v1/users/login', data={ - 'username': 'gitpod', - 'password': 'gitpod', - 'csrfmiddlewaretoken': token, -}) -print(res.status_code, res.content) - -res = req.get('http://localhost:8000/') -print(res.status_code) - -if res.status_code == 401: - exit(1) \ No newline at end of file diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 000000000..fa20725c9 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,34 @@ +{ + // Use IntelliSense to learn about possible attributes. + // Hover to view descriptions of existing attributes. + // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387 + "version": "0.2.0", + "configurations": [ + { + "name": "Attach python debugger", + "type": "python", + "request": "attach", + "connect": { + "host": "localhost", + "port": 5678 + }, + "django": true + }, + { + "name": "Debug python", + "type": "python", + "request": "launch", + "module": "uvicorn", + "cwd": "${workspaceFolder}/api", + "envFile": "${workspaceFolder}/.gitpod/.env", + "args": [ + "--reload", "config.asgi:application", + "--host", "0.0.0.0", + "--port", "5000", + "--reload-dir", "config/", + "--reload-dir", "funkwhale_api/" + ], + "django": true + } + ] +} \ No newline at end of file diff --git a/.vscode/settings.json b/.vscode/settings.json index a7d0fc7b7..7319f5180 100644 --- a/.vscode/settings.json +++ b/.vscode/settings.json @@ -1,3 +1,11 @@ { - "esbonio.sphinx.confDir": "" + "python.defaultInterpreterPath": "/workspace/funkwhale/api/.venv/bin/python", + "python.testing.cwd": "/workspace/funkwhale/api", + "python.envFile": "/workspace/funkwhale/.gitpod/.env", + "python.testing.pytestArgs": [ + "--cov=funkwhale_api", + "tests/" + ], + "python.testing.unittestEnabled": false, + "python.testing.pytestEnabled": true } \ No newline at end of file diff --git a/api/funkwhale_api/common/management/commands/gitpod.py b/api/funkwhale_api/common/management/commands/gitpod.py new file mode 100644 index 000000000..46561220c --- /dev/null +++ b/api/funkwhale_api/common/management/commands/gitpod.py @@ -0,0 +1,72 @@ +from django.core.management.commands.migrate import Command as BaseCommand +from django.core.management import call_command +from funkwhale_api.music.models import Library +from funkwhale_api.users.models import User +import uvicorn +import debugpy +import os + + +class Command(BaseCommand): + help = "Manage gitpod environment" + + def add_arguments(self, parser): + parser.add_argument("command", nargs="?", type=str) + + def handle(self, *args, **options): + command = options["command"] + + if not command: + return self.show_help() + + if command == "init": + return self.init() + + if command == "dev": + return self.dev() + + def show_help(self): + self.stdout.write("") + self.stdout.write("Available commands:") + self.stdout.write("init - Initialize gitpod workspace") + self.stdout.write("dev - Run Funkwhale in development mode with debug server") + self.stdout.write("") + + def init(self): + try: + user = User.objects.get(username="gitpod") + except Exception: + call_command("createsuperuser", username="gitpod", email="gitpod@example.com", no_input=False) + user = User.objects.get(username="gitpod") + + user.set_password('gitpod') + if not user.actor: + user.create_actor() + + user.save() + + # Download music catalog + os.system("git clone https://dev.funkwhale.audio/funkwhale/catalog.git /tmp/catalog") + os.system("mv -f /tmp/catalog/music /workspace/funkwhale/data") + os.system("rm -rf /tmp/catalog/music") + + # # Import music catalog into library + call_command("script", "migrate_to_user_libraries", no_input=False) + call_command( + "import_files", + Library.objects.get(actor=user.actor).uuid, + "/workspace/funkwhale/data/music/", + recursive=True, + in_place=True, + no_input=False, + ) + + def dev(self): + debugpy.listen(5678) + uvicorn.run( + "config.asgi:application", + host="0.0.0.0", + port=5000, + reload=True, + reload_dirs=["/workspace/funkwhale/api/config/", "/workspace/funkwhale/api/funkwhale_api/"], + ) diff --git a/api/funkwhale_api/music/management/commands/import_files.py b/api/funkwhale_api/music/management/commands/import_files.py index 1c1d0917c..cc56e07a9 100644 --- a/api/funkwhale_api/music/management/commands/import_files.py +++ b/api/funkwhale_api/music/management/commands/import_files.py @@ -279,7 +279,7 @@ class Command(BaseCommand): if p and not import_path.startswith(p): raise CommandError( "Importing in-place only works if importing " - "from {} (MUSIC_DIRECTORY_PATH), as this directory" + "from {} (MUSIC_DIRECTORY_PATH), as this directory " "needs to be accessible by the webserver." "Culprit: {}".format(p, import_path) ) diff --git a/api/poetry.lock b/api/poetry.lock index 1bbdaccc0..355f0fea8 100644 --- a/api/poetry.lock +++ b/api/poetry.lock @@ -529,6 +529,14 @@ twisted = {version = ">=18.7", extras = ["tls"]} [package.extras] tests = ["hypothesis (==4.23)", "pytest (>=3.10,<4.0)", "pytest-asyncio (>=0.8,<1.0)"] +[[package]] +name = "debugpy" +version = "1.6.2" +description = "An implementation of the Debug Adapter Protocol for Python" +category = "dev" +optional = false +python-versions = ">=3.7" + [[package]] name = "decorator" version = "5.1.1" @@ -2120,7 +2128,7 @@ testing = ["coverage (>=5.0.3)", "zope.event", "zope.testing"] [metadata] lock-version = "1.1" python-versions = "^3.7" -content-hash = "66a96848a355caf841c42228ca2f411fef64f2507cf91d6d056038b2d2a2c22b" +content-hash = "f09ed385656e74fcd8d2c68ef9c43768de5459eed452dc9a4544df19894b7bfe" [metadata.files] aiohttp = [ @@ -2470,6 +2478,26 @@ daphne = [ {file = "daphne-3.0.2-py3-none-any.whl", hash = "sha256:a9af943c79717bc52fe64a3c236ae5d3adccc8b5be19c881b442d2c3db233393"}, {file = "daphne-3.0.2.tar.gz", hash = "sha256:76ffae916ba3aa66b46996c14fa713e46004788167a4873d647544e750e0e99f"}, ] +debugpy = [ + {file = "debugpy-1.6.2-cp310-cp310-macosx_10_15_universal2.whl", hash = "sha256:77a47d596ce8c69673d5f0c9876a80cb5a6cbc964f3b31b2d44683c7c01b6634"}, + {file = "debugpy-1.6.2-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:726e5cc0ed5bc63e821dc371d88ddae5cba85e2ad207bf5fefc808b29421cb4c"}, + {file = "debugpy-1.6.2-cp310-cp310-win32.whl", hash = "sha256:9809bd1cdc0026fab711e280e0cb5d8f89ae5f4f74701aba5bda9a20a6afb567"}, + {file = "debugpy-1.6.2-cp310-cp310-win_amd64.whl", hash = "sha256:40741d4bbf59baca1e97a5123514afcc036423caae5f24db23a865c0b4167c34"}, + {file = "debugpy-1.6.2-cp37-cp37m-macosx_10_15_x86_64.whl", hash = "sha256:67749e972213c395647a8798cc8377646e581e1fe97d0b1b7607e6b112ae4511"}, + {file = "debugpy-1.6.2-cp37-cp37m-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4e3c43d650a1e5fa7110af380fb59061bcba1e7348c00237e7473c55ae499b96"}, + {file = "debugpy-1.6.2-cp37-cp37m-win32.whl", hash = "sha256:9e572c2ac3dd93f3f1a038a9226e7cc0d7326b8d345c9b9ce6fbf9cb9822e314"}, + {file = "debugpy-1.6.2-cp37-cp37m-win_amd64.whl", hash = "sha256:ac5d9e625d291a041ff3eaf65bdb816eb79a5b204cf9f1ffaf9617c0eadf96fa"}, + {file = "debugpy-1.6.2-cp38-cp38-macosx_10_15_x86_64.whl", hash = "sha256:9f72435bc9a2026a35a41221beff853dd4b6b17567ba9b9d349ee9512eb71ce6"}, + {file = "debugpy-1.6.2-cp38-cp38-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:aaf579de5ecd02634d601d7cf5b6baae5f5bab89a55ef78e0904d766ef477729"}, + {file = "debugpy-1.6.2-cp38-cp38-win32.whl", hash = "sha256:0984086a670f46c75b5046b39a55f34e4120bee78928ac4c3c7f1c7b8be1d8be"}, + {file = "debugpy-1.6.2-cp38-cp38-win_amd64.whl", hash = "sha256:19337bb8ff87da2535ac00ea3877ceaf40ff3c681421d1a96ab4d67dad031a16"}, + {file = "debugpy-1.6.2-cp39-cp39-macosx_10_15_x86_64.whl", hash = "sha256:163f282287ce68b00a51e9dcd7ad461ef288d740dcb3a2f22c01c62f31b62696"}, + {file = "debugpy-1.6.2-cp39-cp39-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:4909bb2f8e5c8fe33d6ec5b7764100b494289252ebe94ec7838b30467435f1cb"}, + {file = "debugpy-1.6.2-cp39-cp39-win32.whl", hash = "sha256:3b4657d3cd20aa454b62a70040524d3e785efc9a8488d16cd0e6caeb7b2a3f07"}, + {file = "debugpy-1.6.2-cp39-cp39-win_amd64.whl", hash = "sha256:79d9ac34542b830a7954ab111ad8a4c790f1f836b895d03223aea4216b739208"}, + {file = "debugpy-1.6.2-py2.py3-none-any.whl", hash = "sha256:0bfdcf261f97a603d7ef7ab6972cdf7136201fde93d19bf3f917d0d2e43a5694"}, + {file = "debugpy-1.6.2.zip", hash = "sha256:e6047272e97a11aa6898138c1c88c8cf61838deeb2a4f0a74e63bb567f8dafc6"}, +] decorator = [ {file = "decorator-5.1.1-py3-none-any.whl", hash = "sha256:b8c3f85900b9dc423225913c5aace94729fe1fa9763b38939a95226f02d37186"}, {file = "decorator-5.1.1.tar.gz", hash = "sha256:637996211036b6385ef91435e4fae22989472f9d571faba8927ba8253acbc330"}, diff --git a/api/pyproject.toml b/api/pyproject.toml index cb1810993..2757aab27 100644 --- a/api/pyproject.toml +++ b/api/pyproject.toml @@ -57,7 +57,7 @@ django-auth-ldap = "==4.1.0" uvicorn = {version = "==0.17.6", extras = ["standard"]} django-cache-memoize = "0.1.10" requests-http-message-signatures = "==0.3.1" -drf-spectacular = "0.22.1" +drf-spectacular = "==0.22.1" [tool.poetry.dev-dependencies] flake8 = "==3.9.2" @@ -80,6 +80,7 @@ aioresponses = "==0.7.3" prompt-toolkit = "==3.0.30" black = "==22.6.0" ipdb = "==0.13.9" +debugpy = "==1.6.2" [build-system] requires = ["poetry-core>=1.0.0"] diff --git a/changes/changelog.d/1875.enchancement b/changes/changelog.d/1875.enchancement new file mode 100644 index 000000000..0026c1ff4 --- /dev/null +++ b/changes/changelog.d/1875.enchancement @@ -0,0 +1 @@ +Add python debug and test support for gitpod \ No newline at end of file