How-to guides are recipes. They guide you through the steps involved in addressing key problems and use-cases. They are more advanced than tutorials and assume some knowledge of how OSRD works.
This is the multi-page printable view of this section. Click here to print.
How-to Guides
- 1: Contribute to OSRD
- 1.1: Batch dependency updates
- 1.2: Report issues
- 1.3: Contribute code
- 1.4: Style guide
- 1.5: Review code
- 2: Deploy OSRD
- 2.1: Docker Compose
- 2.2: Kubernetes with Helm
1 - Contribute to OSRD
First off, thanks for taking the time to contribute!
The following chapters are a set of guidelines for contributing to OSRD1. If you have already contributed to open source projects before, you probably won’t be surprised. If you have not, it will probably help a lot!
Communicate
Chatting with other contributors is a great way to speed things up:
- Join our instant messaging room on IRC at libera.chat#osrd
- Create an issue to discuss your contribution project
Inquire
Just like with any project, changes rely on past work. Before making changes, it is best to learn about what’s already there:
- read technical documentation
- read the existing source code related to your project
- chat with developers who last worked on areas you are interested in
These guidelines are mostly not strict rules, it’s probably fine to do things slightly differently. ↩︎
1.1 - Batch dependency updates
For editoast
We use dependabot on the project to signal when dependencies are outdated. We do not use dependabot to automatically update dependencies, as we want to merge all updates at once and review the changes.
Here is the process to update dependencies:
- Change the versions.
- If you’re using VSCode you can install the
serayuzgur.crates
extension and run the “update all dependencies” command.
Make sure that the new version chosen is stable, and that loose constraints are not overwritten in your commit. - If you’re not, you can go check the versions used by dependabot in its PRs and update the versions manually.
- If you’re using VSCode you can install the
- Run
cargo update
to update the Cargo.lock file (even sub-dependencies). - Check that all dependabot editoast PRs are included in your update.
- Adapt the code to the new versions, if needed.
- Create a PR with your changes, and link all dependabot PRs in the description.
1.2 - Report issues
Please report anything you deem significant!
Our bug tracking platform is github, so you have to register to report bugs.
Follow this link and pick whatever template fits the best.
Bugs
- Bug must have a correct description and the bug’s issue template must be filled carefully.
- Bug must be tagged with (for team members):
kind:bug
- one or several
area:<affected_area>
if possible, if the affected area is not known leave it blank it will be added later by another team member. - one
severity:<bug_severity>
if possible, if severity is not known leave it blank it will be added later by another team member.severity:minor
: User can still use the feature.severity:major
: User sometimes can’t use the feature.severity:critical
: User can’t use the feature.
- OSRD team members can change issues’ tags (severity, area, kind, …). You may leave a comment to explain changes.
- If you are working on a bug or plan to work on a bug, assign yourself to the bug.
- PRs solving bugs should add a regression tests to ensure that bug will not be back in the future.
1.3 - Contribute code
Set things up
Get the source code
- Install
git
.1 - Open a terminal2 in the folder where the source code of OSRD will be located
- Run
git clone git@github.com:osrd-project/osrd
Launch the application
Thanks to docker
, one can easily compile, configure, and run all services after making a change. One can also start only a subset of the services.
- Install
docker
. 13 - Follow OSRD’s README.
Share your changes
The source code of OSRD is available under the LGPLv3 license. By contributing to the codebase, you consent to the distribution of your changes under the project’s license.
LGPLv3 forbids modifying source code without sharing the changes under the same license: use other people’s work, and share yours!
This constraint does not propagate through APIs: You can use OSRD as a library, framework or API server to interface with proprietary software. Please suggest changes if you need new interfaces.
This chapter is about the process of integrating changes into the common code base. If you need help at any stage, open an issue or message us.
If you are not used to Git, follow this tutorial
Create a branch
If you intend to contribute regularly, you can request access to the main repository. Otherwise, create a fork.Add changes to your branch
Before you start working, try to split your work into macroscopic steps. At the end of each stop, save your changes into a commit. Try to make commits of logical and atomic units. Try to follow style conventions.Keep your branch up-to-date
git switch <your_branch> git fetch git rebase origin/dev
Open a pull request
Once your changes are ready, you have to request integration with thedev
branch. If possible, make PR of logical and atomic units too (avoid mixing refactoring, new features and bug fix at the same time). Add a description to PRs to explain what they do and why. Help the reviewer by following advice given in mtlynch article. Add tagsArea:<affected_area>
to show which part of the application have been impacted. It can be done through the web interface.Take feedback into account
Once your pull request is open, other contributors can review your changes:- Any user can review your changes
- Your code has to be approved by a contributor familiar with the code
- All users are expected to take comments into account.
- Comments tend to be written in an open and direct manner. The intent is to efficiently collaborate towards a solution we all agree on.
- Once all discussions are resolved, a maintainer integrates the change.
As a reviewer try to follow advice given in mtlynch article.
If you believe somebody forgot to review / merge your change, please speak out, multiple times if needs be.
Git commit style
The overall format for git commits is as follows:
component: imperative description of the change
Detailed description of the change and what motivates it,
if it is not entirely obvious from the title.
- the commit message, just like the code, must be in english
- all lowercase
- there can be multiple components separated by
:
in case of hierarchical relationships, with,
otherwise - the body of the commit should probably contain a detailed description of the change
1.4 - Style guide
OSRD application is split in multiple services written in several languages. We try to follow general code best practices and follow specificity for each languages when required.
General rules
- Explain what you’re doing and why.
- Document new code with doc comments.
- Include clear, simple tests.
- Break work into digestible chunks.
- Take the time to pick good names.
- Avoid non well-known abbreviations.
- Control and consistency over 3rd party code reuse: Only add a dependency if it is absolutely necessary.
- Every dependency we add decreases our autonomy and consistency.
- Don’t reinvent every wheel: As a counter to the previous point, don’t reinvent everything at all costs.
- If there is a dependency in the ecosystem that is the “de-facto” standard, we should heavily consider using it.
- More code general recommendations in main repository CONTRIBUTING.md.
- Ask for any help that you need!
1.4.1 - Back-end
Python
Python code is used for some packages and integration testing.
- Follow the Zen of Python.
- Code is linted with flake8.
- Code is formatted with Black.
- Imports are sorted with Isort.
- Python tests are written using pytest.
- Typing is checked using pytype.
Rust
- As a reference for our API development we are using the Rust API guidelines. Generally, these should be followed.
- Prefer granular imports over glob imports like
diesel::*
. - Tests are written with the built-in testing framework.
- Use the documentation example to know how to phrase and format your documentation.
- Use consistent comment style:
///
doc comments belong above#[derive(Trait)]
invocations.//
comments should generally go above the line in question, rather than in-line.- Start comments with capital letters. End them with a period if they are sentence-like.
- Use comments to organize long and complex stretches of code that can’t sensibly be refactored into separate functions.
- Code is linted with clippy.
- Code is formatted with fmt.
Java
- Code is formatted with checkstyle.
1.4.2 - Front-end
Nous utilisons ReactJS et tous les fichiers doivent être écrits en Typescript.
Le code est linté avec eslint, et formaté avec prettier.
Nomenclature
Les applications (osrd eex, osrd stdcm, éditeur infra, éditeur matériel) proposent des vues (gestion des projets, gestions des études, etc.) liées à des modules (projet, étude, etc.) qui contiennent les composants.
Ces vues sont constituées de composants et sous-composants tous issus des modules. En plus de contenir les fichiers de vues des applications, elles peuvent contenir un répertoire scripts qui propose des scripts liés à ces vues. Les vues déterminent la logique et l’accès au store.
Les modules sont des collections de composants rattachés à un objet (un scénario, un matériel roulant, un TrainSchedule). Ils contiennent :
- un répertoire components qui héberge tous les composants
- un répertoire styles optionnel par module pour le style des composants en scss
- un répertoire assets optionnel par module (qui contient les assets, de jeux de données par défaut par ex, spécifiques au module)
- un fichier reducers optionnel par module
- un fichier types optionnel par module
- un fichier consts optionnel par module
Un répertoire assets (qui contient les images et autre fichiers).
Enfin, un répertoire common qui propose :
- un répertoire utils pour les fonctions utilitaires communes à l’ensemble du projet
- un fichier types pour les types communs à l’ensemble du projet
- un fichier consts pour les constantes communes à l’ensemble du projet
Principes d’implémentation
Routage & SLUG
Rédaction en cours
projects/{nom du projet}/studies/{nom de l'étude}/scenarios/{nom du scenario}
Styles & SCSS
ATTENTION : en CSS/React, le scope d’une classe ne dépend pas de l’endroit où le fichier est importé mais est valide pour toute l’application. Si vous importez un fichier
scss
au fin fond d’un composant (ce que nous déconseillons fortement par ailleurs), ses classes seront disponibles pour toute l’application et peuvent donc provoquer des effets de bord.
Il est donc très recommandé de pouvoir facilement suivre l’arborescence des applications, vues, modules et composants également au sein du code SCSS, et notamment imbriquer les noms de classes pour éviter les effets de bord, le compilateur se chargera de fabriquer la hiérarchie nécessaire.
Si par exemple nous avons un composant rollingStockSelector
qui propose une liste de matériel rollingStockList
représentés par des cartes rollingStockCard
contenant une image représentant le matériel roulant rollingStockImg
nous devrions avoir la structure SCSS suivante :
.rollinStockSelector {
.rollingStockList {
.rollingStockCard {
.rollingStockImg {
width: 5rem;
height: auto;
}
}
}
}
Ainsi, on a la garantie que l’image contenue dans la carte de matériel roulant héritera bien des bonnes propriétés css .rollinStockSelector.rollingStockList.rollingStockCard.rollingStockImg
.
Noms de classes, utilisation de cx()
Les classes sont ajoutées les unes à la suite des autres, normalement, dans la propriété className=""
.
Cependant, quand cela est nécessaire — tests pour l’utilisation d’une classe, concaténation, etc. — nous utilisons la librairie classnames qui préconise l’usage suivant :
<div className="rollingStockSelector">
<div className="rollingStockList">
<div className="rollingStockCard w-100 my-2">
<img
className={cx('rollingStockImg', 'm-2', 'p-1', 'bg-white', {
valid: isValid(),
selected: rollingStockID === selectedRollingStockID,
})}
/>
</div>
</div>
</div>
Les classes sont séparées chacune dans un string
et les opérations booléennes ou autres sont réalisées dans un objet qui retournera — ou pas — le nom de propriété comme nom de classe à utiliser dans le CSS.
Store/Redux
Tout ce qui est selector est géré par la vue passé en props aux composants et sous-composants.
Par conséquent les appels au store en lecture et en écriture doivent être passés un niveau de la vue, en irrigant par des props et states les composants proposées par la vue.
RTK
Rédaction en cours
Utiliser les endpoints générés à partir des fichiers openapi.yaml
pour consommer le backend.
Lois et éléments importants
Aucun composant ne doit détenir la responsabilité de mise à jour de la donnée qu’il utilise
Seules les vues contiennent les sélecteurs du store, donnés ensuite en props aux composants du module lié à la vue.
Le SCSS n’est pas scopé
Un fichier
.scss
enfoui dans l’arborescence ne vous garantit pas que les classes contenues soient seulement accessibles à cet endroit, y compris par import react (formellement interdit au passage : vous devez utiliser l’import SCSS), toutes les classes déclarées sont accessibles partout.Préférez un choix judicieux de nom de classe racine pour un module donné et utilisez l’arborescence possible dans le fichier SCSS.
Les liens des imports doivent être absolus au sein d’une application
Vous devez utiliser le chemin complet pour tous vos imports, même si le fichier à importer se trouve dans le même répertoire.
Les liens des imports doivent être relatifs au sein d’un module ou d’un composant
Au sein d’un module ou d’un composant, à l’instar d’une librairie, les liens d’imports doivent rester relatifs afin de permettre leur utilisation n’importe où.
TypeScript
import & export
We recommend using typed imports and exports.
When an import
or export
contains only types, indicate it with the type
keyword.
export type { Direction, DirectionalTrackRange as TrackRange };
import type { typedEntries, ValueOf } from 'utils/types';
This allows to:
- Improve the performance and analysis process of the compiler and the linter.
- Make these declarations more readable; we can clearly see what we are importing.
- Avoid dependency cycles:
The error disappears with the type
keyword
- Make final bundle lighter (all types disappear at compilation)
1.4.3 - Tests
Back-end
- Integration tests are written with pytest in the
/tests
folder. - Each route described in the
openapi.yaml
files must have an integration test. - The test must check both the format and content of valid and invalid responses.
Front-end
The functional writing of the tests is carried out with the Product Owners, and the developers choose a technical implementation that precisely meets the needs expressed and fits in with the recommendations presented here.
We use Playwright to write end-to-end tests, and vitest to write unit tests.
The browsers tested are currently Firefox and Chromium.
Basic principles
- Tests must be short (1min max) and go straight to the point.
- Arbitrary timeouts are outlawed; a test must systematically wait for a specific event. It is possible to use polling (retry an action - a click for example - after a certain time) proposed in the Playwright’s API.
- All tests must be parallelizable.
- Tests must not point to or wait for text elements from the translation, prefer the DOM tree structure or place specific
id
. - We’re not testing the data, but the application and its functionality. Data-specific tests should be developed in parallel.
Data
The data tested must be public data.
The data required (infrastructure and rolling stock) for the tests are offered in the application’s json
files, injected at the start of each test and deleted at the end, regardless of its result or how it is stopped, including with CTRL+C
.
This is done by API calls in typescript before launching the actual test.
The data tested is the same, both locally and via continuous integration.
Atomicity of a test
Each test must be atomic: it is self-sufficient and cannot be divided.
A test will target a single feature or component, provided it is not too large. A test will not test an entire module or application; it will necessarily be a set of tests, in order to preserve test atomicity.
If a test needs elements to be created or added, these operations must be carried out by API calls in typescript upstream of the test, as is done for adding data. These elements must be deleted at the end of the test, regardless of the result or how it is stopped, including by CTRL+C
.
This allows tests to be parallelized.
However, in certain cases where it is relevant, a test may contain several clearly explained and justified test subdivisions (several test()
in a single describe()
).
Example of a test
The requirement: “We want to test the addition of a train to a timetable”.
- add the test infrastructure and rolling stock to the database by API calls.
- create project, study and scenario with choice of test infrastructure by API calls.
- start the test, clicking on “add one or more trains” until the presence of the trains in the timetable is verified
- the test passes, fails or is stopped, the project, study and scenario are deleted, along with the test rolling stock and infrastructure by API calls.
NB: the test will not test all the possibilities offered by the addition of trains; this should be a specific test which would test the response of the interface for all scenarios without adding trains.
2 - Deploy OSRD
First of all, we recommend learning about the containers architecture of OSRD.
We will cover how to deploy OSRD within the following setups:
- Using docker compose on a single node.
- Using helm on a kubernetes cluster.
It is also possible to deploy each service of OSRD manually on a system, but we will not cover this topic within this guide.
2.1 - Docker Compose
The OSRD project includes a docker-compose.yml
file designed to facilitate the deployment of a fully functional OSRD environment. Primarily intended for development purposes, this Docker Compose configuration can also be adapted for quick, single-node deployments.
Prerequisites
Before proceeding with the deployment, ensure that you have the following installed:
- Docker
- Docker Compose
Configuration Overview
The docker-compose.yml
file defines the following services:
- PostgreSQL: A PostgreSQL database with PostGIS extension.
- Redis: A Redis server for caching.
- Core: The core OSRD service.
- Front: The front-end service for OSRD.
- Editoast: A OSRD service responsible for various editorial functions.
- Gateway: Serves as the gateway for the OSRD services.
- Wait-Healthy: A utility service to ensure all services are healthy before proceeding.
Each service is configured with health checks, volume mounts and necessary environment variables.
Deployment Steps
- Clone the Repository: First, clone the OSRD repository to your local machine.
- Environment Variables (optional): Set necessary environment variables if you need to adjust some configurations.
- Build and Run: Navigate to the directory containing
docker-compose.yml
and run:
docker-compose up --build
This command builds the images and starts the services defined in the Docker Compose file.
Accessing Services
While all HTTP service are used through the gateway (http://localhost:4000
), you can access directly each service using their exposed ports:
- PostgreSQL: Accessible on
localhost:5432
. - Redis: Accessible on
localhost:6379
. - Core Service: Accessible on
localhost:8080
. - Front-End: Accessible on
localhost:3000
. - Editoast: Accessible on
localhost:8090
.
Notes and Considerations
- This setup is designed for development and quick deployments. For production environments, additional considerations for security, scalability and reliability should be addressed.
- Ensure that the
POSTGRES_PASSWORD
and other sensitive credentials are securely managed, especially in production deployments.
2.2 - Kubernetes with Helm
The OSRD project’s Helm Chart provides a flexible and efficient way to deploy OSRD services in a Kubernetes environment. This document outlines the configuration options available in the Helm Chart, focusing on each service component.
Prerequisites
Before proceeding with the deployment, ensure that you have the following installed:
- A Kubernetes cluster up and running
- A PostgreSQL database with PostGIS
- A Redis server (used for caching)
The tileserver
Tileserver is the component responsible for generating vector map tiles. It is recommended to separate it from standard Editoast while running a production setup since Editoast cannot be scaled horizontally (it is stateful).
You can visualize the recommended deployment here:
flowchart TD
gw["gateway"]
front["front-end static files"]
gw -- local file --> front
browser --> gw
gw -- HTTP --> editoast
gw -- HTTP --> tileserver-1
gw -- HTTP --> tileserver-2
gw -- HTTP --> tileserver-n...
editoast -- HTTP --> core
The Helm chart leverages Kubernete’s HorizontalPodAutoscaler in order to spawn as much tileserver as required for the current workload.
Chart Values Overview
The Helm Chart is configurable through the following values:
Core Service
core
: Configuration for the core OSRD service.internalUrl
: Internal URL for service communication.image
: Docker image to use.pullPolicy
: Image pull policy.replicaCount
: Number of replicas.service
: Service type and port configuration.resources
,env
,annotations
,labels
,nodeSelector
,tolerations
,affinity
: Various Kubernetes deployment options.
Editoast Service
editoast
: Configuration for the Editoast service.- Includes similar options as
core
for Kubernetes deployment. init
: Initialization configuration.
- Includes similar options as
Tile Server
tileServer
: Specialized Editoast service that serves only vector map tiles.enabled
: Set totrue
to enable tile server functionality.image
: Docker image to use (typically the same as Editoast).replicaCount
: Number of replicas, allowing for horizontal scaling.hpa
: Horizontal Pod Autoscaler configuration.- Other standard Kubernetes deployment options.
Gateway
gateway
: Configuration for the OSRD gateway.- Includes service, ingress, and other Kubernetes deployment options.
config
: Specific configurations for authentication and trusted proxies.
Deployment
The chart is available at ghcr OCI repository. You can find 2 Helm charts:
- Stable charts:
oci://ghcr.io/osrd-project/charts/osrd
- Dev charts:
oci://ghcr.io/osrd-project/charts/osrd-dev
To deploy the OSRD services using this Helm Chart:
Configure Values: Adjust the values in the Helm Chart to suit your deployment needs.
Install Chart: Use Helm to install the chart into your Kubernetes cluster.
helm install osrd oci://ghcr.io/osrd-project/charts/osrd -f values.yml