# Contribution guide

## Error reporting

* Create a new issue: <sshmonitor-gitlab-url:issues/new>
  * Describe relevant details that you think might be related to the issue
    (like initial conditions, environment variables, etc).
  * Don't forget to specify what version of {{SSH_Monitor}} you are using.
  * Try to provide a *minimal* example that will reproduce the error.
  * Do not hesitate to share logs, screenshots or any other interesting files/information.

---

## Improvements and features suggestions

* Create a new issue: <sshmonitor-gitlab-url:issues/new>
  * Describe what improvements/features you would like to see coming.
  * Explain how those improvements/features would positively change the way you use {{SSH_Monitor}}.

---

## General work process

* As much as possible,
  work on the [specified SSH Monitor issues](sshmonitor-gitlab-url:issues).
  The priority issues are the ones specified in the upcoming [milestones](sshmonitor-gitlab-url:milestones).
  If you want to work on a subject that is not contained in an existing issue,
  then [create an issue first](sshmonitor-gitlab-url:issues/new).
* Before working on an issue, make sure that this issue is attached to a
  [milestone](sshmonitor-gitlab-url:milestones).
  If not, add it to an existing milestone
  or [create a new one](sshmonitor-gitlab-url:milestones/new).
* Before working on an issue,
  make sure that the associated milestone has a dedicated Git branch
  (and dont forget to switch to it).
* When creating new Git commits,
  please end your commit message with a newline referencing the issue you are working on
  (e.g. end with `see #123`,
  check [this commit](sshmonitor-gitlab-url:commit/9b9bfba2)
  as an example).
* Before closing an issue, make sure that:
  * The HMIs have been updated and tested.
  * The documentation has been updated.
  * The tests have been updated and still pass.

---

## Release checklist

