Update the CONTRIBUTING file

Create sections in the documentation
environments/review-docs-archi-2rzv61/deployments/14145
Ciarán Ainsworth 2022-10-09 19:44:08 +02:00 zatwierdzone przez Georg Krause
rodzic 605b2fee9a
commit f67565c0bf
21 zmienionych plików z 1015 dodań i 920 usunięć

818
CONTRIBUTING.md 100644
Wyświetl plik

@ -0,0 +1,818 @@
# Contribute to Funkwhale's development
Thanks for your interest in contributing to Funkwhale! This document contains details on how to contribute to Funkwhale's codebase. If you'd like to contribute in other ways, check out our [contributor documentation](https://docs.funkwhale.audio).
```{contents}
:local:
:depth: 2
```
## Set up your development environment
There are several ways to set up your development environment depending on what you want to work on.
- Gitpod (recommended)
- Docker
- Vite (Frontend-only)
Follow the instructions in this guide to get set up.
### Gitpod
```{note}
You need a GitHub or GitLab.com account to log in to Gitpod.
```
Funkwhale has a Gitpod instance that gives you all the tools you need to work on Funkwhale's code. You can work on the code in-browser using a hosted VS Code install or open VS Code on your desktop over SSH.
You can open Gitpod directly by clicking the link below. This checks out the `develop` branch for you to work on directly.
[![Open in Gitpod](https://gitpod.io/button/open-in-gitpod.svg)](https://gitpod.io/#https://dev.funkwhale.audio/funkwhale/funkwhale)
If you want to work on a particular branch, commit, or merge request, you can do this straight from the GitLab interface. Select the arrow icon on the {guilabel}`Web IDE` button and select {guilabel}`Gitpod` to open Gitpod with the currently selected branch checked out.
![Select Gitpod as the default web IDE](/_static/images/select-gitpod-in-gitlab.png)
When you start Gitpod, it creates the following using the selected branch:
- A Funkwhale API instance
- A Funkwhale frontend instance
You can access the web app at `http://localhost:8000`. Log in with the following credentials:
- Username – `gitpod`
- Password – `gitpod`
#### Work on the frontend
By default, Gitpod spins up an entire Funkwhale stack. If you want to work only on the frontend:
1. Select `File` > `Open Folder`
2. Select `/workspace/funkwhale/front`
Gitpod starts a new Vite server on port 4000. This creates a frontend that isn't connected to any instance.
#### GitLab Workflow extension
Gitpod offers a GitLab workflow extension to help manage GitLab issues, merge requests, and pipelines. If you want to use it:
1. Navigate to the personal access token section of your [GitLab profile settings](https://dev.funkwhale.audio/-/profile/personal_access_tokens)
2. Create a personal access token with `api` and `read_user` scopes
3. Paste your token into your [Gitpod variables](https://gitpod.io/variables)
Use the following settings to automatically sign in to the extension with Gitpod. The `funkwhale/*` scope ensures you can use the settings for all Funkwhale-hosted projects.
```{list-table} Environment variables
:header-rows: 1
* - Name
- Value
- Scope
* - `GITLAB_WORKFLOW_INSTANCE_URL`
- `https://dev.funkwhale.audio`
- `funkwhale/*`
* - `GITLAB_WORKFLOW_TOKEN`
- Your token
- `funkwhale/*`
```
#### Configure custom instance URL
You can configure Gitpod to use your Funkwhale pod as the default server. This means you can test frontend changes on your pod without selecting it each time. To do this, add the following to your [Gitpod variables](https://gitpod.io/variables):
```{list-table} Environment variables
:header-rows: 1
* - Name
- Value
- Scope
* - `VUE_APP_INSTANCE_URL`
- `https://funkwhale.example.com`
- `funkwhale/funkwhale`
```
### Docker
Funkwhale can be run in Docker containers for local development. You can work on any part of the Funkwhale codebase and run the container setup to test your changes. To work with Docker:
1. [Install Docker](https://docs.docker.com/install)
2. [Install docker-compose](https://docs.docker.com/compose/install)
3. Clone the Funkwhale repository to your system. The `develop` branch is checked out by default
::::{tab-set}
:::{tab-item} SSH
```sh
git clone git@dev.funkwhale.audio/funkwhale/funkwhale.git
cd funkwhale
```
:::
:::{tab-item} HTTPS
```sh
git clone https://dev.funkwhale.audio/funkwhale/funkwhale.git
cd funkwhale
```
:::
::::
#### Set up your Docker environment
````{note}
Funkwhale provides a `dev.yml` file that contains the required docker-compose setup. You need to pass the `-f dev.yml` flag you run docker-compose commands to ensure it uses this file. If you don't want to add this each time, you can export it as a `COMPOSE_FILE` variable:
```sh
export COMPOSE_FILE=dev.yml
```
````
To set up your Docker environment:
1. Create a `.env` file to enable customization of your setup.
```sh
touch .env
```
2. Add the following variables to load images and enable access to Django admin pages:
```text
MEDIA_URL=http://localhost:8000/media/
STATIC_URL=http://localhost:8000/staticfiles/
```
3. Create a network for federation support
```sh
docker network create federation
```
Once you've set everything up, you need to build the containers. Run this command any time there are upstream changes or dependency changes to ensure you're up-to-date.
```sh
docker-compose -f dev.yml build
```
#### Set up the database
Funkwhale relies on a postgresql database to store information. To set this up, you need to run the `manage.py migrate` command:
```sh
docker-compose -f dev.yml run --rm api python manage.py migrate
```
This command creates all the required tables. You need to run this whenever there are changes to the API schema. You can run this at any time without causing issues.
#### Set up local data
You need to create some local data to mimic a production environment.
1. Create a superuser so you can log in to your local app:
```sh
docker-compose -f dev.yml run --rm api pythong manage.py createsuperuser
```
2. Add some fake data to populate the database. The following command creates 25 artists with random albums, tracks, and metadata.
```sh
artists=25 # Adds 25 fake artists
command="from funkwhale_api.music import fake_data; fake_data.create_data($artists)"
echo $command | docker-compose -f dev.yml run --rm -T api python manage.py shell -i python
```
#### Manage services
Once you have set up your containers, bring them up to start working on them.
1. Compile the translations:
```sh
docker-compose -f dev.yml run --rm front yarn run i18n-compile
```
2. Launch all services:
```sh
docker-compose -f dev.yml up front api nginx celeryworker
```
This gives you access to the following:
- The Funkwhale webapp on `http://localhost:8000`
- The Funkwhale API on `http://localhost:8000/api/v1`
- The Django admin interface on `http://localhost:8000/api/admin`
Once you're done with the containers, you can stop them all:
```sh
docker-compose -f dev.yml stop
```
If you want to destroy your containers, run the following:
```sh
docker-compose -f dev.yml down -v
```
#### Set up federation support
Working on federation features requires some additional setup. You need to do the following:
1. Update your DNS resolver to resolve all your .dev hostnames locally
2. Set up a reverse proxy (such as traefik) to catch .dev requests with a TLS certificate
3. Set up two or more local instances
To resolve hostnames locally, run the following:
::::{tab-set}
:::{tab-item} dnsmasq
```sh
echo "address=/test/172.17.0.1" | sudo tee /etc/dnsmasq.d/test.conf
sudo systemctl restart dnsmasq
```
:::
:::{tab-item} NetworkManager
```sh
echo "address=/test/172.17.0.1" | sudo tee /etc/NetworkManager/dnsmasq.d/test.conf
sudo systemctl restart NetworkManager
```
:::
::::
To add a wildcard certificate, copy the test certificate from the `docker/ssl` folder. This certificate is a wildcard for `*.funkwhale.test`
```sh
sudo cp docker/ssl/test.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
```
To run a reverse proxy for your app:
1. Add the following configuration to your `.env` file:
```text
# Remove any port binding so you can specify this per-instance
VUE_PORT_BINDING=
# Disable certificate validation
EXTERNAL_REQUESTS_VERIFY_SSL=false
# Ensure all links use https
FUNKWHALE_PROTOCOL=https
# Disable host ports binding for the nginx container so that traefik handles everything
NGINX_PORTS_MAPPING=80
```
2. Launch traefik using the bundled configuration:
```sh
docker-compose -f docker/traefik.yml up -d
```
3. Set up as many different projects as you need. Make sure the `COMPOSE_PROJECT_NAME` and `VUE_PORT` variables are unique per instance
```sh
export COMPOSE_PROJECT_NAME=node2
export VUE_PORT=1234 # this has to be unique for each instance
docker-compose -f dev.yml run --rm api python manage.py migrate
docker-compose -f dev.yml run --rm api python manage.py createsuperuser
docker-compose -f dev.yml up nginx api front nginx api celeryworker
```
You can access your project at `https://{COMPOSE_PROJECT_NAME}.funkwhale.test`.
### Vite
If you want to make changes to the frontend, you can use Vite to run a development server. This allows you to run a Funkwhale web app and see changes in real time
1. Clone the repository:
::::{tab-set}
:::{tab-item} SSH
```sh
git clone git@dev.funkwhale.audio/funkwhale/funkwhale.git
cd funkwhale/front
```
:::
:::{tab-item} HTTPS
```sh
git clone https://dev.funkwhale.audio/funkwhale/funkwhale.git
cd funkwhale/front
```
:::
::::
2. Install [Node.js](https://nodejs.org/en/download/package-manager/) and [Yarn](https://classic.yarnpkg.com/lang/en/docs/install/)
3. Install all dependencies:
```sh
yarn install
```
4. Compile the translations:
```sh
yarn i18n-compile
```
5. Launch the devlopment server:
```sh
yarn dev
```
You can access the Funkwhale web app at `http://localhost:8000/front`. Connect this app to your pod by selecting {guilabel}`Switch instance` in the sidebar.
## Contribute to the API
The Funkwhale API is the core of the Funkwhale ecosystem. It powers all actions in the Funkwhale app as well as other apps such as the CLI and mopidy plugin. The API is written in [Django rest framework](https://www.django-rest-framework.org/).
Before you start work on the API, you should open up a conversation in [the forum](https://forum.funkwhale.audio) to discuss the changes you want to make. All API changes need to be defined and scoped before code changes are made. If you are fixing a bug, you don't need to discuss this in the forum first.
Each API endpoint is made up of the following:
- Model – defines the shape of data and how it is stored in the database
- View – defines what data is reflected by an endpoint
- Serializer – defines how data is serialized and deserialized by the endpoint
The API directory is structured as follows:
- `config` – contains the project settings, URL structure, and web server gateway information setup
- `settings` – contains all Django settings files
- `funkwhale_api` – contains the Funkwhale API logic
- `pyproject.toml` – contains the Python requirements
- `tests` – contains all tests. This directory matches the structure of the `funkwhale_api` directory
### Write tests
You should write tests to ensure that your code does what you expect it to. We use [pytest](https://pytest.org) and [factory-boy](https://factoryboy.readthedocs.io) to power our API testing suite.
Writing tests is outside the scope of this documentation, but here are some useful links to help you get started:
- [A quick introduction to writing unit tests with pytest](https://semaphoreci.com/community/tutorials/testing-python-applications-with-pytest)
- [A complete guide to Test-Driven Development](https://www.obeythetestinggoat.com/)
- [pytest documentation](https://docs.pytest.org/en/latest)
- [pytest-mock documentation](https://pypi.org/project/pytest-mock)
- [factory-boy documentation](http://factoryboy.readthedocs.io)
Try to keep your tests small and focused. Each test should test a single function, so if you need to test multiple things you should write multiple tests.
```{note}
Test files must target a module and follow the `funkwhale_api` directory structure. If you write tests for `funkwhale_api/myapp/views.py`, you should put them in `tests/myapp/test_views.py`.
```
We provide utilities and fixtures to make writing tests as easy as possible. You can see the list of available fixtures by running `docker-compose -f dev.yml run --rm api pytest --fixtures`.
#### Factories
Each directory includes a `factories.py` file which contains factories for the models in the directory. You can use these to create arbitrary objects
```py
# funkwhale_api/myapp/users.py
def downgrade_user(user):
"""
A simple function that remove superuser status from users
and return True if user was actually downgraded
"""
downgraded = user.is_superuser
user.is_superuser = False
user.save()
return downgraded
# tests/myapp/test_users.py
from funkwhale_api.myapp import users
def test_downgrade_superuser(factories):
user = factories['users.User'](is_superuser=True)
downgraded = users.downgrade_user(user)
assert downgraded is True
assert user.is_superuser is False
def test_downgrade_normal_user_does_nothing(factories):
user = factories['users.User'](is_superuser=False)
downgraded = something.downgrade_user(user)
assert downgraded is False
assert user.is_superuser is False
```
#### Mocking
Use mocks to fake logic in your tests. This is useful when testing components that depend on one another.
```py
# funkwhale_api/myapp/notifications.py
def notify(email, message):
"""
A function that sends an e-mail to the given recipient
with the given message
"""
# our e-mail sending logic here
# ...
# funkwhale_api/myapp/users.py
from . import notifications
def downgrade_user(user):
"""
A simple function that remove superuser status from users
and return True if user was actually downgraded
"""
downgraded = user.is_superuser
user.is_superuser = False
user.save()
if downgraded:
notifications.notify(user.email, 'You have been downgraded!')
return downgraded
# tests/myapp/test_users.py
def test_downgrade_superuser_sends_email(factories, mocker):
"""
Your downgrade logic is already tested, however, we want to ensure
an e-mail is sent when user is downgraded, but we don't have any e-mail
server available in our testing environment. Thus, we need to mock
the e-mail sending process.
"""
mocked_notify = mocker.patch('funkwhale_api.myapp.notifications.notify')
user = factories['users.User'](is_superuser=True)
users.downgrade_user(user)
# here, we ensure our notify function was called with proper arguments
mocked_notify.assert_called_once_with(user.email, 'You have been downgraded')
def test_downgrade_not_superuser_skips_email(factories, mocker):
mocked_notify = mocker.patch('funkwhale_api.myapp.notifications.notify')
user = factories['users.User'](is_superuser=False)
users.downgrade_user(user)
# here, we ensure no e-mail was sent
mocked_notify.assert_not_called()
```
### Run tests
You can run all tests in the pytest suite with the following command:
```sh
docker-compose -f dev.yml run --rm api pytest
```
Run a specific test file by calling pytest against it:
```sh
docker-compose -f dev.yml run --rm api pytest tests/music/test_models.py
```
You can check the full list of options by passing the `-h` flag:
```sh
docker-compose -f dev.yml run --rm api pytest -h
```
## Contribute to the frontend
The Funkwhale frontend is a {abbr}`SPA (Single Page Application)` written in [Typescript](https://typescriptlang.org) and [Vue.js](https://vuejs.org).
### Styles
We currently use [Fomantic UI](https://fomantic-ui.com) as our UI framework. We customize this with our own SCSS files located in `front/src/styles/_main.scss`.
We apply changes to the Fomantic CSS files before we import them:
1. We replace hardcoded color values with CSS variables to make themin easier. For example: ``color: orange`` is replaced by ``color: var(--vibrant-color)``
2. We remove unused values from the CSS files to keep the size down
These changes are applied when you run `yarn install` through a `postinstall` hook. If you want to modify these changes, check the `front/scripts/fix-fomantic-css.py` script.
We plan to replace Fomantic with our own UI framework in the near future. Check our [Penpot](https://design.funkwhale.audio) to see what we've got planned.
### Components
Our [component library](https://ui.funkwhale.audio) contains reusable Vue components that you can add to the Funkwhale frontend. If you want to add a new component, check out [the repository](https://dev.funkwhale.audio/funkwhale/vui).
### Testing
The Funkwhale frontend contains some tests to catch errors before changes go live. The coverage is still fairly low, so we welcome any contributions.
To run the test suite, run the following command:
```sh
docker-compose -f dev.yml run --rm front yarn test:unit
```
To run tests as you make changes, launch the test suite with the `-w` flag:
```sh
docker-compose -f dev.yml run --rm front yarn test:unit -w
```
## Update UI copy
```{note}
Funkwhale is localized into several languages using [Weblate](https://translate.funkwhale.audio). You must make sure that any frontend strings are properly marked for localization. We use the [vue-i18n package](https://kazupon.github.io/vue-i18n/) to handle translation of frontend files.
```
All UI strings are stored in `front/locales/en.json` file. The file is structured to mimic the format of the repository. Each string should be labeled following the semantic naming for the item it applies to.
UI strings can be added to both the `<script>` and `<template>` part of a Vue file using following syntax:
::::{tab-set}
:::{tab-item} Locale file
```json
{
"components": {
"About": {
"title": "About",
"header": {
"funkwhale": "A social platform to enjoy and share music"
},
"button": {
"cancel": "Cancel"
}
}
}
}
```
:::
:::{tab-item} Script
```typescript
import { useI18n } from 'vue-i18n'
//...
const { t } = useI18n()
//...
const labels = computed(() => ({
title: t('components.About.title')
}))
```
:::
:::{tab-item} Template
```html
<h2>
{{ $t('components.About.header.funkwhale') }}
</h2>
<button>
{{ $t('components.About.button.cancel') }}
</button>
```
:::
::::
Some strings change depending on whether they are plural or not. You can create plural strings using the [vue-i18n pluralization syntax](https://kazupon.github.io/vue-i18n/guide/pluralization.html)
::::{tab-set}
:::{tab-item} Locale file
```json
"components": {
"audio": {
"ChannelCard": {
"meta": {
"episodes": "No episodes | {episode_count} episode | {episode_count} episodes",
"tracks": "No tracks | {tracks_count} track | {tracks_count} tracks"
}
}
}
}
```
:::
:::{tab-item} Template
```html
<div class="description">
<span
v-if="object.artist?.content_category === 'podcast'"
class="meta ellipsis"
>
{{ $t('components.audio.ChannelCard.meta.episodes', {episode_count: object.artist.tracks_count}) }}
</span>
<span
v-else
>
{{ $t('components.audio.ChannelCard.meta.tracks', {tracks_count: object.artist?.tracks_count}) }}
</span>
<tags-list
label-classes="tiny"
:truncate-size="20"
:limit="2"
:show-more="false"
:tags="object.artist?.tags ?? []"
/>
</div>
```
:::
::::
## Git workflow
Funkwhale uses GitLab's merge requests to manage changes. The workflow looks like this:
1. Assign the issue you are working on to yourself, or create one if it doesn't exist
2. Create a fork of the project
3. Check out the `develop` branch. If you're making a minor change (such as fixing a typo) you can check out the `stable` branch
4. Create a new branch based on the checked out branch. Make sure to give your branch a meaningful name and include the issue number if required
5. Work on your changes locally. Try to keep each commit small to make reviews easier
6. Add a changelog fragment summarizing your changes
7. Push your branch
8. Create a merge request in the GitLab frontend
9. We'll review your request and feed back
```{mermaid}
%%{init: { 'gitGraph': {'mainBranchName': 'stable'} } }%%
gitGraph
commit
branch develop
commit
commit
branch feature
commit
commit
checkout develop
merge feature
commit
checkout stable
merge develop
```
## Changelog fragments
We try to add changelog fragments when we make changes so that we can show users what we've done. These fragments are small text files that contain a summary of changes. When we make a release, we compile these into a full changelog using [towncrier](https://pypi.org/project/towncrier/).
Each changelog fragment should contain a short and meaningful summary of changes and include the issue number (where applicable). For example:
```text
Fixed broken audio player on Chrome 42 for ogg files (#567)
```
If there's no issue, insert the merge request identifier instead:
```text
Fixed a typo in landing page copy (!342)
```
### Naming
Changelog fragments use the following naming convention: `changes/changelog.d/<name>.category>`. The `<name>` can be anything that describes your work, or the issue ID. The category can be one of the following:
- `feature` – a new feature
- `enhancement` – an extension of an existing feature
- `bugfix` – a bugfix or patch
- `doc` – new documentation
- `i18n` – internationalization-related work
- `misc` – any work that doesn't fit into the above categories
You can create these files manually or use the following snippet to create a fragment:
```sh
issue="42"
content="Fixed an overflowing issue on small resolutions (#$issue)"
category="bugfix"
echo "$content ($issue)" > changes/changelog.d/$issue.$category
```
## Make a release
Once we're ready to release a new version of the software, we can use the following process:
1. Export the new release version
```sh
export NEXT_RELEASE=1.3.0
```
2. Export the previous release version
```sh
export PREVIOUS_RELEASE=1.2.9
```
3. Pull the latest version of the `develop` branch. Use `stable` if you're releasing a bugfix.
::::{tab-set}
:::{tab-item} Bugfix release
:sync: bugfix
```sh
git checkout stable
git pull
```
:::
:::{tab-item} Feature release
:sync: feature
```sh
git checkout develop
git pull
```
:::
::::
4. Compile the changelog
```sh
towncrier build --version $NEXT_RELEASE
```
5. Check the output and fix typos and mistakes
6. Add a list of contributors
```sh
python3 scripts/get-contributions-stats.py $NEXT_RELEASE # Output a list of contributors
git log $PREVIOUS_RELEASE.. --format="- %aN" --reverse | sort | uniq # Get a list of all commit authors
nano CHANGELOG # Add these lists to the CHANGELOG
```
7. Update the `__version__` variable to the next release version
```sh
nano api/funkwhale_api/__init__.py
```
8. Commit all changes
```sh
git add .
git commit -m "Version bump and changelog for $NEXT_RELEASE"
```
9. Create a tag
```sh
git tag $NEXT_RELEASE
```
10. Publish the new tag to GitLab
```sh
git push --tags && git push
```
11. Merge your changes into the alternate branch
::::{tab-set}
:::{tab-item} Bugfix release
:sync: bugfix
```sh
git checkout develop && git merge stable && git push
```
:::
:::{tab-item} Feature release
:sync: feature
```sh
git checkout stable && git merge develop && git push
```
:::
::::
Don't forget to create a blog post to announce the new release!

Wyświetl plik

@ -1,878 +0,0 @@
Contribute to Funkwhale development
===================================
First of all, thank you for your interest in the project! We really
appreciate the fact that you're about to take some time to read this
and hack on the project.
This document will guide you through common operations such as:
- Setup your development environment
- Working on your first issue
- Writing unit tests to validate your work
- Submit your work
The quickest way to contribute to the project is through Gitpod!
----------------------------------------------------------------
.. image:: https://gitpod.io/button/open-in-gitpod.svg
:alt: Open in Gitpod
:target: https://gitpod.io/#https://dev.funkwhale.audio/funkwhale/funkwhale
When you click the button above, you will be redirected to the gitpod.io site. Here you
can sign in with your Gitlab or GitHub account and a new workspace will be automatically
created for you.
The workspace will checkout current ``development`` branch and run a Funkwhale instance
with both frontend and backend started so you can jump straight into development.
The provided backend instance has a default admin user ``gitpod`` authenticated with
password ``gitpod``.
In case you want to develop only frontend, you can go to ``File > Open Folder`` and navigate to
``/workspace/funkwhale/front``. This will start a new vite server on port 4000. This server
integrates well with the extension provided by Gitpod but is disconnected from any instance by default.
Usage with Gitlab
^^^^^^^^^^^^^^^^^
We understand that having to go to the contribution guide and clicking the ``Open in Gitpod``
is not an optimal way to create new branches or test existing ones. That's why we've enabled the
Gitpod integration on our Gitlab server.
You can now simply choose to use Gitpod instead of Gitlab Web IDE on any of branch, commit or merge request:
.. image:: ./_static/images/select-gitpod-in-gitlab.png
:alt: Select Gitpod as the default Web IDE.
Gitlab Workflow extension
^^^^^^^^^^^^^^^^^^^^^^^^^
Gitpod offers a `Gitlab Workflow` extension that can help with managing Gitlab issues,
merge requests and pipelines. To use it you need to create a personal access token with
``api`` and ``read_user`` scopes. You can create it by visiting `your Gitlab profile settings <https://dev.funkwhale.audio/-/profile/personal_access_tokens>`_.
Setting the token through the extension is not an optimal approach as you'd need to do this
every single time you start a new workspace. However you can work around this by creating
user environment variables in `your Gitpod settings <https://gitpod.io/variables>`_.
When you configure your environment variables as follows, you will be signed in to the extension
automatically in old and new workspaces.
.. list-table:: Environment variables
:header-rows: 1
* - Name
- Value
- Scope
* - ``GITLAB_WORKFLOW_INSTANCE_URL``
- ``https://dev.funkwhale.audio``
- ``funkwhale/*``
* - ``GITLAB_WORKFLOW_TOKEN``
- ``TOKEN``
- ``funkwhale/*``
The scope ``funkwhale/*`` will ensure that you will be signed into our instance on every
project that we're hosting, not only Funkwhale itself.
Custom instance url
^^^^^^^^^^^^^^^^^^^
If you want to preview changes on your own Funkwhale server, you can add your domain as an environment variable.
This allows you to test your frontend changes against your domain without setting this value each time.
To add your domain, set a user environment variable in `your Gitpod settings <https://gitpod.io/variables>`_.
.. list-table:: Environment variable
:header-rows: 1
* - Name
- Value
- Scope
* - ``VUE_APP_INSTANCE_URL``
- ``https://funkwhale.example.com``
- ``funkwhale/funkwhale``
The scope ``funkwhale/funkwhale`` ensures that this variable only works on Funkwhale.
A quick path to contribute on the front-end
-------------------------------------------
The next sections of this document include a full installation guide to help
you setup a local, development version of Funkwhale. If you only want to fix small things
on the front-end, and don't want to manage a full development environment, there is another way.
As the front-end can work with any Funkwhale server, you can work with the front-end only,
and make it talk with an existing instance (like the demo one, or you own instance, if you have one).
If even that is too much for you, you can also make your changes without any development environment,
and open a merge request. We will be able to review your work easily by spawning automatically a
live version of your changes, thanks to Gitlab Review apps.
Setup front-end only development environment
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
1. Clone the repository::
git clone ssh://git@dev.funkwhale.audio/funkwhale/funkwhale.git
cd funkwhale
cd front
2. Install `nodejs <https://nodejs.org/en/download/package-manager/>`_ and `yarn <https://yarnpkg.com/lang/en/docs/install/#debian-stable>`_
3. Install the dependencies::
yarn install
4. Compile the translations::
yarn i18n-compile
5. Launch the development server::
# this will serve the front-end on http://localhost:8000
VUE_PORT=8000 yarn dev
6. Make the front-end talk with an existing server (like https://demo.funkwhale.audio or https://open.audio),
by clicking on the corresponding link in the footer
7. Start hacking!
Setup your development environment
----------------------------------
If you want to fix a bug or implement a feature, you'll need
to run a local, development copy of funkwhale.
We provide a docker based development environment, which should
be both easy to setup and work similarly regardless of your
development machine setup.
Instructions for bare-metal setup will come in the future (Merge requests
are welcome).
Installing docker and docker-compose
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
This is already cover in the relevant documentations:
- https://docs.docker.com/install/
- https://docs.docker.com/compose/install/
.. note::
If you are on Fedora, know that you can't use `podman` or `moby-engine` to set up the development environment.
Stick to `docker-ce` and you'll be fine.
Cloning the project
^^^^^^^^^^^^^^^^^^^
Visit https://dev.funkwhale.audio/funkwhale/funkwhale and clone the repository using SSH or HTTPS. Example using SSH::
git clone ssh://git@dev.funkwhale.audio/funkwhale/funkwhale.git
cd funkwhale
.. note::
As of January 2020, the SSH fingerprints of our Gitlab server are the following::
$ ssh-keyscan dev.funkwhale.audio | ssh-keygen -lf -
# dev.funkwhale.audio:22 SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u6
# dev.funkwhale.audio:22 SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u6
# dev.funkwhale.audio:22 SSH-2.0-OpenSSH_7.4p1 Debian-10+deb9u6
2048 SHA256:WEZ546nkMhB9yV9lyDZZcEeN/IfriyhU8+mj7Cz/+sU dev.funkwhale.audio (RSA)
256 SHA256:dEhAo+1ImjC98hSqVdnkwVleheCulV8xIsV1eKUcig0 dev.funkwhale.audio (ECDSA)
256 SHA256:/AxZwOSP74hlNKCHzmu9Trlp9zVGTrsJOV+zet1hYyQ dev.funkwhale.audio (ED25519)
A note about branches
^^^^^^^^^^^^^^^^^^^^^
Next release development occurs on the "develop" branch, and releases are made on the "stable" branch. Therefore, when submitting Merge Requests, ensure you are merging on the develop branch.
Working with docker
^^^^^^^^^^^^^^^^^^^
In development, we use the docker-compose file named ``dev.yml``, and this is why all our docker-compose commands will look like this::
docker-compose -f dev.yml logs
If you do not want to add the ``-f dev.yml`` snippet every time, you can run this command before starting your work::
export COMPOSE_FILE=dev.yml
Creating your env file
^^^^^^^^^^^^^^^^^^^^^^
We provide a working .env.dev configuration file that is suitable for
development. However, to enable customization on your machine, you should
also create a .env file that will hold your personal environment
variables (those will not be commited to the project).
Create it like this::
touch .env
These two environment variables must be included for the images to load in front-end and django admin pages::
MEDIA_URL=http://localhost:8000/media/
STATIC_URL=http://localhost:8000/staticfiles/
Create docker network
^^^^^^^^^^^^^^^^^^^^^
Create the federation network::
docker network create federation
Building the containers
^^^^^^^^^^^^^^^^^^^^^^^
On your initial clone, or if there have been some changes in the
app dependencies, you will have to rebuild your containers. This is done
via the following command::
docker-compose -f dev.yml build
Database management
^^^^^^^^^^^^^^^^^^^
To setup funkwhale's database schema, run this::
docker-compose -f dev.yml run --rm api python manage.py migrate
This will create all the tables needed for the API to run properly.
You will also need to run this whenever changes are made on the database
schema.
It is safe to run this command multiple times, so you can run it whenever
you fetch develop.
Development data
^^^^^^^^^^^^^^^^
You'll need at least an admin user and some artists/tracks/albums to work
locally.
Create an admin user with the following command::
docker-compose -f dev.yml run --rm api python manage.py createsuperuser
Injecting fake data is done by running the following script::
artists=25
command="from funkwhale_api.music import fake_data; fake_data.create_data($artists)"
echo $command | docker-compose -f dev.yml run --rm -T api python manage.py shell -i python
The previous command will create 25 artists with random albums, tracks
and metadata.
Launch all services
^^^^^^^^^^^^^^^^^^^
Before the first Funkwhale launch, it is required to run this::
docker-compose -f dev.yml run --rm front yarn run i18n-compile
Then you can run everything with::
docker-compose -f dev.yml up front api nginx celeryworker
This will launch all services, and output the logs in your current terminal window.
If you prefer to launch them in the background instead, use the ``-d`` flag, and access the logs when you need it via ``docker-compose -f dev.yml logs --tail=50 --follow``.
Once everything is up, you can access the various funkwhale's components:
- The Vue webapp, on http://localhost:8000
- The API, on http://localhost:8000/api/v1/
- The django admin, on http://localhost:8000/api/admin/
Stopping everything
^^^^^^^^^^^^^^^^^^^
Once you're down with your work, you can stop running containers, if any, with::
docker-compose -f dev.yml stop
Removing everything
^^^^^^^^^^^^^^^^^^^
If you want to wipe your development environment completely (e.g. if you want to start over from scratch), just run::
docker-compose -f dev.yml down -v
This will wipe your containers and data, so please be careful before running it.
You can keep your data by removing the ``-v`` flag.
Working with federation locally
-------------------------------
This is not needed unless you need to work on federation-related features.
To achieve that, you'll need:
1. to update your dns resolver to resolve all your .dev hostnames locally
2. a reverse proxy (such as traefik) to catch those .dev requests and
and with https certificate
3. two instances (or more) running locally, following the regular dev setup
Resolve .dev names locally
^^^^^^^^^^^^^^^^^^^^^^^^^^
If you use dnsmasq, this is as simple as doing::
echo "address=/test/172.17.0.1" | sudo tee /etc/dnsmasq.d/test.conf
sudo systemctl restart dnsmasq
If you use NetworkManager with dnsmasq integration, use this instead::
echo "address=/test/172.17.0.1" | sudo tee /etc/NetworkManager/dnsmasq.d/test.conf
sudo systemctl restart NetworkManager
Add wildcard certificate to the trusted certificates
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Simply copy bundled certificates::
sudo cp docker/ssl/test.crt /usr/local/share/ca-certificates/
sudo update-ca-certificates
This certificate is a wildcard for ``*.funkwhale.test``
Run a reverse proxy for your instances
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
Launch everything
^^^^^^^^^^^^^^^^^
Launch the traefik proxy::
docker-compose -f docker/traefik.yml up -d
Then, in separate terminals, you can setup as many different instances as you
need::
export COMPOSE_PROJECT_NAME=node2
export VUE_PORT=1234 # this has to be unique for each instance
docker-compose -f dev.yml run --rm api python manage.py migrate
docker-compose -f dev.yml run --rm api python manage.py createsuperuser
docker-compose -f dev.yml up nginx api front nginx api celeryworker
Note that by default, if you don't export the COMPOSE_PROJECT_NAME,
we will default to node1 as the name of your instance.
Assuming your project name is ``node1``, your server will be reachable
at ``https://node1.funkwhale.test/``. Not that you'll have to trust
the SSL Certificate as it's self signed.
When working on federation with traefik, ensure you have this in your ``env``::
# This will ensure we don't bind any port on the host, and thus enable
# multiple instances of funkwhale to be spawned concurrently.
VUE_PORT_BINDING=
# This disable certificate verification
EXTERNAL_REQUESTS_VERIFY_SSL=false
# this ensure you don't have incorrect urls pointing to http resources
FUNKWHALE_PROTOCOL=https
# Disable host ports binding for the nginx container, as traefik is serving everything
NGINX_PORTS_MAPPING=80
Typical workflow for a contribution
-----------------------------------
0. Fork the project if you did not already or if you do not have access to the main repository
1. Checkout the development branch and pull most recent changes: ``git checkout develop && git pull``
2. If working on an issue, assign yourself to the issue. Otherwise, consider open an issue before starting to work on something, especially for new features.
3. Create a dedicated branch for your work ``42-awesome-fix``. It is good practice to prefix your branch name with the ID of the issue you are solving.
4. Work on your stuff
5. [Optional] Consider running ``yarn lint`` in ``front`` if you changed something there. Consider fixing some
linting errors in the files you touched.
6. Commit small, atomic changes to make it easier to review your contribution
7. Add a changelog fragment to summarize your changes: ``echo "Implemented awesome stuff (#42)" > changes/changelog.d/42.feature``
8. Push your branch
9. Create your merge request
10. Take a step back and enjoy, we're really grateful you did all of this and took the time to contribute!
Changelog management
--------------------
To ensure we have extensive and well-structured changelog, any significant
work such as closing an issue must include a changelog fragment. Small changes
may include a changelog fragment as well but this is not mandatory. If you're not
sure about what to do, do not panic, open your merge request normally and we'll
figure everything during the review ;)
Changelog fragments are text files that can contain one or multiple lines
that describe the changes occurring in a bunch of commits. Those files reside
in ``changes/changelog.d``.
Content
^^^^^^^
A typical fragment looks like that:
Fixed broken audio player on Chrome 42 for ogg files (#567)
If the work fixes one or more issues, the issue number should be included at the
end of the fragment (``(#567)`` is the issue number in the previous example).
If your work is not related to a specific issue, use the merge request
identifier instead, like this:
Fixed a typo in landing page copy (!342)
Naming
^^^^^^
Fragment files should respect the following naming pattern: ``changes/changelog.d/<name>.<category>``.
Name can be anything describing your work, or simply the identifier of the issue number you are fixing.
Category can be one of:
- ``feature``: for new features
- ``enhancement``: for enhancements on existing features
- ``bugfix``: for bugfixes
- ``doc``: for documentation
- ``i18n``: for internationalization-related work
- ``misc``: for anything else
Shortcuts
^^^^^^^^^
Here is a shortcut you can use/adapt to easily create new fragments from command-line:
.. code-block:: bash
issue="42"
content="Fixed an overflowing issue on small resolutions (#$issue)"
category="bugfix"
echo "$content ($issue)" > changes/changelog.d/$issue.$category
You can of course create fragments by hand in your text editor, or from Gitlab's
interface as well.
Internationalization
--------------------
We're using https://github.com/Polyconseil/vue-gettext to manage i18n in the project.
When working on the front-end, any end-user string should be marked as a translatable string,
with the proper context, as described below.
Translations in HTML
^^^^^^^^^^^^^^^^^^^^
Translations in HTML use the ``<translate>`` tag::
<template>
<div>
<h1><translate translate-context="Content/Profile/Header">User profile</translate></h1>
<p>
<translate
translate-context="Content/Profile/Paragraph"
:translate-params="{username: 'alice'}">
You are logged in as %{ username }
</translate>
</p>
<p>
<translate
translate-context="Content/Profile/Paragraph"
translate-plural="You have %{ count } new messages, that's a lot!"
:translate-n="unreadMessagesCount"
:translate-params="{count: unreadMessagesCount}">
You have 1 new message
</translate>
</p>
</div>
</template>
Anything between the `<translate>` and `</translate>` delimiters will be considered as a translatable string.
You can use variables in the translated string via the ``:translate-params="{var: 'value'}"`` directive, and reference them like this:
``val value is %{ value }``.
For pluralization, you need to use ``translate-params`` in conjunction with ``translate-plural`` and ``translate-n``:
- ``translate-params`` should contain the variable you're using for pluralization (which is usually shown to the user)
- ``translate-n`` should match the same variable
- The ``<translate>`` delimiters contain the non-pluralized version of your string
- The ``translate-plural`` directive contains the pluralized version of your string
Translations in javascript
^^^^^^^^^^^^^^^^^^^^^^^^^^
Translations in javascript work by calling the ``this.$*gettext`` functions::
export default {
computed: {
strings () {
let tracksCount = 42
let playButton = this.$pgettext('Sidebar/Player/Button/Verb, Short', 'Play')
let loginMessage = this.$pgettext('*/Login/Message', 'Welcome back %{ username }')
let addedMessage = this.$npgettext('*/Player/Message', 'One track was queued', '%{ count } tracks were queued', tracksCount)
console.log(this.$gettextInterpolate(addedMessage, {count: tracksCount}))
console.log(this.$gettextInterpolate(loginMessage, {username: 'alice'}))
}
}
}
The first argument of the ``$pgettext`` and ``$npgettext`` functions is the string context.
Contextualization
^^^^^^^^^^^^^^^^^
Translation contexts provided via the ``translate-context`` directive and the ``$pgettext`` and ``$npgettext`` are never shown to end users
but visible by Funkwhale translators. They help translators where and how the strings are used,
especially with short or ambiguous strings, like ``May``, which can refer a month or a verb.
While we could in theory use free form context, like ``This string is inside a button, in the main page, and is a call to action``,
Funkwhale use a hierarchical structure to write contexts and keep them short and consistents accross the app. The previous context,
rewritten correctly would be: ``Content/Home/Button/Call to action``.
This hierarchical structure is made of several parts:
- The location part, which is required and refers to the big blocks found in Funkwhale UI where the translated string is displayed:
- ``Content``
- ``Footer``
- ``Head``
- ``Menu``
- ``Popup``
- ``Sidebar``
- ``*`` for strings that are not tied to a specific location
- The feature part, which is required, and refers to the feature associated with the translated string:
- ``About``
- ``Admin``
- ``Album``
- ``Artist``
- ``Embed``
- ``Home``
- ``Login``
- ``Library``
- ``Moderation``
- ``Player``
- ``Playlist``
- ``Profile``
- ``Favorites``
- ``Notifications``
- ``Radio``
- ``Search``
- ``Settings``
- ``Signup``
- ``Track``
- ``Queue``
- ``*`` for strings that are not tied to a specific feature
- The component part, which is required and refers to the type of element that contain the string:
- ``Button``
- ``Card``
- ``Checkbox``
- ``Dropdown``
- ``Error message``
- ``Form``
- ``Header``
- ``Help text``
- ``Hidden text``
- ``Icon``
- ``Input``
- ``Image``
- ``Label``
- ``Link``
- ``List item``
- ``Menu``
- ``Message``
- ``Paragraph``
- ``Placeholder``
- ``Tab``
- ``Table``
- ``Title``
- ``Tooltip``
- ``*`` for strings that are not tied to a specific component
The detail part, which is optional and refers to the contents of the string itself, such as:
- ``Adjective``
- ``Call to action``
- ``Noun``
- ``Short``
- ``Unit``
- ``Verb``
Here are a few examples of valid context hierarchies:
- ``Sidebar/Player/Button``
- ``Content/Home/Button/Call to action``
- ``Footer/*/Help text``
- ``*/*/*/Verb, Short``
- ``Popup/Playlist/Button``
- ``Content/Admin/Table.Label/Short, Noun (Value is a date)``
It's possible to nest multiple component parts to reach a higher level of detail. The component parts are then separated by a dot:
- ``Sidebar/Queue/Tab.Title``
- ``Content/*/Button.Title``
- ``Content/*/Table.Header``
- ``Footer/*/List item.Link``
- ``Content/*/Form.Help text``
Collecting translatable strings
^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
If you want to ensure your translatable strings are correctly marked for translation,
you can try to extract them.
Extraction is done by calling ``yarn run i18n-extract``, which
will pull all the strings from source files and put them in a PO files.
You can then inspect the PO files to ensure everything is fine (but don't commit them, it's not needed).
Contributing to the API
-----------------------
Project structure
^^^^^^^^^^^^^^^^^
.. code-block:: shell
tree api -L 2 -d
api
├── config # configuration directory (settings, urls, wsgi server)
│ └── settings # Django settings files
├── funkwhale_api # project directory, all funkwhale logic is here
├── requirements # python requirements files
└── tests # test files, matches the structure of the funkwhale_api directory
.. note::
Unless trivial, API contributions must include unittests to ensure
your fix or feature is working as expected and won't break in the future
Running tests
^^^^^^^^^^^^^
To run the pytest test suite, use the following command::
docker-compose -f dev.yml run --rm api pytest
This is regular pytest, so you can use any arguments/options that pytest usually accept::
# get some help
docker-compose -f dev.yml run --rm api pytest -h
# Stop on first failure
docker-compose -f dev.yml run --rm api pytest -x
# Run a specific test file
docker-compose -f dev.yml run --rm api pytest tests/music/test_models.py
Writing tests
^^^^^^^^^^^^^
Although teaching you how to write unit tests is outside of the scope of this
document, you'll find below a collection of tips, snippets and resources
you can use if you want to learn on that subject.
Useful links:
- `A quick introduction to unit test writing with pytest <https://semaphoreci.com/community/tutorials/testing-python-applications-with-pytest>`_
- `A complete guide to Test-Driven Development (although not using Pytest) <https://www.obeythetestinggoat.com/>`_
- `pytest <https://docs.pytest.org/en/latest/>`_: documentation of our testing engine and runner
- `pytest-mock <https://pypi.org/project/pytest-mock/>`_: project page of our mocking engine
- `factory-boy <http://factoryboy.readthedocs.io/>`_: documentation of factory-boy, which we use to easily generate fake objects and data
Recommendations:
- Test files must target a module and mimic ``funkwhale_api`` directory structure: if you're writing tests for ``funkwhale_api/myapp/views.py``, you should put thoses tests in ``tests/myapp/test_views.py``
- Tests should be small and test one thing. If you need to test multiple things, write multiple tests.
We provide a lot of utils and fixtures to make the process of writing tests as
painless as possible. You'll find some usage examples below.
Use factories to create arbitrary objects:
.. code-block:: python
# funkwhale_api/myapp/users.py
def downgrade_user(user):
"""
A simple function that remove superuser status from users
and return True if user was actually downgraded
"""
downgraded = user.is_superuser
user.is_superuser = False
user.save()
return downgraded
# tests/myapp/test_users.py
from funkwhale_api.myapp import users
def test_downgrade_superuser(factories):
user = factories['users.User'](is_superuser=True)
downgraded = users.downgrade_user(user)
assert downgraded is True
assert user.is_superuser is False
def test_downgrade_normal_user_does_nothing(factories):
user = factories['users.User'](is_superuser=False)
downgraded = something.downgrade_user(user)
assert downgraded is False
assert user.is_superuser is False
.. note::
We offer factories for almost if not all models. Factories are located
in a ``factories.py`` file inside each app.
Mocking: mocking is the process of faking some logic in our code. This is
useful when testing components that depend on each other:
.. code-block:: python
# funkwhale_api/myapp/notifications.py
def notify(email, message):
"""
A function that sends an e-mail to the given recipient
with the given message
"""
# our e-mail sending logic here
# ...
# funkwhale_api/myapp/users.py
from . import notifications
def downgrade_user(user):
"""
A simple function that remove superuser status from users
and return True if user was actually downgraded
"""
downgraded = user.is_superuser
user.is_superuser = False
user.save()
if downgraded:
notifications.notify(user.email, 'You have been downgraded!')
return downgraded
# tests/myapp/test_users.py
def test_downgrade_superuser_sends_email(factories, mocker):
"""
Your downgrade logic is already tested, however, we want to ensure
an e-mail is sent when user is downgraded, but we don't have any e-mail
server available in our testing environment. Thus, we need to mock
the e-mail sending process.
"""
mocked_notify = mocker.patch('funkwhale_api.myapp.notifications.notify')
user = factories['users.User'](is_superuser=True)
users.downgrade_user(user)
# here, we ensure our notify function was called with proper arguments
mocked_notify.assert_called_once_with(user.email, 'You have been downgraded')
def test_downgrade_not_superuser_skips_email(factories, mocker):
mocked_notify = mocker.patch('funkwhale_api.myapp.notifications.notify')
user = factories['users.User'](is_superuser=False)
users.downgrade_user(user)
# here, we ensure no e-mail was sent
mocked_notify.assert_not_called()
Views: you can find some readable views tests in file: ``api/tests/users/test_views.py``
.. note::
A complete list of available-fixtures is available by running
``docker-compose -f dev.yml run --rm api pytest --fixtures``
Contributing to the front-end
-----------------------------
Styles and themes
^^^^^^^^^^^^^^^^^
Our UI framework is Fomantic UI (https://fomantic-ui.com/), and Funkwhale's custom styles are written in SCSS. All the styles are configured in ``front/src/styles/_main.scss``,
including imporing of Fomantic UI styles and components.
We're applying several changes on top of the Fomantic CSS files, before they are imported:
1. Many hardcoded color values are replaced by CSS vars: e.g ``color: orange`` is replaced by ``color: var(--vibrant-color)``. This makes theming way easier.
2. Unused components variations and icons are stripped from the source files, in order to reduce the final size of our CSS files
This changes are applied automatically when running ``yarn install``, through a ``postinstall`` hook. Internally, ``front/scripts/fix-fomantic-css.py`` is called
and handle both kind of modifications. Please refer to this script if you need to use new icons to the project, or restore some components variations that
were stripped in order to use them.
Running tests
^^^^^^^^^^^^^
To run the front-end test suite, use the following command::
docker-compose -f dev.yml run --rm front yarn test:unit
We also support a "watch and test" mode were we continually relaunch
tests when changes are recorded on the file system::
docker-compose -f dev.yml run --rm front yarn test:unit -w
The latter is especially useful when you are debugging failing tests.
.. note::
The front-end test suite coverage is still pretty low
Making a release
----------------
To make a new 3.4 release::
# setup
export NEXT_RELEASE=3.4 # replace with the next release number
export PREVIOUS_RELEASE=3.3 # replace with the previous release number
# ensure you have an up-to-date repo
git checkout develop # use stable if you're doing a hotfix release
git pull
# compile changelog
towncrier build --version $NEXT_RELEASE
# polish changelog
# - update the date
# - look for typos
# - add list of contributors via `python3 scripts/get-contributions-stats.py $NEXT_RELEASE`
git log $PREVIOUS_RELEASE.. --format="- %aN" --reverse | sort | uniq # Get all commit authors since last release
nano CHANGELOG
# Set the `__version__` variable to $NEXT_RELEASE
nano api/funkwhale_api/__init__.py
# commit
git add .
git commit -m "Version bump and changelog for $NEXT_RELEASE"
# tag
git tag $NEXT_RELEASE
# publish
git push --tags && git push
# if you're doing a hotfix release from stable
git checkout develop && git merge stable && git push
# if you're doing a non-hotfix release, and a real release (not a real release) from develop
git checkout stable && git merge develop && git push
Then, visit https://dev.funkwhale.audio/funkwhale/funkwhale/-/tags, copy-paste the changelog on the corresponding
tag, and announce the good news ;)

Wyświetl plik

@ -1,5 +1,5 @@
FROM python:3.6
RUN apt-get update && apt-get install -y graphviz
RUN pip install sphinx livereload sphinx_rtd_theme django-environ django myst-parser sphinx-design sphinx-multiversion
RUN pip install sphinx livereload sphinx_rtd_theme django-environ django myst-parser sphinx-design sphinx-multiversion sphinxcontrib-mermaid
WORKDIR /app/docs

Wyświetl plik

@ -51,6 +51,7 @@ extensions = [
"myst_parser",
"sphinx_design",
"sphinx_multiversion",
"sphinxcontrib.mermaid",
]
autodoc_mock_imports = [
"celery",

Wyświetl plik

@ -1 +0,0 @@
.. include:: ../CONTRIBUTING.rst

Wyświetl plik

@ -0,0 +1,6 @@
# Contribute to the API
```{include} ../../../CONTRIBUTING.md
:start-after: "## Contribute to the API"
:end-before: "## Contribute to the frontend"
```

Wyświetl plik

@ -0,0 +1,6 @@
# Update UI copy
```{include} ../../../CONTRIBUTING.md
:start-after: "## Update UI copy"
:end-before: "## Git workflow"
```

Wyświetl plik

@ -0,0 +1,6 @@
# Contribute to the frontend
```{include} ../../../CONTRIBUTING.md
:start-after: "## Contribute to the frontend"
:end-before: "## Update UI copy"
```

Wyświetl plik

@ -0,0 +1,14 @@
# Contribute to the Funkwhale codebase
Funkwhale is an open source project, which means we welcome contributions from anyone! If you want to get involved with Funkwhale development, check the guides in this section.
```{toctree}
---
maxdepth: 1
---
frontend
copy
api
```

Wyświetl plik

@ -0,0 +1,15 @@
# Developer documentation
Funkwhale welcomes contributions from all developers. If this is your first time contributing to an open source project, don't be afraid to get stuck in! The Funkwhale community will guide you through the process and help you grow your confidence.
## Set up your development environment
Before you begin, you need to set up a development environment. Follow the [guides in the setup section](setup/index.md) to set up an environment that's right for you.
## Read up on our processes
The Funkwhale project follows a few processes to make managing contributions easier. If you're not sure how to get started, check out the [guides in the workflows section](workflows/index.md) to get a better understanding of what you need to do.
## Contribute to the codebase
Ready to get stuck in? Take a look at the [contribution guides](contribute/index.md) and start making your changes!

Wyświetl plik

@ -0,0 +1,8 @@
# Develop using Docker
```{include} ../../../CONTRIBUTING.md
---
start-after: "### Docker"
end-before: "### Vite"
---
```

Wyświetl plik

@ -0,0 +1,6 @@
# Develop using Gitpod
```{include} ../../../CONTRIBUTING.md
:start-after: "### Gitpod"
:end-before: "### Docker"
```

Wyświetl plik

@ -0,0 +1,15 @@
# Set up your development environment
Follow the instructions in these guides to set up your development environment.
```{toctree}
---
caption: Choose your setup
maxdepth: 1
---
gitpod
docker
vite
```

Wyświetl plik

@ -0,0 +1,8 @@
# Develop using Vite
```{include} ../../../CONTRIBUTING.md
---
start-after: "### Vite"
end-before: "## Contribute to the API"
---
```

Wyświetl plik

@ -0,0 +1,6 @@
# Changelog fragments
```{include} ../../../CONTRIBUTING.md
:start-after: "## Changelog fragments"
:end-before: "## Make a release"
```

Wyświetl plik

@ -0,0 +1,6 @@
# Git workflow
```{include} ../../../CONTRIBUTING.md
:start-after: "## Git workflow"
:end-before: "## Changelog fragments"
```

Wyświetl plik

@ -0,0 +1,15 @@
# Development workflows
Funkwhale follows workflows for each area of development and release management. You can find a breakdown of these in this section.
```{toctree}
---
caption: Workflows
maxdepth: 1
---
git
changelog
release
```

Wyświetl plik

@ -0,0 +1,5 @@
# Make a release
```{include} ../../../CONTRIBUTING.md
:start-after: "## Make a release"
```

Wyświetl plik

@ -70,8 +70,10 @@ caption: Developer documentation
hidden: true
---
contributing
developers/index
developer_documentation/index
developer_documentation/setup/index
developer_documentation/contribute/index
developer_documentation/workflows/index
```
@ -184,7 +186,7 @@ Want to use Funkwhale's API or help with the project? Our developer docs give yo
+++
```{button-link} developers/index.html
```{button-link} developer_documentation/index.html
:ref-type: ref
:color: primary
:outline:

110
docs/poetry.lock wygenerowano
Wyświetl plik

@ -68,7 +68,7 @@ python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*"
[[package]]
name = "Django"
version = "4.0.6"
version = "4.0.5"
description = "A high-level Python web framework that encourages rapid development and clean, pragmatic design."
category = "main"
optional = false
@ -92,9 +92,9 @@ optional = false
python-versions = ">=3.4,<4"
[package.extras]
testing = ["pytest (>=4.6.11)", "coverage[toml] (>=5.0a4)"]
docs = ["sphinx-notfound-page", "sphinx (>=3.5.0)", "furo (>=2021.8.17b43,<2021.9.0)"]
develop = ["sphinx-notfound-page", "sphinx (>=3.5.0)", "furo (>=2021.8.17b43,<2021.9.0)", "pytest (>=4.6.11)", "coverage[toml] (>=5.0a4)"]
develop = ["coverage[toml] (>=5.0a4)", "furo (>=2021.8.17b43,<2021.9.0)", "pytest (>=4.6.11)", "sphinx (>=3.5.0)", "sphinx-notfound-page"]
docs = ["furo (>=2021.8.17b43,<2021.9.0)", "sphinx (>=3.5.0)", "sphinx-notfound-page"]
testing = ["coverage[toml] (>=5.0a4)", "pytest (>=4.6.11)"]
[[package]]
name = "docutils"
@ -146,14 +146,14 @@ python-versions = ">=3.7"
mdurl = ">=0.1,<1.0"
[package.extras]
testing = ["pytest-regressions", "pytest-cov", "pytest", "coverage"]
rtd = ["sphinx-book-theme", "sphinx-design", "sphinx-copybutton", "sphinx", "pyyaml", "myst-parser", "attrs"]
profiling = ["gprof2dot"]
plugins = ["mdit-py-plugins"]
linkify = ["linkify-it-py (>=1.0,<2.0)"]
compare = ["panflute (>=2.1.3,<2.2.0)", "mistune (>=2.0.2,<2.1.0)", "mistletoe (>=0.8.1,<0.9.0)", "markdown (>=3.3.6,<3.4.0)", "commonmark (>=0.9.1,<0.10.0)"]
benchmarking = ["psutil", "pytest", "pytest-benchmark (>=3.2,<4.0)"]
code_style = ["pre-commit (==2.6)"]
benchmarking = ["pytest-benchmark (>=3.2,<4.0)", "pytest", "psutil"]
compare = ["commonmark (>=0.9.1,<0.10.0)", "markdown (>=3.3.6,<3.4.0)", "mistletoe (>=0.8.1,<0.9.0)", "mistune (>=2.0.2,<2.1.0)", "panflute (>=2.1.3,<2.2.0)"]
linkify = ["linkify-it-py (>=1.0,<2.0)"]
plugins = ["mdit-py-plugins"]
profiling = ["gprof2dot"]
rtd = ["attrs", "myst-parser", "pyyaml", "sphinx", "sphinx-copybutton", "sphinx-design", "sphinx_book_theme"]
testing = ["coverage", "pytest", "pytest-cov", "pytest-regressions"]
[[package]]
name = "MarkupSafe"
@ -175,9 +175,9 @@ python-versions = "~=3.6"
markdown-it-py = ">=1.0.0,<3.0.0"
[package.extras]
testing = ["pytest-regressions", "pytest-cov", "pytest (>=3.6,<4)", "coverage"]
rtd = ["sphinx-book-theme (>=0.1.0,<0.2.0)", "myst-parser (>=0.14.0,<0.15.0)"]
code_style = ["pre-commit (==2.6)"]
rtd = ["myst-parser (>=0.14.0,<0.15.0)", "sphinx-book-theme (>=0.1.0,<0.2.0)"]
testing = ["coverage", "pytest (>=3.6,<4)", "pytest-cov", "pytest-regressions"]
[[package]]
name = "mdurl"
@ -207,8 +207,8 @@ typing-extensions = "*"
[package.extras]
code_style = ["pre-commit (>=2.12,<3.0)"]
linkify = ["linkify-it-py (>=1.0,<2.0)"]
rtd = ["ipython", "sphinx-book-theme", "sphinx-design", "sphinxext-rediraffe (>=0.2.7,<0.3.0)", "sphinxcontrib.mermaid (>=0.7.1,<0.8.0)", "sphinxext-opengraph (>=0.6.3,<0.7.0)"]
testing = ["beautifulsoup4", "coverage", "pytest (>=6,<7)", "pytest-cov", "pytest-regressions", "pytest-param-files (>=0.3.4,<0.4.0)", "sphinx-pytest"]
rtd = ["ipython", "sphinx-book-theme", "sphinx-design", "sphinxcontrib.mermaid (>=0.7.1,<0.8.0)", "sphinxext-opengraph (>=0.6.3,<0.7.0)", "sphinxext-rediraffe (>=0.2.7,<0.3.0)"]
testing = ["beautifulsoup4", "coverage[toml]", "pytest (>=6,<7)", "pytest-cov", "pytest-param-files (>=0.3.4,<0.4.0)", "pytest-regressions", "sphinx-pytest"]
[[package]]
name = "packaging"
@ -277,6 +277,19 @@ urllib3 = ">=1.21.1,<1.27"
socks = ["PySocks (>=1.5.6,!=1.5.7)"]
use_chardet_on_py3 = ["chardet (>=3.0.2,<6)"]
[[package]]
name = "setuptools"
version = "65.4.1"
description = "Easily download, build, install, upgrade, and uninstall Python packages"
category = "main"
optional = false
python-versions = ">=3.7"
[package.extras]
docs = ["furo", "jaraco.packaging (>=9)", "jaraco.tidelift (>=1.4)", "pygments-github-lexers (==0.0.5)", "rst.linker (>=1.9)", "sphinx (>=3.5)", "sphinx-favicon", "sphinx-hoverxref (<2)", "sphinx-inline-tabs", "sphinx-notfound-page (==0.8.3)", "sphinx-reredirects", "sphinxcontrib-towncrier"]
testing = ["build[virtualenv]", "filelock (>=3.4.0)", "flake8 (<5)", "flake8-2020", "ini2toml[lite] (>=0.9)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "mock", "pip (>=19.1)", "pip-run (>=8.8)", "pytest (>=6)", "pytest-black (>=0.3.7)", "pytest-checkdocs (>=2.4)", "pytest-cov", "pytest-enabler (>=1.3)", "pytest-flake8", "pytest-mypy (>=0.9.1)", "pytest-perf", "pytest-xdist", "tomli-w (>=1.0.0)", "virtualenv (>=13.0.0)", "wheel"]
testing-integration = ["build[virtualenv]", "filelock (>=3.4.0)", "jaraco.envs (>=2.2)", "jaraco.path (>=3.2.0)", "pytest", "pytest-enabler", "pytest-xdist", "tomli", "virtualenv (>=13.0.0)", "wheel"]
[[package]]
name = "snowballstemmer"
version = "2.2.0"
@ -313,8 +326,8 @@ sphinxcontrib-serializinghtml = ">=1.1.5"
[package.extras]
docs = ["sphinxcontrib-websupport"]
lint = ["flake8 (>=3.5.0)", "flake8-comprehensions", "flake8-bugbear", "isort", "mypy (>=0.971)", "sphinx-lint", "docutils-stubs", "types-typed-ast", "types-requests"]
test = ["pytest (>=4.6)", "html5lib", "cython", "typed-ast"]
lint = ["docutils-stubs", "flake8 (>=3.5.0)", "flake8-bugbear", "flake8-comprehensions", "isort", "mypy (>=0.971)", "sphinx-lint", "types-requests", "types-typed-ast"]
test = ["cython", "html5lib", "pytest (>=4.6)", "typed-ast"]
[[package]]
name = "sphinx-design"
@ -328,13 +341,13 @@ python-versions = ">=3.7"
sphinx = ">=4,<6"
[package.extras]
theme_sbt = ["sphinx-book-theme (>=0.3.0,<0.4.0)"]
theme_rtd = ["sphinx-rtd-theme (>=1.0,<2.0)"]
theme_pydata = ["pydata-sphinx-theme (>=0.9.0,<0.10.0)"]
theme_furo = ["furo (>=2022.06.04,<2022.07)"]
testing = ["pytest-regressions", "pytest-cov", "pytest (>=7.1,<8.0)", "myst-parser (>=0.18.0,<0.19.0)"]
rtd = ["myst-parser (>=0.18.0,<0.19.0)"]
code_style = ["pre-commit (>=2.12,<3.0)"]
rtd = ["myst-parser (>=0.18.0,<0.19.0)"]
testing = ["myst-parser (>=0.18.0,<0.19.0)", "pytest (>=7.1,<8.0)", "pytest-cov", "pytest-regressions"]
theme_furo = ["furo (>=2022.06.04,<2022.07)"]
theme_pydata = ["pydata-sphinx-theme (>=0.9.0,<0.10.0)"]
theme_rtd = ["sphinx-rtd-theme (>=1.0,<2.0)"]
theme_sbt = ["sphinx-book-theme (>=0.3.0,<0.4.0)"]
[[package]]
name = "sphinx-intl"
@ -347,10 +360,11 @@ python-versions = ">=3.5"
[package.dependencies]
babel = "*"
click = "*"
setuptools = "*"
sphinx = "*"
[package.extras]
test = ["pytest", "mock"]
test = ["mock", "pytest"]
transifex = ["transifex_client (>=0.11)"]
[[package]]
@ -388,8 +402,8 @@ optional = false
python-versions = ">=3.5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
lint = ["docutils-stubs", "mypy", "flake8"]
[[package]]
name = "sphinxcontrib-devhelp"
@ -400,8 +414,8 @@ optional = false
python-versions = ">=3.5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
lint = ["docutils-stubs", "mypy", "flake8"]
[[package]]
name = "sphinxcontrib-htmlhelp"
@ -412,8 +426,8 @@ optional = false
python-versions = ">=3.6"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["html5lib", "pytest"]
lint = ["docutils-stubs", "mypy", "flake8"]
[[package]]
name = "sphinxcontrib-jsmath"
@ -424,7 +438,15 @@ optional = false
python-versions = ">=3.5"
[package.extras]
test = ["mypy", "flake8", "pytest"]
test = ["flake8", "mypy", "pytest"]
[[package]]
name = "sphinxcontrib-mermaid"
version = "0.7.1"
description = "Mermaid diagrams in yours Sphinx powered docs"
category = "main"
optional = false
python-versions = "*"
[[package]]
name = "sphinxcontrib-qthelp"
@ -435,8 +457,8 @@ optional = false
python-versions = ">=3.5"
[package.extras]
lint = ["docutils-stubs", "flake8", "mypy"]
test = ["pytest"]
lint = ["docutils-stubs", "mypy", "flake8"]
[[package]]
name = "sphinxcontrib-serializinghtml"
@ -480,17 +502,17 @@ version = "1.26.11"
description = "HTTP library with thread-safe connection pooling, file post, and more."
category = "main"
optional = false
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, !=3.5.*, <4"
python-versions = ">=2.7, !=3.0.*, !=3.1.*, !=3.2.*, !=3.3.*, !=3.4.*, !=3.5.*, <4"
[package.extras]
brotli = ["brotli (>=1.0.9)", "brotlicffi (>=0.8.0)", "brotlipy (>=0.6.0)"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)", "urllib3-secure-extra"]
secure = ["certifi", "cryptography (>=1.3.4)", "idna (>=2.0.0)", "ipaddress", "pyOpenSSL (>=0.14)"]
socks = ["PySocks (>=1.5.6,!=1.5.7,<2.0)"]
[metadata]
lock-version = "1.1"
python-versions = "^3.10"
content-hash = "66c6d523674a13b30727c77021778316358a2c193e9f37432c007a338c6aa45a"
content-hash = "989019929a6c7411c769f7ccaca03d0d2c6c62282a3af54a8f7dcb84d422f096"
[metadata.files]
alabaster = [
@ -510,8 +532,8 @@ certifi = [
{file = "certifi-2022.6.15.1.tar.gz", hash = "sha256:cffdcd380919da6137f76633531a5817e3a9f268575c128249fb637e4f9e73fb"},
]
charset-normalizer = [
{file = "charset-normalizer-2.1.0.tar.gz", hash = "sha256:575e708016ff3a5e3681541cb9d79312c416835686d054a23accb873b254f413"},
{file = "charset_normalizer-2.1.0-py3-none-any.whl", hash = "sha256:5189b6f22b01957427f35b6a08d9a0bc45b46d3788ef5a92e978433c7a35f8a5"},
{file = "charset-normalizer-2.1.1.tar.gz", hash = "sha256:5a3d016c7c547f69d6f81fb0db9449ce888b418b5b9952cc5e6e66843e9dd845"},
{file = "charset_normalizer-2.1.1-py3-none-any.whl", hash = "sha256:83e9a75d1911279afd89352c68b45348559d1fc0506b054b346651b5e7fee29f"},
]
click = [
{file = "click-8.1.3-py3-none-any.whl", hash = "sha256:bb4d8133cb15a609f44e8213d9b391b0809795062913b383c62be0ee95b1db48"},
@ -522,8 +544,8 @@ colorama = [
{file = "colorama-0.4.5.tar.gz", hash = "sha256:e6c6b4334fc50988a639d9b98aa429a0b57da6e17b9a44f0451f930b6967b7a4"},
]
Django = [
{file = "Django-4.0.6-py3-none-any.whl", hash = "sha256:ca54ebedfcbc60d191391efbf02ba68fb52165b8bf6ccd6fe71f098cac1fe59e"},
{file = "Django-4.0.6.tar.gz", hash = "sha256:a67a793ff6827fd373555537dca0da293a63a316fe34cb7f367f898ccca3c3ae"},
{file = "Django-4.0.5-py3-none-any.whl", hash = "sha256:502ae42b6ab1b612c933fb50d5ff850facf858a4c212f76946ecd8ea5b3bf2d9"},
{file = "Django-4.0.5.tar.gz", hash = "sha256:f7431a5de7277966f3785557c3928433347d998c1e6459324501378a291e5aab"},
]
django-environ = [
{file = "django-environ-0.9.0.tar.gz", hash = "sha256:bff5381533056328c9ac02f71790bd5bf1cea81b1beeb648f28b81c9e83e0a21"},
@ -627,6 +649,13 @@ PyYAML = [
{file = "PyYAML-6.0-cp310-cp310-manylinux_2_5_x86_64.manylinux1_x86_64.manylinux_2_12_x86_64.manylinux2010_x86_64.whl", hash = "sha256:f84fbc98b019fef2ee9a1cb3ce93e3187a6df0b2538a651bfb890254ba9f90b5"},
{file = "PyYAML-6.0-cp310-cp310-win32.whl", hash = "sha256:2cd5df3de48857ed0544b34e2d40e9fac445930039f3cfe4bcc592a1f836d513"},
{file = "PyYAML-6.0-cp310-cp310-win_amd64.whl", hash = "sha256:daf496c58a8c52083df09b80c860005194014c3698698d1a57cbcfa182142a3a"},
{file = "PyYAML-6.0-cp311-cp311-macosx_10_9_x86_64.whl", hash = "sha256:d4b0ba9512519522b118090257be113b9468d804b19d63c71dbcf4a48fa32358"},
{file = "PyYAML-6.0-cp311-cp311-macosx_11_0_arm64.whl", hash = "sha256:81957921f441d50af23654aa6c5e5eaf9b06aba7f0a19c18a538dc7ef291c5a1"},
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:afa17f5bc4d1b10afd4466fd3a44dc0e245382deca5b3c353d8b757f9e3ecb8d"},
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:dbad0e9d368bb989f4515da330b88a057617d16b6a8245084f1b05400f24609f"},
{file = "PyYAML-6.0-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl", hash = "sha256:432557aa2c09802be39460360ddffd48156e30721f5e8d917f01d31694216782"},
{file = "PyYAML-6.0-cp311-cp311-win32.whl", hash = "sha256:bfaef573a63ba8923503d27530362590ff4f576c626d86a9fed95822a8255fd7"},
{file = "PyYAML-6.0-cp311-cp311-win_amd64.whl", hash = "sha256:01b45c0191e6d66c470b6cf1b9531a771a83c1c4208272ead47a3ae4f2f603bf"},
{file = "PyYAML-6.0-cp36-cp36m-macosx_10_9_x86_64.whl", hash = "sha256:897b80890765f037df3403d22bab41627ca8811ae55e9a722fd0392850ec4d86"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_aarch64.manylinux2014_aarch64.whl", hash = "sha256:50602afada6d6cbfad699b0c7bb50d5ccffa7e46a3d738092afddc1f9758427f"},
{file = "PyYAML-6.0-cp36-cp36m-manylinux_2_17_s390x.manylinux2014_s390x.whl", hash = "sha256:48c346915c114f5fdb3ead70312bd042a953a8ce5c7106d5bfb1a5254e47da92"},
@ -658,6 +687,10 @@ requests = [
{file = "requests-2.28.1-py3-none-any.whl", hash = "sha256:8fefa2a1a1365bf5520aac41836fbee479da67864514bdb821f31ce07ce65349"},
{file = "requests-2.28.1.tar.gz", hash = "sha256:7c5599b102feddaa661c826c56ab4fee28bfd17f5abca1ebbe3e7f19d7c97983"},
]
setuptools = [
{file = "setuptools-65.4.1-py3-none-any.whl", hash = "sha256:1b6bdc6161661409c5f21508763dc63ab20a9ac2f8ba20029aaaa7fdb9118012"},
{file = "setuptools-65.4.1.tar.gz", hash = "sha256:3050e338e5871e70c72983072fe34f6032ae1cdeeeb67338199c2f74e083a80e"},
]
snowballstemmer = [
{file = "snowballstemmer-2.2.0-py2.py3-none-any.whl", hash = "sha256:c8e1716e83cc398ae16824e5572ae04e0d9fc2c6b985fb0f900f5f0c96ecba1a"},
{file = "snowballstemmer-2.2.0.tar.gz", hash = "sha256:09b16deb8547d3412ad7b590689584cd0fe25ec8db3be37788be3810cbf19cb1"},
@ -672,7 +705,6 @@ sphinx-design = [
]
sphinx-intl = [
{file = "sphinx-intl-2.0.1.tar.gz", hash = "sha256:b25a6ec169347909e8d983eefe2d8adecb3edc2f27760db79b965c69950638b4"},
{file = "sphinx_intl-2.0.1-py3.8.egg", hash = "sha256:2ff97cba0e4e43249e339a3c29dd2f5b63c25ce794050aabca320ad95f5c5b55"},
]
sphinx-multiversion = [
{file = "sphinx-multiversion-0.2.4.tar.gz", hash = "sha256:5cd1ca9ecb5eed63cb8d6ce5e9c438ca13af4fa98e7eb6f376be541dd4990bcb"},
@ -698,6 +730,10 @@ sphinxcontrib-jsmath = [
{file = "sphinxcontrib-jsmath-1.0.1.tar.gz", hash = "sha256:a9925e4a4587247ed2191a22df5f6970656cb8ca2bd6284309578f2153e0c4b8"},
{file = "sphinxcontrib_jsmath-1.0.1-py2.py3-none-any.whl", hash = "sha256:2ec2eaebfb78f3f2078e73666b1415417a116cc848b72e5172e596c871103178"},
]
sphinxcontrib-mermaid = [
{file = "sphinxcontrib-mermaid-0.7.1.tar.gz", hash = "sha256:aa8a40b50ec86ad12824b62180240ca52a9bda8424455d7eb252eae9aa5d293c"},
{file = "sphinxcontrib_mermaid-0.7.1-py2.py3-none-any.whl", hash = "sha256:3e20de1937c30dfa807e446bf99983d73d0dd3dc5c6524addda59800fe928762"},
]
sphinxcontrib-qthelp = [
{file = "sphinxcontrib-qthelp-1.0.3.tar.gz", hash = "sha256:4c33767ee058b70dba89a6fc5c1892c0d57a54be67ddd3e7875a18d14cba5a72"},
{file = "sphinxcontrib_qthelp-1.0.3-py2.py3-none-any.whl", hash = "sha256:bd9fc24bcb748a8d51fd4ecaade681350aa63009a347a8c14e637895444dfab6"},

Wyświetl plik

@ -15,6 +15,7 @@ Django = "==4.0.5"
sphinx-multiversion = "0.2.4"
sphinx-intl = "2.0.1"
sphinx_design = "0.2.0"
sphinxcontrib-mermaid = "^0.7.1"
[tool.poetry.dev-dependencies]