Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
128 changes: 122 additions & 6 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -4,26 +4,142 @@ on:
push:
pull_request:

# Least privilege: hosted CI only needs to read the repository.
permissions:
contents: read

# NOTE: hosted CI runs the offline / fake-hardware gates ONLY.
# Disposable-VM installer lifecycle tests (install / rollback / uninstall in a
# throwaway VM) and real-hardware qualification are SEPARATE self-hosted /
# manual gates and are intentionally NOT run here.

jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
cache: pip
- run: python -m pip install --upgrade pip
- run: python -m pip install "ruff==0.15.20"
- name: Ruff lint
run: ruff check honor_control tests
- name: Byte-compile sanity check
run: python -m compileall -q honor_control tests

test:
runs-on: ubuntu-latest
strategy:
fail-fast: false
matrix:
python: ["3.11", "3.12", "3.13", "3.14"]
steps:
- uses: actions/checkout@v4
- uses: actions/setup-python@v5
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: ${{ matrix.python }}
cache: pip
- name: Install Qt runtime libraries
run: sudo apt-get update && sudo apt-get install -y libegl1 libgl1
- run: python -m pip install --upgrade pip
- run: python -m pip install ".[gui,dev]"
- run: ruff check honor_control tests
- run: pytest -q -m "not hardware"
- run: python -m build
# pytest-cov is installed here (not in pyproject) so the coverage floor
# tooling stays a CI concern.
- run: python -m pip install ".[gui,dev]" "pytest-cov==7.1.0"
# Branch coverage with a hard 72% floor (measured project baseline).
# `-m "not hardware"` selects the whole suite today because no test
# carries a `hardware` mark yet; it becomes meaningful once
# hardware-marked tests exist for the separate self-hosted hardware gate.
- name: Pytest with branch coverage (72% floor)
run: pytest -q -W error -m "not hardware" --cov=honor_control --cov-branch --cov-fail-under=72

security:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
with:
fetch-depth: 0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
cache: pip
- run: python -m pip install --upgrade pip
- run: python -m pip install ".[gui,dev]" "pip-audit==2.10.1" "bandit==1.9.4"
- name: Dependency vulnerability gate
run: pip-audit --strict --requirement requirements-audit.txt
- name: Bandit medium/high gate
run: bandit -r honor_control -ll
- name: Gitleaks full-history gate
uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7
env:
GITHUB_TOKEN: ${{ github.token }}