> The versioning convention to follow is
> [semver](web-archive:20220312074943/https://semver.org/).
>
> 🚧 **?TODO?** 🚧 [git-cliff](https://git-cliff.org/docs/)?

* Create a milestone for an upcoming release.
  E.g. see [milestone v2.1.0](sshmonitor-gitlab-url:milestones/4#tab-issues).
* Create a dedicated branch associated to the milestone.
  E.g. see [milestones_v2.1.0](sshmonitor-gitlab-url:branches/all).

    For example:

    ```{code} bash
    $ git switch -c v1.2.3
    ```

* Add/create issues associated to the milestone.
  E.g. see [the issues of the milestone v2.1.0](sshmonitor-gitlab-url:milestones/4#tab-issues)
* Solve every issues associated to the milestone.
* Before merging the milestone branch, one or several last commits should be pushed in order to
  make sure that:
  * The HMIs have been updated and tested.
  * The documentation has been updated.
  * The tests have been updated and still pass.
  * The [changelog file](sshmonitor-file-gitlab-url:documentation/CHANGELOG.md) is up to date.\
    Each issue addressed in the milestone should be referenced here.
  * The [project layout file](sshmonitor-file-gitlab-url:documentation/project_layout.md) is up to date.

  * The links in the documentation have been checked with [markdown-link-check](https://github.com/tcort/markdown-link-check):

      ```{code} bash
      $ find ./documentation -name "*.md" -not -path "./documentation/.site/*" -print0 | xargs -0 -n1 markdown-link-check -p
      ```

  * The [README.md](sshmonitor-file-gitlab-url:README.md)
    and the [documentation directory](sshmonitor-directory-gitlab-url:documentation)
    are up to date.
  * The [tests](sshmonitor-directory-gitlab-url:tests)
    are up to date and are passing with the following command:

      ```{code} bash
      $ nix flake check
      ```

  * The [CI](sshmonitor-gitlab-url:pipelines) does not break.
  * The `PROJECT_NUMBER`,
    in the [doxyfile.conf](sshmonitor-file-gitlab-url:documentation/.doxygen/doxyfile.conf),
    has been updated to the current milestone version.
  * The `version`,
    in the [conf.py](sshmonitor-file-gitlab-url:documentation/conf.py),
    has been updated to the current milestone version.
  * The `version`,
    in the [pyproject.toml](sshmonitor-file-gitlab-url:pyproject.toml),
    has been updated to the current milestone version.
  * The Doxygen documentation has been re-generated against that new milestone version.
  * The Sphinx documentation has been re-generated against that new milestone version.
  * The GitLab pages have been updated accordingly.
* [Create a new Merge Request](sshmonitor-gitlab-url:merge_requests/new)
  (e.g. like the [merge request for v2.1.0](sshmonitor-gitlab-url:merge_requests/8))
  * Add at least one reviewer to the MR (ideally not yourself) and wait for review(s): change
    your MR accordingly.
  * Once the reviewer(s) approves your MR, merge it.
* Once the MR is merged, tag the latest commit of this MR
  (e.g. [`v2.1.0`](sshmonitor-gitlab-url:tags/v2.1.0)).
* Once the tag has been created,
  [submit a new release](sshmonitor-gitlab-url:releases/new) associated to this tag
  (dont forget to check "Include message from the annotated tag.") and associated to the milestone.
* Close the milestone.

---

## Documentation

* Follow the documentation convention: <https://diataxis.fr/>

* Prerequisites:
  * Doxygen (`1.11+`)
  * Poetry (`1.8+`)
    * After installing Poetry with your package manager,
      don't forget to run `poetry install` at the root of the {{SSH_Monitor}} project.

* [Semantic Line Break](https://sembr.org/)

* [CommonMark](https://commonmark.org/)

* Doxygen
  * `$ doxygen documentation/.doxygen/doxyfile.conf`
  * Make sure there is no warning nor error in the Doxygen output.
  * Note that Doxygen will use the scripts located in the
    [filter-patterns-scripts directory](sshmonitor-directory-gitlab-url:documentation/.doxygen/filter-patterns-scripts)
    directory in order to trick Doxygen into thinking that
    {{substitutions}} and {{template}} files have a syntax similar to Python files.
  * Note that the configuration file used by Doxygen -- in this project --
    is [doxyfile.conf](sshmonitor-file-gitlab-url:documentation/.doxygen/doxyfile.conf).
  * Note that Doxygen will use the layout
    [doxygen_layout.xml](sshmonitor-file-gitlab-url:documentation/.doxygen/doxygen_layout.xml)
    in order to rearrange the generated HTML and LaTeX pages.
    In particular the "Detailed Description" section is put on top
    (this has been done by moving the `<detaileddescription title=""/>` tag
    of the original layout generated with `doxygen -l`).
  * Note that Doxygen will use filter patterns scripts (through the `FILTER_PATTERNS` option of
    [doxyfile.conf](sshmonitor-file-gitlab-url:documentation/.doxygen/doxyfile.conf)).
    Here, those scripts are used to trick Doxygen
    into thinking that {{EPICS}} {{template}} and {{substitutions}} files are valid Python files,
    in order to get a clearer Doxygen documentation for the {{EPICS}} {{App}}.

* Sphinx
  * `$ poetry run sphinx-build --fail-on-warning --keep-going --fresh-env --write-all --jobs auto --builder html ./documentation ./documentation/.site`
  * Make sure there is no warning nor error in the Sphinx output.
  * Then you can check how the Sphinx documentation renders in your web browser (e.g. Firefox): `$ firefox ./documentation/.site/index.html`

* Spelling and phrasing
  * `vale --config='.vale.ini' --glob='*.md' ./documentation/*`

* No broken link objectif:
  * Extract every <https://gitlab.com> link and check them
    in order to make sure they are up to date.
  * Every time an internet-link is added to the documentation,
    please make sure that the link points to the associate
    [web archive (wayback machine)](https://web.archive.org) page.

    E.g. if you have a link to:
    `https://epics-docs-mj-test.readthedocs.io/projects/epics-base/en/latest/record-reference.html`

    then -- *instead* -- add a link to:
    `web.archive:20240612143143/https://epics-docs-mj-test.readthedocs.io/projects/epics-base/en/latest/record-reference.html`

  * Check links availability of the documentation with
    [markdown-link-check](https://github.com/tcort/markdown-link-check):

      ```{code} bash
      $ cd ./documentation
      $ find . -name \*.md -print0 | xargs -0 -n1 markdown-link-check -p
      ```

* Each new {{substitutions}} file file *must*:
  * have a dedicated recommended how-to
  * have a dedicated reference page
  * appear in the documentation everywhere other {{substitutions}} files are mentioned

---

## Developments

* Commit convention:
  <web-archive:20220305083819/https://www.conventionalcommits.org/en/v1.0.0/>

* Comments convention (when discussing GitLab issues):
  <web-archive:20220208061845/https://conventionalcomments.org/>

* 🚧 **TODO** 🚧: EPNix & Nix

* 🚧 **TODO** 🚧: {{EPICS}} (`.c`, `.cpp`, `.h`, `Makefile`, `.db`, `.dbd`, `.template`, `.substitutions`, etc)

---

## Tests

* How to run the quick tests with epics v7 (with the optional `builders` option):

    ```{code} bash
    $ sudo nix build -L ".#checks.x86_64-linux.quick-tests-with-epics-v7" --builders "@$XDG_CONFIG_HOME/nix/build_servers"
    ```

* How to run the full tests with epics v7 (with the optional `builders` option):

    ```{code} bash
    $ sudo nix build -L ".#checks.x86_64-linux.full-tests-with-epics-v7" --builders "@$XDG_CONFIG_HOME/nix/build_servers"
    ```

* How to run all the tests (with epics v3 and v7):

    ```{code} bash
    $ nix flake check
    ```

* How to access the tests VMs and play with them:

    ```{code} bash
    $ nix build ".#checks.x86_64-linux.full-tests-with-epics-v7.driverInteractive" -L
    $ ./result/bin/nixos-test-driver # this executable will open the below Python shell
    ```

    ```{code} python
    ...
    >>> start_all() # start all VMs
    ...
    >>> test_script() # run tests
    ...
    >>> host_machine.succeed("ip a")
    ...
    >>> intermediate_machine.succeed("ip a")
    ...
    >>> target_machine.succeed("ip a")
    ...
    >>> host_machine.shell_interact() # access to a shell running inside a VM
    ...
    ```

    ```{caution}
    In this case,
    make sure you are using a terminal that won't "suck" the windows that it might open.
    So it's not recommended to use terminals like [`st`](https://st.suckless.org/),
    it is preferred to use terminals like [alacritty](https://alacritty.org/).
    Otherwise, when running the `start_all()` command,
    the VMs windows might pop up
    and replace the terminal in which you ran `nixos-test-driver`,
    thus preventing you from running subsequent commands
    like `test_script()`, `<machine-name>.succeed()`, `<machine-name>.shell_interact()`, etc.
    ```

* Let's say you are interactively testing an {{IOC}} running on a VM.
  How to access it from your local machine (e.g. with Phoebus, or caget/caput)?
  In order to do so, run the following Python commands:

    ```{code} Python
    >>> host_machine.send_monitor_command(f"hostfwd_add tcp::5064-:5064")
    ...
    >>> host_machine.send_monitor_command(f"hostfwd_add udp::5064-:5064")
    ...
    >>> host_machine.send_monitor_command(f"hostfwd_add tcp::5065-:5065")
    ...
    >>> host_machine.send_monitor_command(f"hostfwd_add udp::5065-:5065")
    ...
    >>> host_machine.succeed("systemctl stop firewall")
    ...
    ```

* How to re-use the VMs states coming from a previous run:

    ```{code} bash
    $ ./result/bin/nixos-test-driver --keep-vm-state
    
        ...
    ```

    (just use the `--keep-vm-state` flag)

    The machine state is stored in the `$TMPDIR/vm-state-machine-name` directory.

* ```{important}
  When including new {{shell_instruction}}s in {{substitutions}} files,
  do not forget to also specify them in the Python test scripts
  (by setting up the restricted shell accordingly)!
  ```

---

## Training

* How to build the training VM? The associated Nix code is located in the `./training-vm` directory,
  and will produce an `.ova` file for VirtualBox:

  ```{code} bash
  $ nix build -L ".#training-vm"
  ```

  The resulting `.ova` file can be found in `./result/nixos-12.34.yyyymmdd.hash-x86_64-linux.ova`.
