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"