shell:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- name: Install shellcheck
run: sudo apt-get update && sudo apt-get install -y shellcheck
- name: bash -n syntax check
run: |
set -e
for f in scripts/*.sh packaging/systemd/honor-touchpad-system-sleep; do
echo "bash -n $f"
bash -n "$f"
done
# shellcheck is a HARD gate: any finding fails the job.
- name: shellcheck (hard gate)
run: shellcheck scripts/*.sh packaging/systemd/honor-touchpad-system-sleep

packaging:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@d23441a48e516b6c34aea4fa41551a30e30af803 # v6.1.0
- uses: actions/setup-python@ece7cb06caefa5fff74198d8649806c4678c61a1 # v6.3.0
with:
python-version: "3.12"
cache: pip
- name: Install validators
run: sudo apt-get update && sudo apt-get install -y libxml2-utils desktop-file-utils
# XML well-formedness is a HARD gate.
- name: xmllint polkit + dbus XML (hard gate)
run: |
xmllint --noout packaging/polkit/*.policy
xmllint --noout packaging/dbus/*.conf
- name: desktop-file-validate
run: desktop-file-validate packaging/desktop/*.desktop
- name: systemd-analyze verify
run: |
# The units intentionally use stable /usr/bin entry points installed
# by install-local.sh. Provide inert stubs so verification checks the
# unit definitions without depending on a system-wide CI install.
sudo install -m 0755 /bin/true /usr/bin/honor-control-service
sudo install -m 0755 /bin/true /usr/bin/honor-touchpadctl
trap 'sudo rm -f /usr/bin/honor-control-service /usr/bin/honor-touchpadctl' EXIT
systemd-analyze verify packaging/systemd/*.service
- run: python -m pip install --upgrade pip
- run: python -m pip install ".[gui,dev]" "build==1.5.1" "setuptools==83.0.0" "wheel==0.47.0"
- name: Build wheel + sdist reproducibly
run: |
set -e
SOURCE_DATE_EPOCH="$(git show -s --format=%ct HEAD)"
export SOURCE_DATE_EPOCH
export PYTHONHASHSEED=0 TZ=UTC
python -m build --no-isolation --outdir dist/first
bash scripts/normalize-sdist.sh ./dist/first/*.tar.gz "$SOURCE_DATE_EPOCH"
rm -rf build honor_control.egg-info
python -m build --no-isolation --outdir dist/second
bash scripts/normalize-sdist.sh ./dist/second/*.tar.gz "$SOURCE_DATE_EPOCH"
diff -u \
<(cd dist/first && sha256sum -- * | sed 's# # #') \
<(cd dist/second && sha256sum -- * | sed 's# # #')
- name: Verify artifacts contain the package (hard gate)
run: |
set -e
ls -l dist/first/
test "$(find dist/first -maxdepth 1 -name '*.whl' | wc -l)" -eq 1 || { echo "expected one wheel"; exit 1; }
test "$(find dist/first -maxdepth 1 -name '*.tar.gz' | wc -l)" -eq 1 || { echo "expected one sdist"; exit 1; }
python -m zipfile -l "$(find dist/first -maxdepth 1 -name '*.whl')" | grep -q 'honor_control/' || { echo "wheel missing honor_control package"; exit 1; }
tar -tzf "$(find dist/first -maxdepth 1 -name '*.tar.gz')" | grep -q 'honor_control/' || { echo "sdist missing honor_control package"; exit 1; }
echo "wheel + sdist both contain honor_control/"
18 changes: 4 additions & 14 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -30,20 +30,10 @@ htmlcov/
.DS_Store
Thumbs.db

# Reverse-engineering evidence stays local. Keep only the compact source of
# truth and the optional narrow WMI driver in Git; tracked historical files
# remain tracked even though this pattern covers their directory.
Reverse engineering/*
!Reverse engineering/README.md
!Reverse engineering/finish-trackpad-re/
Reverse engineering/finish-trackpad-re/*
!Reverse engineering/finish-trackpad-re/protocol.json
!Reverse engineering/finish-trackpad-re/linux/
Reverse engineering/finish-trackpad-re/linux/*
!Reverse engineering/finish-trackpad-re/linux/PORTING_SPEC.md
!Reverse engineering/finish-trackpad-re/linux/README.md
!Reverse engineering/finish-trackpad-re/linux/wmi/
!Reverse engineering/finish-trackpad-re/linux/wmi/*
# Reverse-engineering evidence stays local and is not tracked. The compact
# protocol source of truth lives under docs/protocol/ and
# docs/hardware-validation/; the optional WMI module under experimental/.
Reverse engineering/

# Generated repo map (local reference, not tracked)
REPO_MAP.md
27 changes: 27 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,33 @@

## [Unreleased] - 0.2.0

### Security / Release hardening

- Touchpad firmware setting writes are now disabled by default behind the
`touchpad_firmware_writes_qualified` capability gate (default `false`);
probe, encode, and gesture input remain available.
- The automatic `honor-touchpad-restore.service` unit is no longer installed
or enabled unless firmware writes are qualified (installer flag
`--enable-touchpad-firmware` / env `HONOR_TOUCHPAD_FIRMWARE_QUALIFIED=1`).
- The installer now supports a complete offline install. `--wheelhouse DIR`
verifies the exact checksummed artifact set and installs reviewed wheels
with `--no-index`.
- Added a transactional rollback path (`scripts/install-local.sh --rollback`
or `scripts/rollback.sh`) that restores the predecessor, managed system
files, service state, and the original release if health checks fail.
- Added `scripts/make-release-bundle.sh` to build and clean-room test a
reproducible wheelhouse containing the application and all dependencies.
- CI split into `lint`, `test`, `security`, `shell`, and `packaging` jobs:
branch-coverage floor (`--cov-branch --cov-fail-under=72`), shellcheck,
strict warning handling, `xmllint` packaging validation, hard
`pip-audit` / `bandit` / full-history gitleaks gates, and pinned actions.
- Removed the unhardened direct-`Exec` / `User=root` D-Bus activation
fallback; activation now requires systemd
(`SystemdService=honor-control.service`).
- Corrected repository URLs (`HonorLinux/honor-control` to
`ZachAR3/HonorControl`) in the systemd unit and polkit policy, and simplified
the README to the supported platform, install, usage, and development basics.

### Fixed

- Power-profile application now coordinates with PPD without masking host
Expand Down
Loading
Loading