Contributing

Thank you for considering contributing to Moe. The following information is catered to contributing via code, but other forms of contributions are more than welcome!

If you have any questions at all about the below process or contributing in general, please don’t hesitate to ask. The best way to do so is by opening a github discussion.

Overview

In short, to contribute code to Moe, you should follow these steps:

  1. Create a development environment

  2. Write some code

  3. Test your code

    $ tox run-parallel
    
  4. Submit a pull request

Creating a dev environment

  1. Fork Moe

  2. Install poetry

  3. Clone the repository

    $ git clone https://github.com/MoeMusic/Moe.git
    
  4. Install the project

    $ cd Moe
    $ poetry install
    
  5. Run Moe

    $ poetry shell
    $ moe
    

    Note

    poetry shell will enter a virtual environment to interact with the project. To exit, just type exit in the shell. If you’d like to run moe or other commands without entering the virtual environment, prepend any commands with poetry run.

Writing Code

Committing

There are strict commit guidelines to follow in order to keep a clean and useful git history, as well as to automatically generate our changelog. Commits should be atomic i.e. they can exist on their own and only consist of a single feature, bug fix, etc.

Moe follows conventional commits. In short, all of your commits should look like the following:

<type>[optional scope]: <description>

[optional body]

[optional footer(s)]

Type

The type should be one of the following:

  • build: Changes that affect the build system or external dependencies.

  • ci: Changes to our CI configuration files and scripts.

  • deprecate: Deprecations of API elements.

  • docs: Documentation only changes.

  • feat: A new feature.

  • fix: A bug fix.

  • perf: A code change that improves performance.

  • refactor: A code change that neither fixes a bug nor adds a feature.

  • style: Changes that do not affect the meaning of the code (white-space, formatting, etc).

  • test: Adding missing tests or correcting existing tests.

Note

If the commit introduces a breaking change, then the type and scope should be followed with an exclamation mark e.g. feat(add)!: new breaking change.

Scope

The scope is optional, but generally should just be the name of a plugin if the change is specific to a single plugin.

Description

The description should be present tense, not capitalized, and have no punctuation at the end. This is what will be displayed in the changelog.

Body

The body should include amplifying information on the change both for users looking at the commit from the changelog, as well as developers to understand the change. This is especially important for any breaking changes.

Migrations

Moe uses alembic for its database migrations. If your code change requires a database migration, use the following steps:

  1. Autogenerate the initial migration script.

    $ cd Moe/moe/moe_alembic
    $ alembic revision --autogenerate -m "<description of the change>"
    
  2. Adjust the auto-generated script as necessary.

    • The script will be under Moe/moe/moe_alembic/versions.

That’s it! For more information regarding migrations, reference the alembic docs. Moe will automatically upgrade or downgrade each user’s database the next time the program is run.

New Field Checklist

If adding a new field to Moe, the following checklist can help ensure you cover all your bases:

  1. Add the database column to the appropriate library class (Album, Extra, or Track).

    • If the field represents metadata and does not deal with the filesystem, also add to the appropriate Meta class (MetaAlbum or MetaTrack).

    • If creating a multi-value field, add the non-plural equivalent property. See Track.genres and the accompanying single-value field, Track.genre for an example.

    • Include documentation for the new field in the class docstring(s).

  2. Add the new field to the appropriate library item’s __init__ method.

    • If the field represents metadata and does not deal with the filesystem, also add to the appropriate Meta class (MetaAlbum or MetaTrack).

    • Add tests for constructing an item with the new field in the appropriate test file’s TestInit and TestMetaInit classes.

  3. Add to the item’s fields method as necessary.

  4. Add code for reading the tag from a track file under Track.read_custom_tags.

  5. Add code for writing the tag to a track file under write.write_custom_tags.

    • Add tests under test_write.py:TestWriteTags:test_write_tags()

  6. Add a weight for how much the field should factor into matching a track or album to another track or album in moe/util/core/match.py:MATCH_<TRACK/ALBUM>_FIELD_WEIGHTS.

  7. Include documentation for your new field in docs/fields.rst

  8. Create a migration script for your new field.

Testing

Writing Tests

With very few exceptions, any new feature or bug fix should include accompanying tests.

What to test

At a minimum, every public function/method should be tested. This includes hook specifications and implementations.

Non-public functions/methods should generally not be tested directly in order to maintain flexibility for these functions to be refactored without breaking tests. Instead, test the public interface that uses the non-public function. For example, cli plugins are tested by running main with the appropriate arguments.

Test structure

Each module should correspond to a single test module, and each public function and hook implementation gets its own test class. Hook specifications can be combined into a single class.

Style/Conventions

  • pytest is used to write tests and should be used over the standard library unittest. The only exception is when it comes to mocking. Use unittest.mock over pytest-mock or other alternatives.

  • Any tests specific to an operating system should use one of the following markers:

    • @pytest.mark.darwin - MacOS

    • @pytest.mark.linux - Linux

    • @pytest.mark.win32 - Windows

Running Tests

When you’ve finished writing your tests, you’ll want to make sure everything works:

$ tox run -e test

Once that passes, the next step is to check against all python versions Moe supports, as well as run the documentation and lint checks.

$ tox run-parallel

Important

Tox will only be able to use python versions you have installed already. The easiest way to install multiple python versions is to use pyenv.

Tip

If you only want to run specific checks, such as the unit tests for a specific python version, or just the lint or documentation tests, you can specify the test “environment” with tox run -e [env]. For example:

$ tox run -e py313-test

Which will run all unit tests with python 3.13. For a list of all possible environments you can use, run tox -l.

Linting

$ tox run -e lint

Runs the following checks:

  • black - used to keep a consistent code format.

  • flake8 - used to check for various stylistic rules. See setup.cfg for an overview on the various rules encompassed by this check.

  • isort - used for sorting imports in modules.

  • pyright - used for type checking.

  • commitizen - used to ensure proper commit conventions.

Building Documentation

$ tox run -e docs

Builds and tests the documentation. You can view the newly built documentation under Moe/.tox/docs/tmp/html/.

Submitting Pull Requests

Once your code changes are ready, or if you just want some early feedback, it’s time to create a Pull Request (PR).

Here’s how the process works:

  1. Open a Pull Request: On GitHub, navigate to your forked repository and click the “New pull request” button. Select the branch where you made your changes and compare it to the main branch of the original MoeMusic/Moe repository.

  2. Choose Draft or Ready:

    • If your work is still in progress, you have questions, or you just want early feedback, open a Draft Pull Request. This clearly signals that it’s not yet ready for a full review.

    • If you think your changes are complete and all tests are passing, you can open a regular Pull Request, indicating it’s ready for review.

    • You can switch between Draft and Ready at any time. If you opened a ready PR but realize more work is needed or tests fail, just convert it back to a draft.

  3. Review and Feedback: Once your PR is open (and marked as ready), a project maintainer will review your changes.

  4. Making Requested Changes: If changes are requested, update your code locally and push the new commits to your branch using fixup commits.

  5. Request Another Review: After pushing the changes addressing the feedback, request another review on the PR.

  6. Merging: Once the reviewers are happy with your contribution and all checks pass, your Pull Request will be approved and merged into the main Moe project!

Don’t hesitate to ask questions in the PR comments if anything is unclear during the review process.