From dd2813340bfabfd0a7876adf85d89af2fca668b9 Mon Sep 17 00:00:00 2001 From: Thales Maciel Date: Sat, 14 Mar 2026 15:45:21 -0300 Subject: [PATCH] Align CI with the validated Ubuntu support floor Stop implying that one Ubuntu 3.11 unit lane validates the full Linux support surface Aman documents.\n\nSplit CI into an Ubuntu CPython 3.10/3.11/3.12 unit-package matrix, a portable install plus doctor smoke lane, and a packaging lane gated on both. Add a reproducible ci_portable_smoke.sh helper with fake systemctl coverage, and force the installer onto /usr/bin/python3 so the smoke path uses the distro-provided GI and X11 Python packages it is meant to validate.\n\nUpdate the README, release/distribution docs, and Debian metadata to distinguish the automated Ubuntu CI floor from broader manual GA signoff families, and add the missing AppIndicator introspection package to the Ubuntu/Debian dependency lists.\n\nValidate with python3 -m unittest discover -s tests -p 'test_*.py', python3 -m py_compile src/*.py tests/*.py, and bash -n scripts/ci_portable_smoke.sh. The full xvfb-backed smoke could not be run locally in this sandbox because xvfb-run is unavailable. --- .github/workflows/ci.yml | 108 +++++++++++++++++++++- README.md | 8 +- docs/developer-workflows.md | 3 + docs/persona-and-distribution.md | 5 +- docs/portable-install.md | 7 +- docs/releases/1.0.0.md | 5 +- docs/x11-ga/ga-validation-report.md | 5 + packaging/deb/control.in | 2 +- scripts/ci_portable_smoke.sh | 136 ++++++++++++++++++++++++++++ 9 files changed, 270 insertions(+), 9 deletions(-) create mode 100755 scripts/ci_portable_smoke.sh diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 605cded..78f54e2 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -5,18 +5,120 @@ on: pull_request: jobs: - test-and-build: + unit-matrix: + name: Unit Matrix (${{ matrix.python-version }}) + runs-on: ubuntu-latest + strategy: + fail-fast: false + matrix: + python-version: ["3.10", "3.11", "3.12"] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: ${{ matrix.python-version }} + - name: Install Ubuntu runtime dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + gobject-introspection \ + libcairo2-dev \ + libgirepository1.0-dev \ + libportaudio2 \ + pkg-config \ + python3-gi \ + python3-xlib \ + gir1.2-gtk-3.0 \ + gir1.2-ayatanaappindicator3-0.1 \ + libayatana-appindicator3-1 + - name: Create project environment + run: | + python -m venv .venv + . .venv/bin/activate + python -m pip install --upgrade pip + python -m pip install uv build + uv sync --active --frozen + echo "${GITHUB_WORKSPACE}/.venv/bin" >> "${GITHUB_PATH}" + - name: Run compile check + run: python -m py_compile src/*.py tests/*.py + - name: Run unit and package-logic test suite + run: python -m unittest discover -s tests -p 'test_*.py' + + portable-ubuntu-smoke: + name: Portable Ubuntu Smoke runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-python@v5 with: python-version: "3.11" - - name: Install dependencies + - name: Install Ubuntu runtime dependencies run: | + sudo apt-get update + sudo apt-get install -y \ + gobject-introspection \ + libcairo2-dev \ + libgirepository1.0-dev \ + libportaudio2 \ + pkg-config \ + python3-gi \ + python3-xlib \ + gir1.2-gtk-3.0 \ + gir1.2-ayatanaappindicator3-0.1 \ + libayatana-appindicator3-1 \ + xvfb + - name: Create project environment + run: | + python -m venv .venv + . .venv/bin/activate python -m pip install --upgrade pip python -m pip install uv build - uv sync + uv sync --active --frozen + echo "${GITHUB_WORKSPACE}/.venv/bin" >> "${GITHUB_PATH}" + - name: Run portable install and doctor smoke with distro python + env: + AMAN_CI_SYSTEM_PYTHON: /usr/bin/python3 + run: bash ./scripts/ci_portable_smoke.sh + - name: Upload portable smoke logs + if: always() + uses: actions/upload-artifact@v4 + with: + name: aman-portable-smoke-logs + path: build/ci-smoke + + package-artifacts: + name: Package Artifacts + runs-on: ubuntu-latest + needs: + - unit-matrix + - portable-ubuntu-smoke + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-python@v5 + with: + python-version: "3.11" + - name: Install Ubuntu runtime dependencies + run: | + sudo apt-get update + sudo apt-get install -y \ + gobject-introspection \ + libcairo2-dev \ + libgirepository1.0-dev \ + libportaudio2 \ + pkg-config \ + python3-gi \ + python3-xlib \ + gir1.2-gtk-3.0 \ + gir1.2-ayatanaappindicator3-0.1 \ + libayatana-appindicator3-1 + - name: Create project environment + run: | + python -m venv .venv + . .venv/bin/activate + python -m pip install --upgrade pip + python -m pip install uv build + uv sync --active --frozen + echo "${GITHUB_WORKSPACE}/.venv/bin" >> "${GITHUB_PATH}" - name: Prepare release candidate artifacts run: make release-prep - name: Upload packaging artifacts diff --git a/README.md b/README.md index 7bd070e..4649b20 100644 --- a/README.md +++ b/README.md @@ -19,12 +19,16 @@ Support requests and bug reports go to | Supported daily-use mode | `systemd --user` service | | Manual foreground mode | `aman run` for setup, support, and debugging | | Canonical recovery sequence | `aman doctor` -> `aman self-check` -> `journalctl --user -u aman` -> `aman run --verbose` | -| Representative GA validation families | Debian/Ubuntu, Arch, Fedora, openSUSE | +| Automated CI floor | Ubuntu CI: CPython `3.10`, `3.11`, `3.12` for unit/package coverage, plus portable install and `aman doctor` smoke with Ubuntu system `python3` | +| Manual GA signoff families | Debian/Ubuntu, Arch, Fedora, openSUSE | | Portable installer prerequisite | System CPython `3.10`, `3.11`, or `3.12` | Distribution policy and user persona details live in [`docs/persona-and-distribution.md`](docs/persona-and-distribution.md). +The wider distro-family list is a manual validation target for release signoff. +It is not the current automated CI surface yet. + ## 60-Second Quickstart First, install the runtime dependencies for your distro: @@ -33,7 +37,7 @@ First, install the runtime dependencies for your distro: Ubuntu/Debian ```bash -sudo apt install -y libportaudio2 python3-gi python3-xlib gir1.2-gtk-3.0 libayatana-appindicator3-1 +sudo apt install -y libportaudio2 python3-gi python3-xlib gir1.2-gtk-3.0 gir1.2-ayatanaappindicator3-0.1 libayatana-appindicator3-1 ``` diff --git a/docs/developer-workflows.md b/docs/developer-workflows.md index 0eb4970..1f9cbdd 100644 --- a/docs/developer-workflows.md +++ b/docs/developer-workflows.md @@ -14,10 +14,13 @@ make package-arch make runtime-check make release-check make release-prep +bash ./scripts/ci_portable_smoke.sh ``` - `make package-portable` builds `dist/aman-x11-linux-.tar.gz` plus its `.sha256` file. +- `bash ./scripts/ci_portable_smoke.sh` reproduces the Ubuntu CI portable + install plus `aman doctor` smoke path locally. - `make release-prep` runs `make release-check`, builds the packaged artifacts, and writes `dist/SHA256SUMS` for the release page upload set. - `make package-deb` installs Python dependencies while creating the package. diff --git a/docs/persona-and-distribution.md b/docs/persona-and-distribution.md index e15dbbc..eae7233 100644 --- a/docs/persona-and-distribution.md +++ b/docs/persona-and-distribution.md @@ -50,7 +50,10 @@ For X11 GA, Aman supports: - Runtime dependencies installed from the distro package manager. - `systemd --user` as the supported daily-use path. - `aman run` as the foreground setup, support, and debugging path. -- Representative validation across Debian/Ubuntu, Arch, Fedora, and openSUSE. +- Automated validation floor on Ubuntu CI: CPython `3.10`, `3.11`, and `3.12` + for unit/package coverage, plus portable install and `aman doctor` smoke with + Ubuntu system `python3`. +- Manual GA signoff families: Debian/Ubuntu, Arch, Fedora, openSUSE. - The recovery sequence `aman doctor` -> `aman self-check` -> `journalctl --user -u aman` -> `aman run --verbose`. diff --git a/docs/portable-install.md b/docs/portable-install.md index 21a241e..a22cd87 100644 --- a/docs/portable-install.md +++ b/docs/portable-install.md @@ -15,6 +15,11 @@ Download published bundles, checksums, and release notes from - System CPython `3.10`, `3.11`, or `3.12` - Runtime dependencies installed from the distro package manager +Current automated validation covers Ubuntu CI on CPython `3.10`, `3.11`, and +`3.12` for unit/package coverage, plus a portable install and `aman doctor` +smoke path with Ubuntu system `python3`. The other distro-family instructions +below remain manual validation targets. + ## Runtime dependencies Install the runtime dependencies for your distro before running `install.sh`. @@ -22,7 +27,7 @@ Install the runtime dependencies for your distro before running `install.sh`. ### Ubuntu/Debian ```bash -sudo apt install -y libportaudio2 python3-gi python3-xlib gir1.2-gtk-3.0 libayatana-appindicator3-1 +sudo apt install -y libportaudio2 python3-gi python3-xlib gir1.2-gtk-3.0 gir1.2-ayatanaappindicator3-0.1 libayatana-appindicator3-1 ``` ### Arch Linux diff --git a/docs/releases/1.0.0.md b/docs/releases/1.0.0.md index a3bd191..b1533ad 100644 --- a/docs/releases/1.0.0.md +++ b/docs/releases/1.0.0.md @@ -15,7 +15,10 @@ This is the first GA-targeted X11 release for Aman. - `systemd --user` for supported daily use - System CPython `3.10`, `3.11`, or `3.12` for the portable installer - Runtime dependencies installed from the distro package manager -- Representative validation families: Debian/Ubuntu, Arch, Fedora, openSUSE +- Automated validation floor: Ubuntu CI on CPython `3.10`, `3.11`, and `3.12` + for unit/package coverage, plus portable install and `aman doctor` smoke + with Ubuntu system `python3` +- Manual GA signoff families: Debian/Ubuntu, Arch, Fedora, openSUSE ## Artifacts diff --git a/docs/x11-ga/ga-validation-report.md b/docs/x11-ga/ga-validation-report.md index 6e4db7c..237825b 100644 --- a/docs/x11-ga/ga-validation-report.md +++ b/docs/x11-ga/ga-validation-report.md @@ -34,6 +34,10 @@ state. ## Evidence sources +- Automated CI validation: + GitHub Actions Ubuntu lanes for CPython `3.10`, `3.11`, and `3.12` for + unit/package coverage, plus a portable install and `aman doctor` smoke lane + with Ubuntu system `python3` - Portable lifecycle matrix: [`portable-validation-matrix.md`](./portable-validation-matrix.md) - Runtime reliability matrix: @@ -52,6 +56,7 @@ state. | Milestone 2 portable lifecycle | Complete for now | Arch row in `portable-validation-matrix.md` plus [`user-readiness/1773357669.md`](../../user-readiness/1773357669.md) | | Milestone 3 runtime reliability | Complete for now | Arch runtime rows in `runtime-validation-report.md` plus [`user-readiness/1773357669.md`](../../user-readiness/1773357669.md) | | Milestone 4 first-run UX/docs | Complete | `first-run-review-notes.md` and `user-readiness/1773352170.md` | +| Automated validation floor | Repo-complete | GitHub Actions Ubuntu matrix on CPython `3.10`-`3.12` plus portable smoke with Ubuntu system `python3` | | Release metadata and support surface | Repo-complete | `LICENSE`, `SUPPORT.md`, `pyproject.toml`, packaging templates | | Release artifacts and checksums | Repo-complete | `make release-prep`, `dist/SHA256SUMS`, `docs/releases/1.0.0.md` | | Full four-family GA validation | Pending | Complete the remaining Debian/Ubuntu, Fedora, and openSUSE rows in both validation matrices | diff --git a/packaging/deb/control.in b/packaging/deb/control.in index 345868b..e8623bc 100644 --- a/packaging/deb/control.in +++ b/packaging/deb/control.in @@ -4,7 +4,7 @@ Section: utils Priority: optional Architecture: __ARCH__ Maintainer: Thales Maciel -Depends: python3, python3-venv, python3-gi, python3-xlib, libportaudio2, gir1.2-gtk-3.0, libayatana-appindicator3-1 +Depends: python3, python3-venv, python3-gi, python3-xlib, libportaudio2, gir1.2-gtk-3.0, gir1.2-ayatanaappindicator3-0.1, libayatana-appindicator3-1 Description: Aman local amanuensis daemon for X11 desktops Aman records microphone input, transcribes speech, optionally rewrites output, and injects text into the focused desktop app. Includes tray controls and a diff --git a/scripts/ci_portable_smoke.sh b/scripts/ci_portable_smoke.sh new file mode 100755 index 0000000..f34eaaf --- /dev/null +++ b/scripts/ci_portable_smoke.sh @@ -0,0 +1,136 @@ +#!/usr/bin/env bash +set -euo pipefail + +SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)" +source "${SCRIPT_DIR}/package_common.sh" + +require_command mktemp +require_command tar +require_command xvfb-run + +DISTRO_PYTHON="${AMAN_CI_SYSTEM_PYTHON:-/usr/bin/python3}" +require_command "${DISTRO_PYTHON}" + +LOG_DIR="${BUILD_DIR}/ci-smoke" +RUN_DIR="${LOG_DIR}/run" +HOME_DIR="${RUN_DIR}/home" +FAKE_BIN_DIR="${RUN_DIR}/fake-bin" +EXTRACT_DIR="${RUN_DIR}/bundle" +RUNTIME_DIR="${RUN_DIR}/xdg-runtime" +COMMAND_LOG="${LOG_DIR}/commands.log" +SYSTEMCTL_LOG="${LOG_DIR}/systemctl.log" + +dump_logs() { + local path + for path in "${COMMAND_LOG}" "${SYSTEMCTL_LOG}" "${LOG_DIR}"/*.stdout.log "${LOG_DIR}"/*.stderr.log; do + if [[ -f "${path}" ]]; then + echo "=== ${path#${ROOT_DIR}/} ===" + cat "${path}" + fi + done +} + +on_exit() { + local status="$1" + if [[ "${status}" -ne 0 ]]; then + dump_logs + fi +} +trap 'on_exit $?' EXIT + +run_logged() { + local name="$1" + shift + local stdout_log="${LOG_DIR}/${name}.stdout.log" + local stderr_log="${LOG_DIR}/${name}.stderr.log" + { + printf "+" + printf " %q" "$@" + printf "\n" + } >>"${COMMAND_LOG}" + "$@" >"${stdout_log}" 2>"${stderr_log}" +} + +rm -rf "${LOG_DIR}" +mkdir -p "${HOME_DIR}" "${FAKE_BIN_DIR}" "${EXTRACT_DIR}" "${RUNTIME_DIR}" +: >"${COMMAND_LOG}" +: >"${SYSTEMCTL_LOG}" + +cat >"${FAKE_BIN_DIR}/systemctl" <<'EOF' +#!/usr/bin/env bash +set -euo pipefail + +log_path="${SYSTEMCTL_LOG:?}" +if [[ "${1:-}" == "--user" ]]; then + shift +fi +printf '%s\n' "$*" >>"${log_path}" + +case "$*" in + "daemon-reload") + ;; + "enable --now aman") + ;; + "stop aman") + ;; + "disable --now aman") + ;; + "is-system-running") + printf 'running\n' + ;; + "show aman --property=FragmentPath --value") + printf '%s\n' "${AMAN_CI_SERVICE_PATH:?}" + ;; + "is-enabled aman") + printf 'enabled\n' + ;; + "is-active aman") + printf 'active\n' + ;; + *) + echo "unexpected systemctl command: $*" >&2 + exit 1 + ;; +esac +EOF +chmod 0755 "${FAKE_BIN_DIR}/systemctl" + +run_logged package-portable bash "${SCRIPT_DIR}/package_portable.sh" + +VERSION="$(project_version)" +PACKAGE_NAME="$(project_name)" +PORTABLE_TARBALL="${DIST_DIR}/${PACKAGE_NAME}-x11-linux-${VERSION}.tar.gz" +BUNDLE_DIR="${EXTRACT_DIR}/${PACKAGE_NAME}-x11-linux-${VERSION}" + +run_logged extract tar -C "${EXTRACT_DIR}" -xzf "${PORTABLE_TARBALL}" + +export HOME="${HOME_DIR}" +export PATH="${FAKE_BIN_DIR}:${HOME_DIR}/.local/bin:${PATH}" +export SYSTEMCTL_LOG +export AMAN_CI_SERVICE_PATH="${HOME_DIR}/.config/systemd/user/aman.service" + +run_logged distro-python "${DISTRO_PYTHON}" --version + +( + cd "${BUNDLE_DIR}" + run_logged install env \ + PATH="${FAKE_BIN_DIR}:${HOME_DIR}/.local/bin:$(dirname "${DISTRO_PYTHON}"):${PATH}" \ + ./install.sh +) + +run_logged version "${HOME_DIR}/.local/bin/aman" version +run_logged init "${HOME_DIR}/.local/bin/aman" init --config "${HOME_DIR}/.config/aman/config.json" +run_logged doctor xvfb-run -a env \ + HOME="${HOME_DIR}" \ + PATH="${PATH}" \ + SYSTEMCTL_LOG="${SYSTEMCTL_LOG}" \ + AMAN_CI_SERVICE_PATH="${AMAN_CI_SERVICE_PATH}" \ + XDG_RUNTIME_DIR="${RUNTIME_DIR}" \ + XDG_SESSION_TYPE="x11" \ + "${HOME_DIR}/.local/bin/aman" doctor --config "${HOME_DIR}/.config/aman/config.json" +run_logged uninstall "${HOME_DIR}/.local/share/aman/current/uninstall.sh" --purge + +echo "portable smoke passed" +echo "logs: ${LOG_DIR}" +cat "${LOG_DIR}/doctor.stdout.log"