1
0
Fork 1
mirror of https://gitlab.postmarketos.org/postmarketOS/pmbootstrap.git synced 2025-07-12 10:59:49 +03:00
pmbootstrap/CONTRIBUTING.md
Newbyte 75bc105605
CONTRIBUTING.md, README.md: Use descriptive links
Markdownlint complained about the links not being descriptive enough for
accesibility purposes, so make them actually describe what we're linking
to.

Alternative to https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/-/merge_requests/2602

Part-of: https://gitlab.postmarketos.org/postmarketOS/pmbootstrap/-/merge_requests/2605
[ci:skip-build]: already built successfully in CI
2025-05-21 23:13:59 +02:00

5.8 KiB

Contributing

pmbootstrap development is being discussed in #postmarketOS-devel.

CI scripts

Use pmbootstrap ci inside your pmbootstrap.git dir, to run all CI scripts locally.

Coding style

A lot of the coding style is enforced by the CI scripts.

Python

  • Use PEP8.
  • Max line length: 80-100 characters (use 80 for comments and most code lines except when 100 makes much more sense; try to keep it consistent with existing code).
  • Use f-strings for any new or modified code, instead of any of the other string formatting methods.
  • pmbootstrap should run on any Linux distribution, so we support all active Python versions.
  • ruff is used to enforce a consistent code style, it is run in CI and can be easily run locally too (see Linting down below).
  • Docstrings below functions are formatted in reST style:
"""
This is a reST style.

:param param1: this is a first param
:param param2: this is a second param
:returns: this is a description of what is returned
:raises keyError: raises an exception
"""

Linting

To avoid having to fight with ruff in CI, it's recommended to enable a pre-commit hook to run it automatically before you commit. This drastically streamlines interacting with ruff.

Enable the git hook with:

ln -sf ../../.ci/hooks/pre-commit .git/hooks/pre-commit

Shell scripts

  • Must be POSIX compliant, so busybox ash can interpret them. (Exception: the local keyword can also be used, to give variables a local scope inside functions).

Markdown

Markdown files are linted with markdownlint-cli, to avoid having to install npm on your host, you can lint the markdown files in this repo with:

pmbootstrap ci markdown

Additionally, most electron based IDEs (e.g. vscode) have markdownlint available as an extension.

Code patterns

The args variable

This contains the arguments passed to pmbootstrap, and some additional data. See pmb/helpers/args.py for details. This is a legacy construct, see #1879.

Executing commands

Use one of the following functions instead of Python's built-in subprocess:

  • pmb.helpers.run.user()
  • pmb.helpers.run.root()
  • pmb.chroot.user()
  • pmb.chroot.root()

These functions call pmb.helpers.run_core.core() internally to write to the log file (that you can read with pmbootstrap log) and timeout when there is no output. A lot of function parameters are passed through to core() as well, see its docstring for a detailed description of what these parameters do.

Using shell syntax

The passed commands do not run inside a shell. If you need to use shell syntax, wrap your command with sh -c and use shutil.quote on the parameters (if they contain untrusted input):

# Does not work, the command does not run in a shell!
pmb.chroot.root(["echo", "test", ">", "/tmp/test"])

# Use this instead (assuming untrusted input for text, dest)
text = "test"
dest = "/tmp/test"
shell_cmd = f"echo {shutil.quote(text)} > {shutil.quote(dest)}"
pmb.chroot.root(["sh", "-c", shell_cmd])

If you need to run many commands in a shell at once, write them into a temporary shell script and execute that with one of the pmb command functions.

Writing files to the chroot

The users in the chroots (root and pmos) have different user IDs than the user of the host system. Therefore we can't just write a file to anywhere in the chroot. Use one of the following methods.

Short files

pmb.chroot.user(["sh", "-c", f"echo {shlex.quote(hostname)}"
                       " > /etc/hostname"], suffix)

Long files

Write to a temp dir first with python code, then move and chown the file.

with open("tmp/somefile", "w") as handle:
    handle.write("Some long file")
    handle.write("with multiple")
    handle.write("lines here")
pmb.chroot.root(["mv", "/tmp/somefile", "/etc/somefile"])
pmb.chroot.root(["chown", "root:root", "/etc/somefile"], suffix)

Manual testing

APKBUILD parser

Besides the python tests, it's a good idea to let the APKBUILD parsing code run over all APKBUILDs that we have in pmaports.git, before and after making changes. This makes it easy to spot regressions.

pmbootstrap apkbuild_parse > /tmp/new
git checkout master
pmbootstrap apkbuild_parse > /tmp/old
colordiff /tmp/old /tmp/new | less -R

Debugging

Tab completion

When tab completion breaks, commands-line pmbootstrap build <TAB> will simply not return the expected list of packages anymore. Exceptions are not printed. To change this behavior and get the exceptions, adjust the eval "$(register-python-argcomplete pmbootstrap)" line in your shell's rc file.

$ register-python-argcomplete3 pmbootstrap

_python_argcomplete() {
    local IFS=$'\013'
    local SUPPRESS_SPACE=0
    if compopt +o nospace 2> /dev/null; then
        SUPPRESS_SPACE=1
    fi
    COMPREPLY=( $(IFS="$IFS" \
                  COMP_LINE="$COMP_LINE" \
                  COMP_POINT="$COMP_POINT" \
                  COMP_TYPE="$COMP_TYPE" \
                  _ARGCOMPLETE_COMP_WORDBREAKS="$COMP_WORDBREAKS" \
                  _ARGCOMPLETE=1 \
                  _ARGCOMPLETE_SUPPRESS_SPACE=$SUPPRESS_SPACE \
                  "$1" 8>&1 9>&2 1>/dev/null 2>/dev/null) )
    if [[ $? != 0 ]]; then
        unset COMPREPLY
    elif [[ $SUPPRESS_SPACE == 1 ]] && [[ "$COMPREPLY" =~ [=/:]$ ]]; then
        compopt -o nospace
    fi
}
complete -o nospace -o default -F _python_argcomplete "pmbootstrap"

Copy the whole output of the command to your shell's rc file instead of the eval line, but remove 1>/dev/null 2>/dev/null. Then it will print exceptions to the shell.