Files
profile/doc/CONTRIBUTING.md
T

8.3 KiB

Contributing to profile

Thank you for your interest in contributing. This document explains how to get set up, what the conventions are, and how to submit work.


1. Before you start

  • Check the to-do list to see if your idea is already planned.
  • Check the issue tracker to avoid duplicate work.
  • For significant changes, open an issue or contact the maintainer before writing code — alignment on design saves everyone time.

2. Getting the source

A Git clone is mandatory for contributions:

git clone https://git.geoffray-levasseur.org/fatalerrors/profile.git
cd profile

Always work from the latest commit on master (or the branch you intend to target). Stale forks cause avoidable merge conflicts.


3. Branch policy

Branch Purpose
master Main development branch — new features and enhancements go here
<version> (e.g. 3.x) Maintenance branch for a released version — bugfixes backported from master

New functionality must always target master.

Bugfixes must target the branch where the bug was introduced:

  • If the bug exists in a released version, open the fix against that version's maintenance branch first, then cherry-pick onto master.
  • If the bug is only in master (unreleased), fix it directly on master.
  • During a release-candidate cycle, bugfixes go on the x.* branch and are merged back into master before the final release.

Do not add new features to a maintenance branch.


4. Versioning scheme

Versions follow the format MAJOR.MINOR.PATCH where the MINOR number conveys the development stage of the next major release:

Minor range Stage Rules
x.90.y Alpha toward x+1 Stays on master. Development is open: new features are welcome, regressions are acceptable.
x.95.y Beta toward x+1 The x+1.* maintenance branch is created at this point. No regression unless absolutely necessary; new features still allowed.
x.99.y RC toward x+1 Bugfixes only. No new features. No regression allowed. Becomes x+1.0.0 when stable.

Examples: 3.90.1 is the first alpha toward 4.0, 3.99.2 is the second release candidate for 4.0.

The PATCH number increments freely within a stage. A bump in MINOR (e.g. 90 → 95) always indicates a stage promotion in development phase.

Any experimental version must have it's dedicated branch.


5. Development environment

| --- | --- | --- | | Bash | 4.3 | Namerefs (local -n) required | | shellcheck | any recent | Run before every commit | | git | any | For contributing patches | | bats-core | 1.x | Optional — for running the test suite |

Install shellcheck:

# Debian / Ubuntu
apt-get install shellcheck

# Fedora / RHEL
dnf install ShellCheck

# macOS
brew install shellcheck

6. Code style

General rules

  • Bash only — no external interpreters in core modules. Python or Perl is acceptable for completely self-contained, optional utilities that have no dependencies beyond a minimal Debian or CentOS installation.
  • 4-space indentation — no tabs.
  • [[ … ]] for all conditionals — not [ … ].
  • (( … )) for arithmetic — not $(( … )) in conditionals.
  • local for all function-internal variables — avoid polluting the environment. Prefer upper case for global and lowercase for local.
  • printf instead of echo all the time.
  • Never eval — use namerefs (local -n), ${!varname} indirection, or declare -g instead. The only exception is the function generator of build_locale_shortcuts (profile.d/lang.sh): bash cannot create a function whose name is only known at run time otherwise, and the only value injected into it is quoted with printf %q.
  • No hardcoded defaults — wire every configurable value through ${VAR:-default} and document the key in profile.conf and README.md §4.

Function conventions

  • Public functions must be exported: export -f funcname.

  • Every public function must support -h / --help and print usage to stdout, returning 0.

  • Use getopt (not getopts) for option parsing — it handles long options and -- correctly. Rebuild the arguments from its output with the _getopt_split helper (helpers/common.sh), never with eval set -- or xargs:

    PARSED=$(getopt -o hv --long help,verbose -n 'mycmd' -- "$@") || return 1
    local -a _pargs=()
    _getopt_split _pargs "$PARSED"
    set -- "${_pargs[@]}"
    

    To restore traps saved with trap -p, use _trap_restore (no eval either).

  • Follow existing error-return conventions: 0 = success, 1 = usage error, 2 = bad options, 3 = missing dependency, 4+ = runtime failure.

  • Prefix all local helper variables with a short unique prefix (e.g. _taz_) to prevent collisions with caller-scope variables.

Module structure

Every new module should follow this pattern:

#!/usr/bin/env bash
# <copyright block identical to existing modules>

load_conf "<module_name>"

# --- functions ---

export -f my_function

# EOF

Add the load_conf call near the top after any variable declarations.

Helpers

Internal functions (prefixed with _) do not belong to the module file; they live in the helpers/ directory:

  • helpers/common.sh: helpers used by several modules (_getopt_split, _trap_restore, _is_uint, _profile_load_helpers). It is loaded by profile.sh before any module. Put a helper there as soon as a second module needs it, instead of copying it.

  • helpers/<module name>.sh: helpers private to profile.d/<module name>.sh (for instance helpers/rain.sh for profile.d/rain.sh). The file is optional; the module loads it with the two lines below, which also load common.sh when the module is sourced alone:

    declare -F _profile_load_helpers >/dev/null || . "$(dirname -- "${BASH_SOURCE[0]}")/../helpers/common.sh"
    _profile_load_helpers "${BASH_SOURCE[0]}"
    

Helper files use the same copyright block as the modules and are only definitions (no side effect besides the variables the helpers need). Document each helper with a comment (purpose, Usage: line).


7. Configuration keys

When adding a configurable default:

  1. Use ${MY_VAR:-hardcoded_default} in the function body.
  2. Add a commented-out entry with a description to profile.conf.
  3. Document the key in the matching table in README.md §4.2.

8. Theming

New theme files go in profile.d/themes/ with a .theme extension. They are parsed, not executed — do not add shell logic.
See the existing themes and README.md §4.4 for the allowed syntax.


9. Running shellcheck

shellcheck profile.sh profile.d/*.sh helpers/*.sh profile.d/bash-completion/*.sh

The .shellcheckrc at the root of the project makes shellcheck follow the sourced files (external-sources, source-path=SCRIPTDIR). The run must report nothing: all warnings must be resolved before a patch will be accepted. Accepted suppression directives (# shellcheck disable=SCxxxx) require an inline comment explaining why the suppression is necessary.


10. Submitting a contribution

Via Git (preferred)

  1. Contact the maintainer to obtain push access, or fork on the Gitea instance.
  2. Create a branch: git checkout -b feature/my-feature.
  3. Commit with a clear subject line: module: short description (≤ 72 chars).
  4. Push and open a pull request against master.

Via patch

If you do not have push access:

git format-patch origin/master

Send the resulting .patch file(s) to fatalerrors <at> geoffray-levasseur <dot> org.

Commit message format

module: imperative short description

Optional longer explanation of what changed and why. Wrap at 72 characters.
Reference issue numbers if applicable: closes #42.

11. What will be rejected

  • Code requiring packages not in a minimal Debian or CentOS install, unless optionnal.
  • Use of eval, source-based config loading, or other code-injection vectors.
  • Changes that break Bash 4.3 compatibility.
  • Patches without a passing shellcheck run.
  • New functions without --help support.

12. Financial contributions

Contact the maintainer by mail if you wish to make a financial contribution.