How to contribute#

Leer en español

To contribute, just fork this repository, make a new branch and open a pull request.

cpanel-cli is written in Python (version 3.11 or later required). I organized the code into a standard tree:

cpanel-cli
├── API.md
├── CONTRIBUTING.rst
├── cpanel
│   ├── cli.py
│   ├── core.py
│   ├── __init__.py
│   ├── __main__.py
│   ├── REFERENCE
│   └── USAGE
├── doc
│   ├── conf.py
│   ├── contributing.rst
│   ├── index.rst
│   ├── locale
│   │   └── es
│   │       └── LC_MESSAGES
│   │           ├── contributing.po
│   │           ├── index.po
│   │           ├── installation.po
│   │           ├── reference.po
│   │           └── reference
│   │               └─── *.po
│   ├── reference.sh
│   ├── requirements.txt
│   └── _static
│       ├─── *.svg
│       └─── *.png
├── hatch.py
├── LICENSE
├── Makefile
├── pyproject.toml
├── pyrightconfig.json
├── README.rst
├── .readthedocs.yaml
├── test
│   ├── cpanelrc.test.example
│   └── test_uapi.py
└── tox.ini

cpanel contains the main source code. Files REFERENCE and USAGE contain the actual text for the --help and --version flags and the help command. (I keep them in external files to make them easier to change. Also, I can automatically parse REFERENCE to generate *.rst files for the Sphinx documentation builder. See the reference.sh script below.)

Standard pyproject.toml contains the project metadata, development and release dependencies, and build backend definitions. (See Writing your pyproject.toml for further info.)

I’m using the Hatchling build backend, with a small custom hatch.py script to get the dynamic metadata properties in pyproject.toml. The custom hatch.py script works by parsing the cpanel/__init__.py source file.

pyrightconfig.json is the configuration file for the Pyright static type checker.

test contains a set of unit API tests. They’re written using the tox automation framework. The code driving the tests is in test/test_uapi.py; the main tox configuration file is tox.ini. These are not simple standalone unit tests, but API tests running against a live cPanel instance. See Running tests below for further details.

doc contains the documentation sources, written in reStructuredText and processed using Sphinx. The main configuration file for Sphinx is doc/conf.py. The Sphinx version and theme used to build the documentation are in doc/requirements.txt.

