Skip to content

Contributing Guidelines

Thanks for your interest in contributing! 🎉
We welcome all contributions - code, documentation, bug reports, or ideas.

When contributing, remember to follow our Code of Conduct.

How to Contribute?

  • Report bugs or request features by opening an issue.
    • By opening an issue you can be in touch with us to request a new feature or report a bug. Check in the Report bugs or request features section how to make the request.
  • Code contributions: pick an open issue or propose a new idea.
    • Do you have development skills? You also can contribute by helping us to develop Djehuty. Check the Code contributions section on how to become a developer contributor.

Recognition of Contributions

All contributions - whether it’s code, bug reports, or ideas are appreciated and recognized. Contributors may be credited in release notes or project acknowledgments. Thank you for helping make the project better!

Join the community

Contributors are encouraged to take part in the djehuty community, where users and contributors connect, share ideas and discuss recent developments and upcoming work. Join the monthly Community Hours; the community website has the meeting information.

Contribution workflows

Report bugs or request features

We welcome contributions in the form of bug reports and feature suggestions. Here’s how to make them most useful:

  1. Look for Existing Issues

    Before opening a new report or suggestion, check the issue tracker to see if it’s already been raised. This helps prevent duplicates and keeps the conversation focused.

  2. Submit a New Issue

    If you don’t find an existing issue, create a new one using the appropriate template. Add relevant labels if applicable.

  3. Provide Useful Information

    • For Bugs: Explain the steps to reproduce the problem, what you expected to happen, what actually happened, and include any relevant screenshots, logs, or environment details.
    • For Feature Suggestions: Describe the idea clearly and explain why it would improve the project.
  4. Participate in Discussion

    Be ready to answer questions or provide additional details. Open discussion helps the team understand the issue and work toward the best solution.

Code contributions

  1. Contact us!

    Before starting any work, please contact repository maintainers at info@djehuty.4tu.nl to discuss how your idea fits with our strategic goals.

  2. Check or open an issue

    Before you start work, search the issue tracker to see if your idea is already being discussed.

    • If you find a relevant issue, comment to say you’re taking it on and, if possible, assign yourself. If you cannot assign, leave a comment like “Working on this” so maintainers know.
    • If no issue exists, open a new issue using the issue template and include: a short, descriptive title; a brief explanation of the problem or feature and why it’s needed.
    • ⚠️ IMPORTANT: Do not discuss security-related aspects: to report a vulnerability, please see SECURITY.md
  3. Work from a fork

    Contributors should work from a fork of the repository. Maintainers may work directly on the main djehuty instance. If you’re new to the fork-and-pull-request workflow, check out the First Contributions guide for a step-by-step introduction.

  4. Clone and create a branch

    Clone your fork to your local machine and create a new branch for your work.

  5. Set up your development environment

    Follow the Development environment instructions below to install the prerequisites, start a local instance, and run the tests. Make sure you can build and test the project locally before starting your contribution.

  6. Work on your branch and open a PR

    • Make your changes on your branch.
    • Commits must be verified, see the commit signature verification guide for more details.
    • Once your work is ready for review, open a Pull Request (PR) against the main project repository.
    • Provide a clear description of what you changed and link the related issue.
    • Use the PR template - you will see it once you open a PR, and check the approval checklist before submitting.
    • ⚠️ IMPORTANT: Do not open a PR for a security issue: to report a vulnerability, please see SECURITY.md
  7. Final approval and merge

    After review and approval, your PR must be squashed into a single commit. Once the checklist is complete, a maintainer will rebase-merge it into the main branch to keep the history clean.

If you want to make a very small contribution, such as one or a few lines of code for which following the code contributions workflow is not convenient, please contact the core maintainers.


Development environment

Prerequisites

Optional:

  • Quarto, only to build the community site locally with just docs-community. Everything else, including the Markdown documentation (just docs-md) and the API reference (just openapi), works without it, and the published site is built in CI.

Getting started

git clone https://github.com/4TUResearchData/djehuty.git
cd djehuty/

To install your working copy into the current Python environment:

pip install .

Running the development environment

To spin up a fully working local instance, run:

just dev

This builds and starts Docker containers for djehuty and Virtuoso (SPARQL store). On first run, the database is automatically initialized with categories, licences, and a dev account with full admin privileges — no extra setup needed.

Once running:

  • Djehuty: http://localhost:8080 (auto-login, no auth setup needed)
  • Virtuoso SPARQL: http://localhost:8890/sparql (useful for troubleshooting)

Edit any Python file under src/ and the server reloads automatically.

To start the development environment with a Virtuoso database backup (e.g. to test against specific production data):

just db_backup=path/to/prod-2025-10-09_#1.bp dev

