-
Eliot Berriot authoredVerifiede587e5ae
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
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
-
Clone the repository:
git clone ssh://git@dev.funkwhale.audio/funkwhale/funkwhale.git cd funkwhale cd front
-
Install the dependencies:
yarn install
-
Compile the translations:
yarn i18n-compile
-
Launch the development server:
# this will serve the front-end on http://localhost:8000/front/ VUE_PORT=8000 yarn serve
-
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
-
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:
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
A note about branches
Next release development occurs on the "develop" branch, and releases are made on the "master" 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
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 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:8080
- The API, on http://localhost:8080/api/v1/
- The django admin, on http://localhost:8080/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:
- to update your dns resolver to resolve all your .dev hostnames locally
- a reverse proxy (such as traefik) to catch those .dev requests and and with https certificate
- 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
- Fork the project if you did not already or if you do not have access to the main repository
- Checkout the development branch and pull most recent changes:
git checkout develop && git pull
- 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.
- 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. - Work on your stuff
- Commit small, atomic changes to make it easier to review your contribution
- Add a changelog fragment to summarize your changes:
echo "Implemented awesome stuff (#42)" > changes/changelog.d/42.feature
- Push your branch
- Create your merge request
- 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:
issue="42"
content="Fixed an overflowing issue on small resolutions (#$issue)"
category="bugfix"
echo $content > 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.