The source files for the documentation are in doc/*.rst. The index.rst file is the main page. You will notice a doc/reference folder and a series of *.rst files on it. These are programmatically generated by the reference.sh script and need not be edited manually. (I’m committing them to the source tree because they serve as the actual source files for Sphinx.)

The reference.sh script generates doc/reference.rst and the files inside doc/reference/ by parsing the REFERENCE text file, splitting it into sections and converting them to restructuredText. This allows me to keep REFERENCE as a single source of truth for the documentation and keep the Sphinx documents up to date.

_static contains the images and SVG files used in the documentation.

.readthedocs.yaml is a configuration file for Read the Docs. The remote Sphinx build system uses this file.

I maintain a Spanish translation of the documentation, generated using strings from a series of catalog files (*.po) inside locale/es/LC_MESSAGES/. See Translations for further information.

Finally, I’m using a Makefile to automate all phases of the development life cycle. (Make and Makefiles are awesome.)

Development environment#

I developed cpanel-cli on Ubuntu Linux 23.10 “Mantic” with Python 3.11. cpanel-cli, however, has no special requirements, so any Linux distro supporting at least Python 3.11 should work. You can also use macOS “Ventura” or a later macOS release.

To create a development environment on macOS:

Install Python 3:

$ brew install python

Homebrew will install the latest Python version available. If you want to install a specific version, you can use the excellent Pyenv utility to manage different releases of Python side by side.

Install GNU Make:

$ brew install make

To create a development environment on Linux:

On a Debian-based distro (Ubuntu, Mint), install Python 3 using:

$ sudo apt install python3 python3-pip python3-venv

On a RPM-based distro (RHEL, Fedora), install Python 3 using:

$ sudo dnf install python3 python3-pip

apt and dnf will install the latest Python version available. If you want to install a specific version, you can use the excellent Pyenv utility to manage different releases of Python side by side.

GNU Make is installed by default on most Linux distros. Check its availability using:

$ make --version

Building a local cpanel-cli package from source#

Build and install a local cpanel-cli package:

$ make install

This will:

  1. Create a new virtual Python 3 environment in a venv directory

  2. Locally install in venv the development packages listed on the [project.optional-dependencies] section of pyproject.toml

  3. Build a local Python package cpanel-cli

Running the local executable#

To run the executable, first activate the virtual environment (you need to run this only once per session):

$ source venv/bin/activate

Then run the cpanel utility:

$ cpanel --help

If you edit the sources, just run pip3 install . (note the dot .) to rebuild the local package.

Running the (optional) type checker#

Running the type checker is optional — you can ignore this step if you want.

The Python source code is annotated using type hints. I use them to improve the readability of Python code. Read the Python Type Checking Guide for an excellent introduction to the use of type hints in Python.

Type hints are not actually checked by the Python runtime — you need a third party type checker utility. For this project I use Pyright, which is my Python type checker of choice.

To install Pyright:

$ pip3 install --user pyright

Run it using:

$ make typecheck

The type checker configuration is in pyrightconfig.json.

Note that Pyright is based on Node.js, so that pip will indirectly install it and pull a lot of JavaScript dependencies required by Pyright.

Running tests#

I’m using the tox automation framework for a series of unit API tests. The main code driving the tests is in test/test_uapi.py; the main tox configuration file is tox.ini.

These are not simple unit tests, but unit API tests running against a live cPanel instance. To run the tests, you need access to a cPanel instance running on another host reachable from the host you’re running the tests on.

To set the remote hosts credentials, make a copy of the provided cpanelrc.test.example file and name it cpanelrc.test (keep in the test directory):

$ cp test/cpanelrc.test.example test/cpanelrc.test

Then edit cpanelrc.test and set:

  • The hostname of your cPanel instance

  • The username of your cPanel account

  • An API token associated to that username

Token-based authentication is the only supported authentication method.

To run the tests, use:

$ make test

The above command will hit the cPanel UAPI REST interface with most of the functions implemented in cpanel-cli.

The remote state of cPanel is left unchanged, i.e., the tests are strictly non-destructive.

Packaging#

Packaging is done via the Hatchling build backend, as specified on the [build-system] section of pyproject.toml.

To run the packager, use:

$ make package

The above command should generate the following two distribution files in the temporary dist directory:

cpanel_cli-<version>-py3-none-any.whl
cpanel-cli-<version>.tar.gz

where <version> is the release number set in cpanel/__init__.py.

The tarball is the source archive; the wheel file is the built distribution archive. The included files for these distribution packages are listed on the [tool.hatch.build.targets.sdist] and [tool.hatch.build.targets.wheel] sections of pyproject.toml respectively.

These packages are ready to be uploaded to the Python Package Index.

Building the documentation#

The API documentation source files are in the doc directory. These comprise reStructuredText (.rst) files which are processed using Sphinx into groups of static HTML trees.

To build the documentation, use:

$ make doc

The above command will generate several static HTML trees in doc/build/html. For example, it generates the default English documentation in doc/build/html/en — the start page is a conventional index.html file.

This GitHub repository is currently connected to my Read the Docs account, so that any committed (or merged) change that updates the documentation sources will automatically trigger a remote Sphinx rebuild. The resulting updated HTML documentation will always be available at https://cpanel-cli.readthedocs.io/en/stable/

The main configuration file for Sphinx is doc/conf.py. The Sphinx version and theme used to build the documentation are in doc/requirements.txt.

Translations#

The English language *.rst files in doc are the source documentation files. Any translation is based on these documents. Translation is done on a string-by-string basis, using the original English string as a key (msgid), and the corresponding translated string as a value (msgstr). For example, for Spanish:

msgid "To be, or not to be, that is the question"
msgstr "Ser o no ser, he ahí el dilema"

These msgid and msgstr pairs are kept in a catalog file (*.po), which is a simple text file. These catalog files are stored in the doc/locale subdirectory.

I personally maintain a Spanish translation of the documentation in catalog files doc/locale/es/LC_MESSAGES/*.po.

Catalog .po files are compiled into .mo files using the Sphinx internationalization utility. These compiled .mo files are later used to compose translated versions when Building the documentation.

Adding a translation#

To add a new translation:

  1. Create a new catalog using:

    $ make locale iso=<language code>
    

    where <language code> is the ISO 639-1 code corresponding to the new language. For example, to add a French translation you would use:

    $ make locale iso=fr
    

    This would add a new locale/fr/LC_MESSAGES/index.po directory with several .po files in it.

  2. Edit the .po files created in step 1 and insert the translated strings as msgstr fields. For example:

    msgid "Indices and tables"
    msgstr "Indices et tableaux"
    
  3. Rebuild the documentation:

    $ make doc
    

    The above command will create a new static HTML tree in doc/build/html/<language code>. For example, for French, it will create a new tree in doc/build/html/fr.

Correcting and expanding an existing translation#

if you edit the original doc/*.rst source documentation files, you need to update the translations as well:

  1. Run the following to update the catalog files:

    $ make locale iso=<language code>
    

    where <language code> is the ISO 639-1 code. You need to run it for every translated language.

  2. The previous step will emit a report telling you which .po files need to be updated, for example:

    Update: doc/locale/es/LC_MESSAGES/reference.po +5, -2
    Update: doc/locale/es/LC_MESSAGES/contributing.po +9, -0
    

    Open the mentioned .po files and edit or add new msgstr strings. Be advised that some entries might get annotated as #, fuzzy, which means the internationalization engine is not sure if there already exists a translation for the entry because of similarities with another entry. Just edit the msgstr text and delete the fuzzy line.

For further information, see the Internationalization Guide