Contributing
============

.. note:: **Abstract**

   This document describes the development process of {kiwi}
   and how you can be part of it. This description applies
   to version |version|.

.. toctree::
   :maxdepth: 1

   contributing/kiwi_from_python
   contributing/kiwi_plugin_architecture
   contributing/scripts_testing
   contributing/schema_extensions.rst

The Basics
----------

The core appliance builder is developed in Python and follows the test-
driven development rules.

If you want to implement a bigger feature, consider opening an issue on
GitHub first to discuss the changes. Or, join the discussion in the
`#kiwi` channel on `Matrix <https://matrix.to/#kiwi:matrix.org>`_.

Fork the upstream repository
----------------------------

1. On GitHub, navigate to: https://github.com/OSInside/kiwi.

2. In the top-right corner of the page, click :command:`Fork`.

Create a local clone of the forked repository
---------------------------------------------

.. code:: shell-session

    $ git clone https://github.com/YOUR-USERNAME/kiwi

    $ git remote add upstream https://github.com/OSInside/kiwi.git

Install Required Operating System Packages
------------------------------------------

{kiwi} requires additional packages at runtime that are not
provided by `pip`. Those will be pulled in by installing
the following package:

* kiwi-systemdeps

The package is provided on the Open Build Service in the
`Virtualization:Appliances:Builder
<https://download.opensuse.org/repositories/Virtualization:/Appliances:/Builder>`__
project. For manual inspection of the packages
that are pulled in from the above `kiwi-systemdeps` package, please refer
to the `package/python-kiwi-spec-template` spec file from the checked-
out Git repository.

Create a Python Virtual Development Environment
-----------------------------------------------

The following commands initialize and activate a development
environment for Python 3:

.. code:: shell-session

   $ poetry install --all-extras

The command above automatically creates the application script
called :command:`kiwi-ng`, which allows you to run {kiwi} from the
Python sources inside the virtual environment using Poetry:

.. code:: shell-session

    $ poetry run kiwi-ng --help

Running the Unit Tests
----------------------

Before submitting your changes via a pull request, ensure that all tests
pass and that the code has the required test coverage via the command:

.. code:: shell-session

    $ make check
    $ make test

Create a Branch for each Feature or Bugfix
------------------------------------------

Code changes should be done in an extra Git branch. This allows for
creating GitHub pull requests in a clean way. See also: `Collaborating with
issues and pull requests
<https://help.github.com/en/categories/collaborating-with-issues-and-pull-requests>`_.

.. code:: shell-session

    $ git checkout -b my-topic-branch

Make and commit your changes.

.. note::

    You can make multiple commits, which is generally useful to
    give your changes a clear structure and to allow us to better
    review your work.

.. note::

    Your work is important and must be signed to ensure the integrity of
    the repository and the code. Thus, we recommend setting up a signing key
    as documented in :ref:`Signing_Git_Patches`.

.. code:: shell-session

    $ git commit -S -a

Once everything is done, push your local branch to your forked repository and
create a pull request into the upstream repository.

.. code:: shell-session

    $ git push origin my-topic-branch

Thank you much for contributing to {kiwi}. Your time and work effort are very
much appreciated!

AI policy
---------

{kiwi} has a policy of human-centric development. Only humans can be authors
and are ultimately responsible for the quality of their contributions.
That said, assisted development with "AI" tools (particularly contemporary
LLM-based tools) is permitted with the restriction that such usage is clearly
labeled with the `Assisted-by:` trailer in the pull request and commit message.
The format of the trailer is as such: `Assisted-by: <Tool>:<model-id>`.

Examples of such trailers:

+----------------+---------------------------------------+
|      Tool      |                Trailer                |
+================+=======================================+
| Claude         | `Assisted-by: Claude:claude-opus-4-6` |
| Gemini         | `Assisted-by: Gemini:gemini-2.5-pro`  |
| GitHub Copilot | `Assisted-by: Copilot:gpt-4o`         |
| ChatGPT        | `Assisted-by: ChatGPT:gpt-4o`         |
+----------------+---------------------------------------+

The trailers are required for the following scenarios:

- AI wrote or generated code that ended up in the commit
- AI substantially modified or refactored existing code
- AI generated an implementation that the contributor then adapted

It is **not** required for the following scenarios:

- AI was used for **research only** - asking questions, understanding APIs,
  exploring approaches - but the human wrote all the code
- AI performed purely **mechanical tasks** like formatting, running commands,
  or committing

All submitted changes must be at least self-reviewed and all other guidelines
continue to apply (follow code style, pass tests, make tests for new functionality,
etc.)

Intentional obfuscation of AI tooling usage is grounds for rejection and may result
in being blocked from future contributions.

Coding Style
------------

