From 1fa5a00436f4622d4f2898e021cc69ec2802fa80 Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 22 Oct 2026 02:08:52 +0000 Subject: [PATCH 1/2] docs: correct packaging guidance for Trixie Debhelper compat, maintainer scripts, runtime dependencies, unit paths, native versions, Standards-Version, the /opt lintian override, the Packagecloud artifact name, and the install URL now match current Debian and the active repos. REPOS.md records the remaining deviations. Co-authored-by: jsz --- GETTING_STARTED.md | 12 +- PACKAGING_EXAMPLE.md | 188 +++++++++++++++++++---------- README.md | 4 +- RELEASE_PROCESS.md | 27 +++-- REPOS.md | 119 +++++++++++++++++-- architecture/FHS.md | 6 +- architecture/PACKAGING.md | 241 +++++++++++++++++++++++++++++++++----- style/DCH.md | 10 +- style/WORKFLOW.md | 8 +- 9 files changed, 499 insertions(+), 116 deletions(-) diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index 9934a57..f850a3d 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -18,9 +18,11 @@ This repository contains the documentation for developers who want to contribute ### 1. Install dependencies ```bash -curl -fSL http://wlanpi.us/install | sudo bash +curl -fSL https://raw.githubusercontent.com/WLAN-Pi/developers/main/install.sh | sudo bash ``` +That URL is `install.sh` in this repository, fetched over HTTPS. + ### 2. Set up git ```bash @@ -129,13 +131,15 @@ REPO_NAME/ ├── debian/ # Debian packaging files │ ├── control # Package metadata and dependencies │ ├── changelog # Version history (managed via dch) +│ ├── copyright # DEP-5 copyright │ ├── rules # Build instructions +│ ├── source/format # 3.0 (native) for first-party packages │ └── ... ├── wlanpi_yourapp/ # Python package source ├── tests/ # Test files ├── pyproject.toml # Project metadata and tool config ├── requirements.in # Direct dependencies (managed via pip-tools) -├── requirements.txt # Pinned dependencies (generated, do not edit by hand) +├── requirements.txt # Pinned runtime dependencies (generated, do not edit by hand) ├── tox.ini # Tox environments for lint, format, test ├── README.md # Project documentation └── LICENSE # BSD-3-Clause License @@ -145,6 +149,10 @@ This is the target, not a guarantee. Some older repos still use `setup.py` instead of `pyproject.toml`, or omit `requirements.in`/`tox.ini`. See [Repository Reference](REPOS.md) for the current state of each repo. +`requirements.txt` is the runtime set that `dh_virtualenv` installs into the +`.deb`. Test tools stay in a `pyproject.toml` extra. See +[Debian Packaging](architecture/PACKAGING.md#runtime-and-test-dependencies). + ## Your first contribution Not sure where to start? Look for issues labeled: diff --git a/PACKAGING_EXAMPLE.md b/PACKAGING_EXAMPLE.md index 6109434..942fbbb 100644 --- a/PACKAGING_EXAMPLE.md +++ b/PACKAGING_EXAMPLE.md @@ -23,21 +23,23 @@ We'll create `wlanpi-hello` - a simple "Hello World" application that demonstrat wlanpi-hello/ ├── debian/ # Debian packaging files │ ├── changelog # Version history -│ ├── compat # Debhelper version │ ├── control # Package metadata │ ├── copyright # License info -│ ├── wlanpi-hello.install # File installation rules │ ├── postinst # Post-install script │ ├── prerm # Pre-remove script -│ └── rules # Build instructions +│ ├── rules # Build instructions +│ ├── source/ +│ │ └── format # 3.0 (native) +│ ├── wlanpi-hello.install # File installation rules +│ ├── wlanpi-hello.lintian-overrides +│ └── wlanpi-hello.service # Installed by dh_installsystemd ├── src/ # Application source │ └── hello/ │ ├── __init__.py +│ ├── __version__.py │ └── main.py ├── config/ # Configuration files │ └── hello.conf -├── systemd/ # Systemd service files -│ └── wlanpi-hello.service ├── tests/ # Test files │ └── test_hello.py ├── pyproject.toml # Python package metadata @@ -53,10 +55,7 @@ wlanpi-hello/ Create the Python application in `src/hello/main.py`: ```python -#!/usr/bin/env python3 -""" -WLAN Pi hello world - A simple example -""" +"""WLAN Pi hello world - A simple example.""" import argparse import logging @@ -108,11 +107,21 @@ if __name__ == "__main__": sys.exit(main()) ``` +Create `src/hello/__version__.py`. The deploy version gate reads this file. +Its `major.minor.patch` is the changelog version: + +```python +"""Package version. Keep major.minor.patch identical to debian/changelog.""" +__version__ = "1.0.0" +``` + Create `src/hello/__init__.py`: ```python """WLAN Pi Hello package""" -__version__ = "1.0.0" +from hello.__version__ import __version__ + +__all__ = ["__version__"] ``` ## Step 2: Python package setup @@ -160,15 +169,14 @@ python_functions = ["test_*"] addopts = "-v --tb=short" ``` -Create `requirements.txt`: +Create `requirements.txt` for runtime dependencies only. `dh_virtualenv` +runs `pip install -r requirements.txt` whenever the file exists, so a test +tool listed here is unpacked into the `.deb`. This example has no runtime +dependencies. Pytest stays in the `test` extra above. -``` -# Production dependencies -# Add any non-standard library packages here - -# Development dependencies -pytest>=7.0.0 -pytest-cov>=4.0.0 +```text +# Runtime Python dependencies for dh_virtualenv. +# Keep test tools out of this file; they belong in the pyproject test extra. ``` ## Step 3: Configuration @@ -190,7 +198,12 @@ level = INFO ## Step 4: Systemd service -Create `systemd/wlanpi-hello.service`: +Create `debian/wlanpi-hello.service`. On Trixie, debhelper 13.24's +`dh_installsystemd` installs a unit of that name into +`/usr/lib/systemd/system` and wires enable/start/stop into `#DEBHELPER#`. +Leave it out of the `.install` file. Bookworm's debhelper 13.11.4 still +writes `lib/systemd/system`; see +[debian/rules](architecture/PACKAGING.md#debianrules). ```ini [Unit] @@ -225,7 +238,7 @@ Section: contrib/python Priority: optional Maintainer: Your Name Build-Depends: debhelper-compat (= 13), python3, python3-venv, dh-virtualenv (>= 1.0) -Standards-Version: 4.7.0 +Standards-Version: 4.7.2 Homepage: https://github.com/wlan-pi/wlanpi-hello Package: wlanpi-hello @@ -252,12 +265,25 @@ wlanpi-hello (1.0.0) unstable; urgency=medium -- Your Name Mon, 01 Jan 2024 00:00:00 +0000 ``` -### debian/compat +Do not add `debian/compat`. The compat level is the `debhelper-compat (= 13)` +build dependency above. Shipping both makes `dh` fail: +```text +dh: error: debhelper compat level specified both in debian/compat and in debian/control ``` -13 + +### debian/source/format + +```text +3.0 (native) ``` +This package is native: `debian/` lives in the same git tree as the +application, and the changelog version `1.0.0` has no Debian revision. +The built file is `wlanpi-hello_1.0.0_.deb`. Details and the quilt +exception are in +[Source Format and Version](architecture/PACKAGING.md#source-format-and-version). + ### debian/copyright ``` @@ -304,17 +330,38 @@ License: BSD-3-Clause export DH_VIRTUALENV_INSTALL_ROOT=/opt +PACKAGE=$(shell dh_listpackages) +DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE) + %: dh $@ --with python-virtualenv override_dh_virtualenv: dh_virtualenv \ --python python3 \ - --install-suffix wlanpi-hello - -override_dh_installsystemd: - dh_installsystemd --name=wlanpi-hello -``` + --builtin-venv \ + --install-suffix wlanpi-hello \ + --extra-pip-arg "--no-compile" + # Bytecode and pip are not needed at runtime and lintian rejects them. + find $(DH_VENV_DIR) -type d -name '__pycache__' -print0 | xargs -0r rm -rf + find $(DH_VENV_DIR) -name '*.pyc' -delete + rm -rf $(DH_VENV_DIR)/bin/pip* \ + $(DH_VENV_DIR)/bin/wheel \ + $(DH_VENV_DIR)/.gitignore \ + $(DH_VENV_DIR)/lib/python*/site-packages/pip \ + $(DH_VENV_DIR)/lib/python*/site-packages/pip-*.dist-info \ + $(DH_VENV_DIR)/lib/python*/site-packages/setuptools \ + $(DH_VENV_DIR)/lib/python*/site-packages/setuptools-*.dist-info \ + $(DH_VENV_DIR)/lib/python*/site-packages/wheel \ + $(DH_VENV_DIR)/lib/python*/site-packages/wheel-*.dist-info +``` + +`--builtin-venv` uses the stdlib `venv` module. On Trixie, `dh-virtualenv` +depends on `virtualenv | python3-virtualenv | python3-venv`. This example +build-depends on `python3-venv`. Without `--builtin-venv`, `dh_virtualenv` +executes `virtualenv` and fails with `FileNotFoundError` when that binary +is not installed. There is no `override_dh_installsystemd`: the default +helper picks up `debian/wlanpi-hello.service`. Make it executable: @@ -326,29 +373,36 @@ chmod +x debian/rules ``` config/hello.conf etc/wlanpi-hello/ -systemd/wlanpi-hello.service lib/systemd/system/ +``` + +### debian/wlanpi-hello.lintian-overrides + +`dir-or-file-in-opt` is an error. The override and the reason are in +[Lintian and /opt](architecture/PACKAGING.md#lintian-and-opt). + +```text +# dh_virtualenv installs this add-on application and its dependencies under +# /opt/wlanpi-hello. /opt is reserved for add-on software; these packages +# are not candidates for the Debian archive. +wlanpi-hello: dir-or-file-in-opt ``` ### debian/postinst ```bash -#!/bin/bash +#!/bin/sh set -e case "$1" in configure) - # Create log directory + if ! getent group wlanpi >/dev/null; then + groupadd --system wlanpi + fi + if ! id -u wlanpi >/dev/null 2>&1; then + useradd --system --gid wlanpi --shell /usr/sbin/nologin --comment "WLAN Pi service" wlanpi + fi mkdir -p /var/log/wlanpi-hello chown wlanpi:wlanpi /var/log/wlanpi-hello - - # Create user if doesn't exist - if ! id -u wlanpi > /dev/null 2>&1; then - useradd -r -s /bin/false wlanpi - fi - - # Enable and start service - deb-systemd-helper enable wlanpi-hello.service - deb-systemd-helper start wlanpi-hello.service ;; esac @@ -357,6 +411,13 @@ esac exit 0 ``` +Create the user before `chown`. The other order fails the install with +`chown: invalid user: 'wlanpi:wlanpi'`. `#DEBHELPER#` comes after that, +because it expands to the `dh_installsystemd` block that starts the +service. `deb-systemd-helper` has no `start` or `stop` subcommand. From a +maintainer script those arguments exit 0 and do not start or stop anything. +`deb-systemd-invoke start` and `stop` are what the helper inserts. + Make it executable: ```bash @@ -366,21 +427,18 @@ chmod +x debian/postinst ### debian/prerm ```bash -#!/bin/bash +#!/bin/sh set -e -case "$1" in - remove|upgrade|deconfigure) - deb-systemd-helper stop wlanpi-hello.service - deb-systemd-helper disable wlanpi-hello.service - ;; -esac - #DEBHELPER# exit 0 ``` +`#DEBHELPER#` is required in every maintainer script you ship. It is where +`dh_installsystemd` inserts `deb-systemd-invoke stop` for `prerm`. Use LF +line endings. A CRLF shebang fails with `cannot execute: required file not found`. + Make it executable: ```bash @@ -397,8 +455,6 @@ Create `tests/test_hello.py`: import sys from unittest.mock import patch -import pytest - from hello.main import main @@ -442,11 +498,9 @@ source venv/bin/activate Run tests with pytest: ```bash -# Install test dependencies -pip install -r requirements.txt - -# Install package editable -pip install -e . +# Install the package and its test extra. requirements.txt is the runtime +# set shipped in the .deb. +pip install -e ".[test]" # Run tests pytest @@ -473,7 +527,10 @@ dpkg-buildpackage -us -uc -b ls ../*.deb ``` -You should see `wlanpi-hello_1.0.0-1_arm64.deb` in the parent directory. +You should see `wlanpi-hello_1.0.0_.deb` in the parent directory. +The version is the changelog version `1.0.0`, with no `-1`. On a WLAN Pi +or the arm64 CI runner, `` is `arm64`. An amd64 build machine +produces `wlanpi-hello_1.0.0_amd64.deb`. ## Step 8: GitHub actions workflows @@ -512,6 +569,11 @@ example `test-python-package.yml`, `python-lint-police.yml`, and `python-format-police.yml`). Trigger paths differ between repos: some watch only `debian/changelog`, others ignore docs-only changes. +Call these reusable workflows. Do not copy the `sbuild` or `package_cloud` +steps into the repository. The `~gha` timestamp some older notes mention is +part of the Actions artifact name inside `sbuild-pkg.yml`, not part of the +`.deb` version. See [Release Process](RELEASE_PROCESS.md#4-deploy-to-packagecloud). + Create `.github/workflows/deploy.yml`: ```yaml @@ -567,7 +629,7 @@ After installation, the package will create: /var/log/wlanpi-hello/ # Log files └── hello.log # Application logs -/lib/systemd/system/ # Systemd service +/usr/lib/systemd/system/ # Systemd service └── wlanpi-hello.service ``` @@ -577,13 +639,13 @@ Install and test on actual hardware: ```bash # Copy the .deb file to your WLAN Pi -scp ../wlanpi-hello_1.0.0-1_arm64.deb wlanpi@your-device-ip:/tmp/ +scp ../wlanpi-hello_1.0.0_arm64.deb wlanpi@your-device-ip:/tmp/ # SSH into the device ssh wlanpi@your-device-ip # Install the package -sudo dpkg -i /tmp/wlanpi-hello_1.0.0-1_arm64.deb +sudo dpkg -i /tmp/wlanpi-hello_1.0.0_arm64.deb # Check if service is running sudo systemctl status wlanpi-hello @@ -641,10 +703,16 @@ Common causes: Check package for issues: ```bash -lintian ../wlanpi-hello_1.0.0-1_arm64.deb +lintian ../wlanpi-hello_1.0.0_*.deb ``` -Many `/opt` related warnings are expected and can be overridden. +`dir-or-file-in-opt` is an error. The override in +`debian/wlanpi-hello.lintian-overrides` suppresses it. With that override, +and with bytecode and pip removed in `debian/rules`, a Trixie lintian run +of this example reports one warning: +`unusual-interpreter /opt/wlanpi-hello/bin/python` on the console script. +That warning is expected. Installing the unit to `/lib/systemd/system` +instead reports `aliased-location`, also an error. ## Next steps diff --git a/README.md b/README.md index e3cd5c0..346f67a 100644 --- a/README.md +++ b/README.md @@ -16,9 +16,11 @@ A collection of documentation for WLAN Pi developers and contributors. To set up a WLAN Pi device for development (requires `curl` and `sudo`): ```bash -curl -vfSL http://wlanpi.us/install | sudo bash +curl -vfSL https://raw.githubusercontent.com/WLAN-Pi/developers/main/install.sh | sudo bash ``` +That URL is `install.sh` in this repository, fetched over HTTPS. + ## Documentation ### For Contributors diff --git a/RELEASE_PROCESS.md b/RELEASE_PROCESS.md index e887c00..7539ca0 100644 --- a/RELEASE_PROCESS.md +++ b/RELEASE_PROCESS.md @@ -67,8 +67,10 @@ Example: dch -v 1.2.3 "Release version 1.2.3" ``` -For Python packages, set the same `major.minor.patch` in `__version__.py` -(see [Version gate](#version-gate)). +For a first-party package the changelog version is `major.minor.patch` with +no Debian revision (`1.2.3`, not `1.2.3-1`). Set the same value in +`__version__.py` (see [Version gate](#version-gate) and +[Source Format and Version](architecture/PACKAGING.md#source-format-and-version)). Commit these changes: ```bash @@ -86,18 +88,29 @@ merge** once CI passes. The `debian/changelog` change reaching `main` triggers the deployment workflow. Pushing the version tag does not trigger it. -1. Package is built automatically with `sbuild` -2. Uploaded to [wlanpi/dev](https://packagecloud.io/wlanpi/dev) with a - `~gha` build-metadata suffix on the version +1. Package is built automatically with `sbuild`. The `.deb` version is the + changelog version. +2. Uploaded to [wlanpi/dev](https://packagecloud.io/wlanpi/dev). Packagecloud + receives that `.deb` unchanged. 3. Test the package 4. Once verified, promote from `dev` to `main` in Packagecloud +The `~gha` suffix is only on the GitHub Actions artifact +name, not on the package version. `sbuild-pkg` and `sbuild-deploy-pkg` set +the artifact name to +`_~ghat+_` +(the timestamp is `date -u +'%Y%m%dt%H%M%S'`). The file inside the artifact, +and the file pushed to Packagecloud, keep the changelog version. For +changelog `1.2.3` the Packagecloud filename is `wlanpi-yourapp_1.2.3_arm64.deb`. + ### 5. Tag the release If the repo's deploy workflow calls `tag-release` from [gh-workflows](https://github.com/WLAN-Pi/gh-workflows), skip this step. After -the upload, CI tags the deployed commit as `v` without the Debian -revision, for example `v1.2.3` for `1.2.3-1`. +the upload, CI tags the deployed commit as `v`. For a native +package `1.2.3` the tag is `v1.2.3`. `tag-release` strips an epoch and a +Debian revision if one is still present, so a quilt version `1.2.3-1` is +also tagged `v1.2.3`. Otherwise, after the deploy succeeds, tag the merge commit on `main` with an annotated tag: diff --git a/REPOS.md b/REPOS.md index 0c9e0ee..468a619 100644 --- a/REPOS.md +++ b/REPOS.md @@ -41,8 +41,9 @@ Target conventions live in [Contributing](CONTRIBUTING.md), | Repository | Default branch | Branches | Packaging | Notes | |------------|----------------|----------|-----------|-------| -| `wlanpi-kernel` | `7.2-trixie` | per-version, e.g. `6.12-bookworm`, `7.2-trixie` | kernel `.deb` | `AGENTS.md`; dual-kernel support | +| `wlanpi-kernel` | `7.3-trixie` | per-version, e.g. `6.12-bookworm`, `7.2-trixie`, `7.3-trixie` | kernel `.deb` | `AGENTS.md`; dual-kernel support | | `wlanpi-misc-packages` | `main` | `main` | multi-package repo | third-party package rebuilds | +| `pi-gen-bookworm` | `bookworm` | `bookworm` | OS image (pi-gen) | no Dependabot, no `pr-size.yml`; Slack action on `@master` | ## Deviations from target @@ -51,10 +52,82 @@ issues are tracked against each repository. ### Packaging -- `wlanpi-grafana` (10), `wlanpi-profiler` (11), `wlanpi-webui` (12): still - ship a legacy `debian/compat` file with `debhelper (>= 11)`; move to - `debhelper-compat (= 13)`. `wlanpi-grafana` also needs its `dh_installinit` - and `dh_systemd_start` overrides replaced with `dh_installsystemd`. +`debian/compat` is still on these active repos. The number is the level in +the file. Each of them build-depends on `debhelper (>= N)`, not on +`debhelper-compat`. Archived `wlanpi-chat-bot` also has compat 12. + +| Repository | compat | +|------------|--------| +| `wlanpi-bridge` | 10 | +| `wlanpi-ctx` | 11 | +| `wlanpi-grafana` | 10 | +| `wlanpi-librespeed-cli` | 12 | +| `wlanpi-profiler` | 11 | +| `wlanpi-server` | 10 | +| `wlanpi-webui` | 12 | +| `wlanpi-wconsole` | 10 | +| `wlanpi-wipry` | 10 | + +`wlanpi-grafana` also overrides `dh_installinit` and `dh_systemd_start` +instead of `dh_installsystemd`. + +Source format and changelog version, against +[Source Format and Version](architecture/PACKAGING.md#source-format-and-version). +Checked on each repo's default branch. + +Already `3.0 (native)` with no Debian revision: `wlanpi-desktop` (1.0.5), +`wlanpi-misc-firmware` (1.0.20), `wlanpi-shell-config` (1.1.5), +`wlanpi-usb-ethernet` (1.5.4). + +`debian/source/format` is `1.0` and the changelog version has a revision. +`dpkg-source` warns and still builds a native tarball; lintian 2.122 reports +`malformed-debian-changelog-version` (error) on the source package. The next +upload should move to `3.0 (native)` at a higher upstream version (`1.2.3-1` +sorts above `1.2.3`): + +- `wlanpi-core` `2.4.10-1` +- `wlanpi-ctx` `1.0.2-2` +- `wlanpi-fpms` `1.4.25-1` +- `wlanpi-hwtest` `1.2.0-1` +- `wlanpi-mcp` `0.7.0-1` +- `wlanpi-profiler` `2.1.8-1` +- `wlanpi-webui` `1.21.18-1` + +No `debian/source/format` (lintian warning `missing-debian-source-format`), +and the version has no revision, so it is implicitly native: `wlanpi-bluetooth`, +`wlanpi-bridge`, `wlanpi-common`, `wlanpi-grafana`, `wlanpi-hotspot`, +`wlanpi-server`, `wlanpi-wconsole`, `wlanpi-wipry`. + +No `debian/source/format`, and the version has a revision (same lintian error +once the source package is checked): + +- `wlanpi-librespeed-cli` `1.0.14-1wlanpi1` wraps a third-party binary. The + matching format is `3.0 (quilt)` with an orig tarball, not native. +- `wlanpi-persistent-identity` `1.1.0-1` is first-party. The next upload + should be `3.0 (native)` at a higher upstream version. + +`debian/copyright` is missing on `wlanpi-bluetooth`, `wlanpi-bridge`, +`wlanpi-common`, `wlanpi-grafana`, `wlanpi-hotspot`, `wlanpi-mcp`, +`wlanpi-persistent-identity`, `wlanpi-server`, `wlanpi-webui`, +`wlanpi-wconsole`, and `wlanpi-wipry`. + +`wlanpi-core`'s `debian/wlanpi-core.install` still installs units into +`/lib/systemd/system`. On Trixie that is the `aliased-location` error. +Other active packages either let `dh_installsystemd` place +`debian/*.service` (debhelper 13.24 installs to `/usr/lib/systemd/system`) +or already name `usr/lib/systemd/system` in the `.install` file +(`wlanpi-mcp`, the `wlanpi-common` irtt drop-in). + +`Standards-Version` is not `4.7.2` on these default branches: + +| Value | Repositories | +|-------|----------------| +| `4.7.0` | `wlanpi-bluetooth`, `wlanpi-common`, `wlanpi-core`, `wlanpi-hotspot`, `wlanpi-hwtest`, `wlanpi-mcp`, `wlanpi-misc-firmware`, `wlanpi-webui` | +| `4.6.2` | `wlanpi-persistent-identity`, `wlanpi-shell-config`, `wlanpi-usb-ethernet` | +| `4.6.0` | `wlanpi-ctx`, `wlanpi-desktop`, `wlanpi-fpms`, `wlanpi-grafana`, `wlanpi-profiler` | +| `4.3.0` | `wlanpi-librespeed-cli` | +| `3.9.8` | `wlanpi-bridge`, `wlanpi-server`, `wlanpi-wconsole` | +| absent | `wlanpi-wipry` | ### CI @@ -64,17 +137,39 @@ issues are tracked against each repository. `wlanpi-bridge`, `wlanpi-ctx`, `wlanpi-persistent-identity`, `wlanpi-server`, `wlanpi-wconsole`, and `wlanpi-wipry`. Their shell scripts may need shellcheck fixes when it is added. -- Workflow security ([Style/Workflow](style/WORKFLOW.md#workflow-security)): - every active repo pins third-party actions to SHAs and has a Dependabot - `github-actions` entry. Old branches such as `debian/bullseye` still use - `@master` and have no `permissions:` block; under the read-only token - default, their Slack notification step may fail. +- Workflow security is the policy in + [Style/Workflow](style/WORKFLOW.md#workflow-security) (SHA-pin third-party + actions, and a Dependabot `github-actions` entry). It is not yet true of + every active repo. On the default branches checked here, third-party + actions are SHA-pinned and `.github/dependabot.yml` exists, except + `pi-gen-bookworm`: `Gamesight/slack-workflow-status@master` and + `raven-actions/actionlint@v2`, no Dependabot config. + `debian/bullseye` on `wlanpi-common` still uses + `Gamesight/slack-workflow-status@master`, has no `permissions:` block in + its build workflow, and has no `dependabot.yml`. Under the read-only + token default, that Slack notification step may fail. - `wlanpi-core`: the Dependabot `pip` job has failed on every run since at least 2026-08-31 (`dependency_file_not_resolvable`). - PR size check (`pr-size.yml`, calling the reusable - `WLAN-Pi/gh-workflows/.github/workflows/pr-size.yml`): rolled out to every - active, non-fork WLAN-Pi repo. Copy + `WLAN-Pi/gh-workflows/.github/workflows/pr-size.yml`): present on the + active non-fork application, kernel, and `wlanpi-misc-packages` repos, and + on `developers`. `pi-gen-bookworm` does not have it. `pi-gen` is a fork of + `RPi-Distro/pi-gen` and also does not have it. Copy `developers/.github/workflows/pr-size.yml` into new repos unchanged. +- Build and deploy workflows. The target is the reusable `sbuild-pkg` / + `sbuild-deploy-pkg` workflows. These default branches call them: + `wlanpi-core`, `wlanpi-ctx`, `wlanpi-hwtest`, `wlanpi-mcp`, + `wlanpi-profiler`. These still inline `wlan-pi/sbuild-debian-package` and + the `package_cloud` push: `wlanpi-bluetooth`, `wlanpi-bridge`, + `wlanpi-common`, `wlanpi-desktop`, `wlanpi-fpms`, `wlanpi-grafana`, + `wlanpi-hotspot`, `wlanpi-librespeed-cli`, `wlanpi-misc-firmware` (the + action is pinned to a SHA), `wlanpi-server`, `wlanpi-shell-config`, + `wlanpi-wconsole`, `wlanpi-webui`, `wlanpi-wipry`, and the deploy workflow + of `wlanpi-persistent-identity` and `wlanpi-usb-ethernet`. + `wlanpi-persistent-identity` and `wlanpi-usb-ethernet` build with an inline + `dpkg-buildpackage` step rather than `sbuild-pkg`. Image and kernel repos + (`pi-gen-bookworm`, `pi-gen`, `wlanpi-kernel`, `wlanpi-misc-packages`) are + not application `.deb` builds and do not use those two workflows. - `wlanpi-app`: `lint.yml`'s Slack notification job fails. The repo is private and the org is on the free plan, so it cannot read the org `SLACK_WEBHOOK_URL` secret; it needs a repo-level one. shellcheck and diff --git a/architecture/FHS.md b/architecture/FHS.md index fc9fbe4..983a9dc 100644 --- a/architecture/FHS.md +++ b/architecture/FHS.md @@ -39,7 +39,7 @@ Applications are installed in `/opt/wlanpi-{app}`. This includes: - Simplifies complete application removal - Consistent with "add-on software" definition in FHS 3.0 -**Note:** Using `/opt` will cause `dir-or-file-in-etc-opt` lintian warnings. These are expected and acceptable for our use case. +**Note:** Installing under `/opt` makes lintian report `dir-or-file-in-opt`, and that tag is an error. WLAN Pi packages are add-on software, not candidates for the Debian archive, so each package ships a `debian/.lintian-overrides` entry with that justification. The override is described in [Packaging](PACKAGING.md#lintian-and-opt). `dir-or-file-in-etc-opt` is a different error, for files under `/etc/opt`, and these packages do not install there. ### /etc/wlanpi-{app} @@ -65,7 +65,7 @@ Packages also install into shared locations when they integrate with system services. Use the narrowest path that works and prefer a drop-in or an app-scoped directory over editing another package's files: -- `/lib/systemd/system/` for unit files +- `/usr/lib/systemd/system/` for unit files. On Trixie, `/lib` is a symlink to `/usr/lib`, and lintian reports `aliased-location` (an error) for a unit installed through that alias. `dh_installsystemd` in debhelper 13.24 installs units into `/usr/lib/systemd/system`. Debhelper 13.11.4 on Bookworm still installs those units into `lib/systemd/system`, and Bookworm lintian 2.116.3 accepts that path and `usr/lib/systemd/system`. A destination written in a `.install` file is `usr/lib/systemd/system/` on both suites. `/etc/systemd/system/` remains the place for local administrator overrides, not for files shipped by the package. - `/etc/systemd/system/.service.d/` for drop-ins to another package's units - `/etc/sudoers.d/` for sudoers fragments - `/etc/profile.d/` for login shell snippets @@ -98,7 +98,7 @@ For a package named `wlanpi-example`: /var/lib/wlanpi-example/ # Variable data └── data.db -/lib/systemd/system/ # Service files +/usr/lib/systemd/system/ # Service files └── wlanpi-example.service ``` diff --git a/architecture/PACKAGING.md b/architecture/PACKAGING.md index 791b99a..6660f7d 100644 --- a/architecture/PACKAGING.md +++ b/architecture/PACKAGING.md @@ -31,18 +31,78 @@ project-name/ │ ├── control # Package metadata (debhelper-compat level lives here) │ ├── copyright # License information │ ├── rules # Build instructions +│ ├── source/format # 3.0 (native) or 3.0 (quilt) +│ ├── .lintian-overrides │ └── *.{install,postinst,prerm,...} # Additional control files ├── src/ or {app}/ # Application source code ├── tests/ # Test files -├── requirements.txt # Python dependencies (if applicable) +├── requirements.txt # Runtime Python dependencies, when the package has any ├── pyproject.toml # Python package metadata (if applicable) └── README.md # Project documentation ``` -Modern packages declare the debhelper compatibility level in -`debian/control` via `debhelper-compat (= 13)` and do not ship a -`debian/compat` file. A `debian/compat` file is the legacy style and is only -kept in older packages. +Declare the debhelper compatibility level once, in `debian/control`, as +`debhelper-compat (= 13)`. Do not ship `debian/compat`. Debhelper 13.24 +errors if both are present (`debhelper compat level specified both in +debian/compat and in debian/control`) and `dh clean` fails. A compat level +of 13 in `debian/compat` alone is deprecated. Repos that still have the +file are listed in [Repository Reference](../REPOS.md#packaging). + +Every package has `debian/copyright` (DEP-5) and `debian/source/format`. +Maintainer scripts that you write yourself include a `#DEBHELPER#` line. +`debian/rules` and maintainer scripts use LF line endings. A CRLF shebang +does not execute (`cannot execute: required file not found`, because the +kernel looks up `/bin/sh` with a trailing CR). + +## Source Format and Version + +First-party WLAN Pi packages keep `debian/` in the same git tree as the +application. That is a native package: + +- `debian/source/format` contains `3.0 (native)`. +- The changelog version is `major.minor.patch` with no Debian revision: + `wlanpi-yourapp (1.2.3) unstable; urgency=medium`. +- The built filename is `wlanpi-yourapp_1.2.3_.deb`. `dpkg` uses the + changelog version as-is. It does not append `-1`. +- For a Python package, `__version__.py` is that same `major.minor.patch`. + `pyproject.toml`'s `version` matches it. The deploy gate + `check-py-deb-pkg-versions-match` compares `major.minor.patch` after + stripping an epoch and a `-revision`, so bump the changelog and + `__version__.py` together. + +`dpkg-buildpackage -b` does not enforce this. The CI build runs +`dpkg-source -b` (see +[sbuild-debian-package](https://github.com/WLAN-Pi/sbuild-debian-package/blob/main/build.sh)). +For `3.0 (native)`, a version that contains a revision fails there: + +```text +dpkg-source: error: can't build with source format '3.0 (native)': native package version may not have a revision +``` + +A `1.0` source format (or no `debian/source/format`, which defaults to +`1.0`) with a `-N` version still produces a tarball, and lintian 2.122 +reports an error on the source package: +`malformed-debian-changelog-version (for native)`. + +Do not drop `-N` from a version that is already published. `1.2.3-1` sorts +higher than `1.2.3`, so the next native upload has to be a higher upstream +version, for example `1.2.4`. + +`3.0 (quilt)` is for a separate upstream tarball, such as a repackaged +third-party tool. The changelog version is `-` (for +example `1.0.14-1wlanpi1`), and `dpkg-source` requires +`../_.orig.tar.*`. A quilt version with no revision fails +with `no upstream tarball found`. + +## Runtime and Test Dependencies + +`dh_virtualenv` runs `pip install -r requirements.txt` when that file exists +in the source tree (dh-virtualenv 1.2.2). Anything listed there is unpacked +into `/opt/` inside the `.deb`. Put runtime dependencies only in +`requirements.txt` (or generate it from `requirements.in` with pip-tools). +Put pytest and other test tools in a `pyproject.toml` extra, and install +that extra in CI and local test environments (`pip install -e ".[test]"`). +Do not pass the test extra to `dh_virtualenv`. ## Required Debian files @@ -54,7 +114,7 @@ Section: contrib/python Priority: optional Maintainer: Your Name Build-Depends: debhelper-compat (= 13), python3, python3-venv, dh-virtualenv (>= 1.0) -Standards-Version: 4.7.0 +Standards-Version: 4.7.2 Homepage: https://github.com/wlan-pi/wlanpi-yourapp Package: wlanpi-yourapp @@ -72,7 +132,7 @@ Description: Short description |-------|-------| | Section | `contrib/python` for Python apps, `embedded` for firmware and shell packages, `utils`/`net`/`x11` where a more specific section fits | | Priority | `optional` | -| Standards-Version | Use the current Debian policy version (4.7.0 on Trixie) | +| Standards-Version | `4.7.2`, the policy version shipped in Debian Trixie (`debian-policy` 4.7.2.0). The fourth component of the package version is non-normative, so the field has three components. | | Package | Must match the source name with `wlanpi-` prefix | | Architecture | `all` for pure Python/shell/firmware, `any` when the package ships compiled extensions or native binaries | @@ -81,30 +141,43 @@ dependencies. Packages that ship only interpreted code or data use `Architecture: all`; see [Repository Reference](../REPOS.md) for the current state of each repo. +Trixie is the development target. Suites that are still supported keep a +`debian/` branch, including `debian/bookworm` +([branch strategy](../RELEASE_PROCESS.md#branch-strategy-quick-reference)). +Bookworm's debhelper 13.11.4 provides `debhelper-compat (= 13)`, and its +dh-virtualenv is 1.2.2-1.3, so the build-dependencies in the control file +above are satisfiable there. `Standards-Version: 4.7.2` names the policy +version in Trixie's `debian-policy` 4.7.2.0. Bookworm ships `debian-policy` +4.6.2.0. The field records which policy the packaging follows. + ### debian/changelog ``` -wlanpi-yourapp (1.0.0) unstable; urgency=medium +wlanpi-yourapp (1.2.3) unstable; urgency=medium * Initial release * Add core functionality * Add systemd service - -- Your Name Mon, 01 Jan 2026 00:00:00 +0000 + -- Your Name Thu, 22 Oct 2026 00:00:00 +0000 ``` -Use `dch` to manage this file: +Use `dch` to manage this file. For a native package the argument has no +Debian revision: + ```bash -dch -v 1.0.0 "Initial release" +dch -v 1.2.3 "Initial release" dch -r "" ``` -### debian/compat (legacy) +### debian/source/format -New packages set `debhelper-compat (= 13)` in `debian/control` instead of -shipping a `debian/compat` file. If you touch a package that still has one, -leave the level alone unless you are deliberately modernizing the package. +```text +3.0 (native) +``` + +See [Source Format and Version](#source-format-and-version). ### debian/copyright @@ -135,15 +208,49 @@ Standard dh_virtualenv setup: export DH_VIRTUALENV_INSTALL_ROOT=/opt +PACKAGE=$(shell dh_listpackages) +DH_VENV_DIR=debian/$(PACKAGE)$(DH_VIRTUALENV_INSTALL_ROOT)/$(PACKAGE) + %: dh $@ --with python-virtualenv override_dh_virtualenv: dh_virtualenv \ --python python3 \ - --install-suffix wlanpi-yourapp + --builtin-venv \ + --install-suffix wlanpi-yourapp \ + --extra-pip-arg "--no-compile" + find $(DH_VENV_DIR) -type d -name '__pycache__' -print0 | xargs -0r rm -rf + find $(DH_VENV_DIR) -name '*.pyc' -delete + rm -rf $(DH_VENV_DIR)/bin/pip* \ + $(DH_VENV_DIR)/bin/wheel \ + $(DH_VENV_DIR)/.gitignore \ + $(DH_VENV_DIR)/lib/python*/site-packages/pip \ + $(DH_VENV_DIR)/lib/python*/site-packages/pip-*.dist-info \ + $(DH_VENV_DIR)/lib/python*/site-packages/setuptools \ + $(DH_VENV_DIR)/lib/python*/site-packages/setuptools-*.dist-info \ + $(DH_VENV_DIR)/lib/python*/site-packages/wheel \ + $(DH_VENV_DIR)/lib/python*/site-packages/wheel-*.dist-info ``` +`--builtin-venv` is required when the build chroot has `python3-venv` and +no `virtualenv` binary. dh-virtualenv 1.2.2 on Trixie depends on +`virtualenv | python3-virtualenv | python3-venv`. Without the flag it +executes `virtualenv` and the build fails with `FileNotFoundError`. +`--no-compile` plus the cleanup stops the package shipping `__pycache__` +and pip, which lintian reports as `package-installs-python-pycache-dir` +(an error) and `wrong-path-for-interpreter` on `bin/pip*`. + +Put the unit file at `debian/.service` and let `dh_installsystemd` +install it. Override that helper only when one unit needs different start +behaviour. Debhelper 13.24 (Trixie) installs the unit into +`/usr/lib/systemd/system`. Debhelper 13.11.4 (Bookworm) still installs it +into `lib/systemd/system`. Bookworm lintian 2.116.3 accepts both of those +paths: `systemd-service-in-odd-location` matches `etc/systemd/system` only. +Trixie lintian reports `aliased-location` for the `lib/` path. The helper +inserts `deb-systemd-helper` enable/state calls and `deb-systemd-invoke` +start/stop at `#DEBHELPER#`. + Make executable: ```bash chmod +x debian/rules @@ -155,9 +262,79 @@ Define additional files to install: ``` config/yourapp.conf etc/wlanpi-yourapp/ -systemd/yourapp.service lib/systemd/system/ ``` +A unit that is not named `debian/.service` (a helper unit, a +drop-in) goes in the `.install` file with destination +`usr/lib/systemd/system/` on Trixie and on Bookworm. On Trixie, +`lib/systemd/system/` is the `aliased-location` path. + +### Maintainer Scripts + +Create the service user and writable directories before `#DEBHELPER#`. +The token expands to the `dh_installsystemd` block, which starts the unit +on configure. `chown` to `wlanpi` before `useradd` fails the install +(`invalid user`). + +`deb-systemd-helper` implements `enable`, `disable`, `purge`, `mask`, +`unmask`, `is-enabled`, `was-enabled`, `debian-installed`, `update-state`, +and `reenable`. It has no `start` or `stop`. Called from a maintainer +script (`DPKG_MAINTSCRIPT_PACKAGE` set), `start` and `stop` exit 0 and do +nothing. Starting and stopping is `deb-systemd-invoke`, which +`dh_installsystemd` inserts. Do not call either helper by hand. + +```bash +#!/bin/sh +set -e + +case "$1" in + configure) + if ! getent group wlanpi >/dev/null; then + groupadd --system wlanpi + fi + if ! id -u wlanpi >/dev/null 2>&1; then + useradd --system --gid wlanpi --shell /usr/sbin/nologin --comment "WLAN Pi service" wlanpi + fi + mkdir -p /var/log/wlanpi-yourapp + chown wlanpi:wlanpi /var/log/wlanpi-yourapp + ;; +esac + +#DEBHELPER# + +exit 0 +``` + +A `prerm` you write yourself is the same shape: `set -e`, then `#DEBHELPER#`, +then `exit 0`. Omit `postinst`/`prerm` entirely when the generated snippets +are enough. + +Scripts are executable and LF. `#DEBHELPER#` missing from a script you ship +is the lintian warning `maintainer-script-lacks-debhelper-token`. + +## Lintian and /opt + +`dir-or-file-in-opt` is an error (lintian 2.122 on Trixie), not a warning. +`dir-or-file-in-etc-opt` is a separate error for `/etc/opt`. WLAN Pi +applications install under `/opt` on purpose. Ship +`debian/.lintian-overrides` with the justification above the tag. +Omitting the path suppresses every instance of the tag, which is what a +virtualenv tree needs: + +```text +# dh_virtualenv installs this add-on application and its dependencies under +# /opt/wlanpi-yourapp. /opt is reserved for add-on software; these packages +# are not candidates for the Debian archive. +wlanpi-yourapp: dir-or-file-in-opt +``` + +The console script's shebang points at `/opt//bin/python`. Lintian +warns `unusual-interpreter` for that path. That warning is expected for a +`dh_virtualenv` entry point. + +`dh_lintian` installs the overrides file. Do not replace it with an empty +override. + ## Build process ### Local build @@ -169,22 +346,32 @@ sudo apt-get install build-essential debhelper dh-virtualenv python3-venv # Build the package dpkg-buildpackage -us -uc -b -# Result: ../wlanpi-yourapp_1.0.0-1_arm64.deb +# Result: ../wlanpi-yourapp_1.2.3_.deb ``` -### CI/CD build +On the arm64 CI runner the architecture is `arm64`. A local amd64 build +produces `amd64` in that position. -GitHub Actions automatically build packages by calling the reusable workflows -in [WLAN-Pi/gh-workflows](https://github.com/WLAN-Pi/gh-workflows): +### CI/CD build -1. **Build and archive** (`build-and-archive-debian-package.yml`) - Builds the - package with `sbuild` and uploads it as a workflow artifact. Trigger paths - vary per repo: some watch only `debian/changelog`, others ignore docs-only - changes. It does not run tests or lint; those are separate workflows. -2. **Deploy to Packagecloud** (`deploy-to-packagecloud.yml`) - Triggered by a - `debian/changelog` change pushed to `main`. Builds and uploads to the +A new repository calls the reusable workflows in +[WLAN-Pi/gh-workflows](https://github.com/WLAN-Pi/gh-workflows). Do not copy +the `sbuild` and `package_cloud` steps into the repo. The caller files are +`build-and-archive-debian-package.yml` and `deploy-to-packagecloud.yml`, and +they `uses:` the shared workflows: + +1. **Build and archive** calls `sbuild-pkg.yml`. It builds with `sbuild` and + uploads the `.deb` as a workflow artifact. Trigger paths vary per repo: + some watch only `debian/changelog`, others ignore docs-only changes. It + does not run tests or lint; those are separate workflows. +2. **Deploy to Packagecloud** calls `sbuild-deploy-pkg.yml`. A + `debian/changelog` change pushed to `main` builds and uploads to the Packagecloud `dev` repository. +The full caller is in the [Packaging Example](../PACKAGING_EXAMPLE.md#step-8-github-actions-workflows). +Repos that still inline the steps are listed in +[Repository Reference](../REPOS.md#ci). + Releases are only cut when a `debian/changelog` change reaches `main`; pushing a git tag does not deploy. See [Release Process](../RELEASE_PROCESS.md) for details. diff --git a/style/DCH.md b/style/DCH.md index 3f600cf..8b47d49 100644 --- a/style/DCH.md +++ b/style/DCH.md @@ -24,7 +24,11 @@ You can run debchange from the root of the repository as debchange will climb th ### Update an existing version entry -You should minially use `dch -i` when adding a new changelog because `-i` increases the release number and adds a changelog entry. +Use `dch -i` when adding a changelog entry for a new upload. On a native +package, `-i` increments the upstream version (`1.2.3` becomes `1.2.4`). +On a package whose version still has a Debian revision, `-i` increments +that revision. First-party WLAN Pi packages are native; the version form +is in [Source Format and Version](../architecture/PACKAGING.md#source-format-and-version). If you want to edit the changelog without changing the version or adding a new entry, use `-e` instead of `-i`. @@ -34,6 +38,10 @@ On version numbers, from the Debian maintainers guide: > One tricky case can occur when you make a local package, to experiment with the packaging before uploading the normal version to the official archive, e.g., 1.0.1-1. For smoother upgrades, it is a good idea to create a changelog entry with a version string such as 1.0.1-1~rc1. You may unclutter changelog by consolidating such local change entries into a single entry for the official package. +That note is about a local experiment on a non-native version. A first-party +WLAN Pi changelog version has no `-N` revision. See +[Source Format and Version](../architecture/PACKAGING.md#source-format-and-version). + ## debchange - environment variables You will likely want to set the `DEBFULLNAME` and `DEBEMAIL` environment variables on your development system. Two options demonstrated: diff --git a/style/WORKFLOW.md b/style/WORKFLOW.md index ab446ee..52ed336 100644 --- a/style/WORKFLOW.md +++ b/style/WORKFLOW.md @@ -55,9 +55,11 @@ Bump both in the same change. Please note that Python package versioning should follow PEP 440. https://www.python.org/dev/peps/pep-0440/ -Please note that the Debian package versioning does not strictly align with Python versioning. - -- Debian generally follows `_-_.deb`. Package format also matters. Quilt vs native have differences. Native does not allow revision numbers (`1.0.0` instead of `1.0.0-1`). [More on quilt vs native here](https://wiki.debian.org/Projects/DebSrc3.0). +The Debian version and the source format are one scheme, defined in +[Architecture/Packaging](../architecture/PACKAGING.md#source-format-and-version). +A first-party package is `3.0 (native)` with changelog version `1.2.3` (no +`-N` revision). `__version__.py` carries that same `major.minor.patch`. The +binary filename is `_1.2.3_.deb`. ## Committing From 70ecf731e850e99914e151840e8d0a9f171de80d Mon Sep 17 00:00:00 2001 From: Cursor Agent Date: Thu, 22 Oct 2026 02:26:37 +0000 Subject: [PATCH 2/2] docs: show the wlanpi.us install alias next to the HTTPS one-liner The documented install stays the raw GitHub URL over HTTPS. The short form is curl -fSL http://wlanpi.us/install, whose first hop is plain HTTP. Co-authored-by: jsz --- GETTING_STARTED.md | 5 +++++ README.md | 5 +++++ 2 files changed, 10 insertions(+) diff --git a/GETTING_STARTED.md b/GETTING_STARTED.md index f850a3d..5d52da8 100644 --- a/GETTING_STARTED.md +++ b/GETTING_STARTED.md @@ -19,9 +19,14 @@ This repository contains the documentation for developers who want to contribute ```bash curl -fSL https://raw.githubusercontent.com/WLAN-Pi/developers/main/install.sh | sudo bash +curl -fSL http://wlanpi.us/install | sudo bash ``` That URL is `install.sh` in this repository, fetched over HTTPS. +`http://wlanpi.us/install` is a short alias: a GET redirects to the same +script. The first hop is plain HTTP, because Hover's forwarder is HTTP-only. +The one-liner can move to `https://wlanpi.us/install` once that domain +serves HTTPS, for example with Cloudflare redirect rules or GitHub Pages. ### 2. Set up git diff --git a/README.md b/README.md index 346f67a..5e9c4f1 100644 --- a/README.md +++ b/README.md @@ -17,9 +17,14 @@ To set up a WLAN Pi device for development (requires `curl` and `sudo`): ```bash curl -vfSL https://raw.githubusercontent.com/WLAN-Pi/developers/main/install.sh | sudo bash +curl -fSL http://wlanpi.us/install | sudo bash ``` That URL is `install.sh` in this repository, fetched over HTTPS. +`http://wlanpi.us/install` is a short alias: a GET redirects to the same +script. The first hop is plain HTTP, because Hover's forwarder is HTTP-only. +The one-liner can move to `https://wlanpi.us/install` once that domain +serves HTTPS, for example with Cloudflare redirect rules or GitHub Pages. ## Documentation