Point db_backup at any one of the backup files. All siblings sharing the same prefix in that directory are applied in order, so a full backup plus its incrementals (e.g. prod-2025-10-09_#1.bp, …_#2.bp, …_#3.bp) are restored together.

To stop and remove the development environment, run just clean. To see all available commands, run just --list.

Running the tests

The project includes an end-to-end test suite built with Playwright and pytest. Tests run against a live djehuty + Virtuoso stack seeded with test data — all in containers, so no host Python or browser setup is required.

just test

That single command builds the test image (with Playwright and chromium), brings up Virtuoso and djehuty, loads the SPARQL permissions, runs --initialize, applies the seed dataset, and runs the suite inside the docker network. Coverage data lands in docker/coverage/; failure screenshots in docker/test-results/.

Filter the run with any pytest argument:

just test -m smoke              # one marker
just test -k test_homepage      # by keyword
just test tests/test_auth.py    # specific file

Marker isolation

CI runs each marker (smoke, auth, dataset, admin, embargo, citation, versioning, …) in its own job with a fresh stack, so a test never sees data left over from another marker. just test runs everything against one shared stack, which is faster but means a few state-sensitive tests can fail locally that pass in CI. When that happens, run the affected marker on its own:

just clean   # drop volumes for a truly fresh stack
just test -m citation

CI

Tests run automatically on every push via GitHub Actions. Each runner in the matrix invokes just test -m <marker> against the same compose stack used locally, so a green just test on your laptop reproduces what CI sees. Screenshots are captured on failure and uploaded as artifacts; coverage from each shard is combined into a single report.

Linting

Code style is enforced with Ruff and rolled out incrementally: only paths that have already been cleaned are checked (in the include list under [tool.ruff] in pyproject.toml), starting with src/djehuty/utils/.

just lint

This runs ruff check (bugs, style errors, import sorting) and ruff format --check over the cleaned paths, using the Ruff version pinned in uv.lock. CI runs the same recipe on every push and pull request, so a clean just lint locally means a green Lint job.

just format

That applies Ruff's automatic formatting and fixes to the same paths.

Building the documentation

The documentation site is built with MkDocs. See Documentation in the README for how to build and preview it locally.


Commits

Pull requests are squashed before merging. Keep changes focused, avoid unrelated modifications, and use clear commit messages. When possible follow the Conventional Commits convention:

  • feat: add search filters
  • fix: prevent duplicate uploads
  • docs: improve contributing guide

Releases

Releases are handled by maintainers and automated by GitHub Actions. See RELEASE.md for the step-by-step procedure.


đź’ˇ By contributing to this project, you help us build a positive and supportive community. Thank you!

This section traces the path from invoking djehuty to responding to an HTTP request.

Starting point

Because djehuty is installable as a Python package, the starting point can be found in pyproject.toml:

[project.scripts]
djehuty = "djehuty.ui:main"

The tour starts at src/djehuty/ui.py in the procedure called main.

How djehuty initializes

The main procedure calls main_inner, which handles the command-line arguments. When invoking djehuty web, the following snippet handles it:

import djehuty.web.ui as web_ui
...
if args.command == "web":
    web_ui.main (args.config_file, True, 
                 args.initialize, args,
                 extract_transactions_from_log,
                 args.apply_transactions)

The entry-point for the web subcommand is found in src/djehuty/web/ui.py at the main procedure.

This procedure sets up an instance of WebServer (found in src/djehuty/web/wsgi.py) and uses werkzeug's run_simple to start the web server.

Translating URI paths to internal procedures

An instance of WebServer is passed along in werkzeug's run_simple procedure. Werkzeug calls the instance directly, which is handled by the __call__ procedure of the WebServer class. The __call__ procedure invokes its wsgi instance, configured as follows:

self.wsgi = SharedDataMiddleware(self.__respond, self.static_roots)

The __respond procedure calls __dispatch_request, where the requested URI is translated into a procedure name using the url_map. Except for static resources in src/djehuty/web/resources and pre-configured static pages, URIs are handled by a procedure in the WebServer instance.

The mapping between URIs and their handler procedures can be found in the url_map defined in the WebServer class in src/djehuty/web/wsgi.py.

Diving into the code that displays the homepage

As an example, the url_map contains:

R("/", self.ui_home),

self is a reference to a WebServer instance, so we look for a procedure called ui_home inside that class. Most code editors have a "go to definition" feature to help navigate.

ui_home gathers summary numbers from the SPARQL endpoint:

summary_data = self.db.repository_statistics()

And a list of the latest datasets:

records = self.db.latest_datasets_portal(30)

It then passes that information to __render_template, which renders portal.html from src/djehuty/web/resources/html_templates using Jinja:

return self.__render_template (request, "portal.html",
                               summary_data = summary_data,
                               latest = records, ...)

Database communication

In ui_home, we found a call to self.db.repository_statistics. To find where self.db is assigned:

self.db = database.SparqlInterface()

And where database comes from:

from djehuty.web import database

This leads to src/djehuty/web/database.py.

In repository_statistics, a call to self.__query_from_template is followed by a call to __run_query, which sends the query to the SPARQL endpoint and returns results as a list of Python dictionaries.

self.__query_from_template takes the name of a template file (without extension) containing a SPARQL query. These templates can be found in src/djehuty/web/resources/sparql_templates.