{kiwi} follows the general PEP8 guidelines with the following exceptions:

- We do not use free functions at all. Even utility functions must be part
  of a class but should be either prefixed with the `@classmethod` or
  `@staticmethod` decorators (whichever is more appropriate).

- Do not set module and class-level variables; put these into the class's
  `__init__` method.

Documentation
~~~~~~~~~~~~~

{kiwi} uses `Sphinx <https://www.sphinx-doc.org/en/master/>`_ for the API,
user documentation, and man pages.

.. code:: shell-session

    $ make docs

API documentation is created from type hints. Even though python is
not a strong typed language we value if all parameters and return
values uses proper type hints.

.. code:: python

   class Example:
       """
       **Example class**

       This class has a single responsibility and provides...
       """
       def __init__(self, names: List[str], set_names: bool = False):
           """
           Initialize instance of Example with a list of some
           example names. If param_w_default is set to True the
           instance is initialized with an empty name list
           """
           self.some_names = names if set_names else []

       def is_retired(self, age: int = 0) -> bool:
           """
           A method that implements...
           """
           return len(age) > 70

Try to stick to the following guidelines when documenting source code:

- Classes should be documented directly in their main docstring and not in
  `__init__`.

- Document function parameters unless their name makes it clear

- Only public methods should be documented; private methods don't have to be,
  unless they are complex and it is not easy to grasp what they do (which
  should be avoided anyway).

Please also document any user-facing changes that you are implementing
(e.g., adding a new build type) in the user documentation, which can be
found in `doc/source`.

Additional Information
----------------------

The following sections provide further information about the repository
integrity, version, package, and documentation management.

.. _Signing_Git_Patches:

Signing Git Patches
~~~~~~~~~~~~~~~~~~~

To ensure the integrity of the repository and the codebase, patches sent
for inclusion should be signed with a GPG key.

To prepare Git to sign commits, follow these instructions:

#. Create a key suitable for signing (it is not recommended to use
   existing keys to not mix it with your email environment):

   .. code:: shell-session

    $ gpg2 --expert --full-gen-key

#. Either choose an RSA key for signing (option `(4)`) or an ECC key for
   signing (option `(10)`). For an RSA key, choose a key size of 4096 bits,
   and for an ECC key, choose Curve 25519 (option `(1)`). Enter a reasonable
   validity period (we recommend 2 to 5 years). Complete the key generation
   by entering your name and email address.

#. Add the key ID to your git configuration by running the following
   :command:`git config` commands:

   .. code:: shell-session

      $ git config --local user.signingkey $YOUR_SIGN_KEY_ID
      $ git config --local commit.gpgSign true

   Omitting the `--local` flag will make these settings global for all
   repositories (they will be added to :file:`~/.gitconfig`). You can find
   your signkey's ID via:

   .. code:: shell-session

      $ gpg2 --list-keys --keyid-format long $YOUR_EMAIL
      pub   rsa4096/AABBCCDDEEFF0011 2019-04-26 [S] [expires: 2021-04-16]
      AAAAAAAAAAAAAAAAAAAAAABBBBBBBBBBBBBBBBBB
      uid                 [ultimate] YOU <$YOUR_EMAIL>

   The key's ID in this case would be `AABBCCDDEEFF0011`. Note that your
   sign key will have only a `[S]` after the creation date, not a `[SC]`
   (then you are looking at your ordinary GPG key that can also encrypt).

Bumping the Version
~~~~~~~~~~~~~~~~~~~

The {kiwi} project follows the `Semantic Versioning <https://semver.org>`_
scheme. We use the :command:`bumpversion` tool for consistent versioning.

Follow these instructions to bump the major, minor, or patch part of the
{kiwi} version. Ensure that your repository is clean (i.e., no modified and
unknown files exist) before running :command:`bumpversion`.

*  For backward-compatible bug fixes:

   .. code:: shell-session

    $ bumpversion patch

*  For additional functionality in a backward-compatible manner. When
   changed, the patch level is reset to zero:

   .. code:: shell-session

    $ bumpversion minor

*  For incompatible API changes. When changed, the patch and minor
   levels are reset to zero:

   .. code:: shell-session

    $ bumpversion major

Creating an RPM Package
~~~~~~~~~~~~~~~~~~~~~~~

We provide a template for an RPM spec file in
:file:`package/python-kiwi-spec-template` alongside a `rpmlint`
configuration file and an automatically updated
:file:`python-kiwi.changes`.

To create the necessary files to build an RPM package via `rpmbuild`, run:

.. code:: shell-session

   $ make build

The sources are collected in the :file:`dist/` directory. These can be
directly built with :command:`rpmbuild`, :command:`fedpkg`, or submitted
to the Open Build Service using :command:`osc`.
