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!

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 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 using the project’s commit message template. 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

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.


Conventions

Code conventions

Follow existing code conventions and existing patterns such as:

  • Naming conventions: Follow existing patterns for variables, functions, classes, and file names.
    • Use snake_case for functions and variables.
    • Be descriptive but concise in names.
  • Indentation: Use the same indentation style (tabs vs. spaces, number of spaces) already present in the codebase.
    • Line length: Keep lines within the project’s limit (100 characters, enforced by Ruff).
    • Comments & docs: Write comments/docstrings in the same style.
  • No unused code: Remove dead or commented-out code before committing.
  • Linting: Run just lint before committing (or format using just format).

Commits

All commits will be squashed into a single commit before merging into main. This has two purposes:

  • Clean history: The main branch stays tidy.
  • Readable log: Each merge commit clearly tells the story of a completed change.

When planning a change remember to:

  • Limit the scope: Keep the diff as small as possible so reviewers can understand the change quickly.
  • Avoid commit noise: Don’t include generated files, formatting-only changes, or experimental code unless they are the sole purpose of the commit.

For the squashed commit message please also have a look at the commit message template.

Branch Naming Conventions

Branches must follow a consistent naming scheme to make collaboration, reviews, and automation easier. Use the following pattern:

wip-<type>-<issue-number>-<short-description>
- wip = prefix for “Work in Progress”
- type = category of change (bug, feat, impr, docs, chore)
- issue-number = the GitHub issue number related to the work (if applicable)
- short-description = a brief, kebab-case summary of the change

Example of branch name:

Type Branch name example When to use
bug wip-bug-123-fix-login-crash Bug fixes
feat wip-feat-007-add-endpoint New features
impr wip-impr-321-optimize-query Improvements, refactors, cleanup
docs wip-docs-789-update-install-guide Documentation updates
chore wip-chore-101-bump-dependencies Maintenance or config updates

Releases

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


Templates

Issue Template

🐞 Bug

Use when something is broken or misbehaving (broken functionality).

**Describe the bug**
A clear and short description of the bug.

** Steps to Reproduce**
Steps to reproduce the behavior:

1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error

**Expected behavior**
A clear and short description of what you expected to happen.

**Screenshots**
If applicable, add screenshots to help explain the problem.

**Your personal set up:**
 - Smartphone or Desktop
 - OS: [e.g. iOS]
 - Browser [e.g. chrome, safari]

**Additional context**
Add any other context about the problem here.

🪴 Improvement

Use when you want to refine an existing functionality (enhance functionality).

**Summary**
A clear and short description of the enhancement.

**Current Behavior**
Brief description of the existing behavior or limitation.

**Proposed Improvement**
How you suggest to improve it.

**Additional Notes**
References, related issues, examples.

🚀 New Feature

Use when you would like to introduce a new idea.

Pull Request Template

Regardless of the issue type, use the PR template below. Note that some PRs may not be associated with an issue.

**Summary**
A clear and short description of the change. Please provide what and why.

**Changes**
- filename: description of key update. Keep it concise.

**Approval Checklist**
- [ ] I agree to follow _Djehuty's_ [code of conduct](https://github.com/4TUResearchData/djehuty?tab=coc-ov-file#readme).
- [ ] I have read and I have follow the [code contribution workflow](https://github.com/4TUResearchData/djehuty/blob/main/CONTRIBUTING.md).
- [ ] Code style and conventions were respected.
- [ ] Documentation has been updated where needed (README, docs, or examples).
- [ ] Review approved by at least one maintainer.
- [ ] Merge readiness (PR is squashed into a single commit and follows the [commit template](https://github.com/4TUResearchData/djehuty/blob/main/CONTRIBUTING.md#commit-message-template)).

**Issue Reference (optional - PRs may not be associated with an issue)**
Closes #ISSUE_NUMBER

**Screenshots (optional)**
Before/After visuals, UI changes, or relevant logs.

**Notes (optional)**
Additional context, caveats, or follow-up tasks.

Commit Message Template

The commits in djehuty have a specific format. By being detailed in your commit message, you help specific changes to the software be more traceable, and if necessary, revertible.

The commits should be clear and focused. In the commit message:

  • The first line provides a general idea of what change has been done and in which part of code.
  • The following lines give a one-line summary of changes made to each individual file with the commit.
  • If a line extends 80 characters, a line break should be introduced.
  • Imperative mood (e.g. “Add test for …”, “Implement error handling …”, “Fix UUID validator …”) is used to describe the changes made.

The message follows the format:

[folder]:[subfolder]: <Describe a change in one line>
* [path to 1st file changed]: <Describe change in the file>
* [path to 2nd file changed]: <Describe change in the file>
* [path to 3rd file changed]: <Describe change in the file>
...

Example of a commit message:

web: html_templates: Add keyword autocomplete options.
* src/djehuty/web/resources/html_templates/depositor/edit-dataset.html: Add
  ID for displaying keyword autocomplete and edit help text.
* src/djehuty/web/resources/static/js/edit-dataset.js: Load keyword
  autocomplete options when typing a keyword.
* src/djehuty/web/resources/html_templates/depositor/edit-collection.html: Add
  ID for displaying keyword autocomplete and edit help text.
* src/djehuty/web/resources/static/js/edit-collection.js: Load keyword
  autocomplete options when typing a keyword.
* src/djehuty/web/resources/static/js/utils.js: Add method to search keyword
  options and load them as an autocomplete dropdown.


Label Guide

To help indicate the status of issue or pull request discussions, maintainers will apply labels to each as described below:

Label When to use
🐞 bug Something is broken or behaves unexpectedly
🪴 improvement Refining current functionality
🚀 new feature Introducing functionality that did not previously exist
📚 documentation Docs updates, corrections, or additions
🔧 refactor Internal code restructuring without changing external behavior
🌱 good first issue Beginner-friendly tasks with clear steps
💬 needs discussion Further clarification or consensus is required
blocked Waiting on dependencies, or prerequisites
wontfix This will not be worked on
duplicate Waiting on dependencies, or prerequisites

💡 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.