Add package-first build and distribution workflow
This commit is contained in:
parent
4a69c3d333
commit
993f51712b
13 changed files with 462 additions and 52 deletions
21
.github/workflows/ci.yml
vendored
21
.github/workflows/ci.yml
vendored
|
|
@ -17,9 +17,18 @@ jobs:
|
||||||
python -m pip install --upgrade pip
|
python -m pip install --upgrade pip
|
||||||
python -m pip install uv build
|
python -m pip install uv build
|
||||||
uv sync --extra x11
|
uv sync --extra x11
|
||||||
- name: Compile
|
- name: Release quality checks
|
||||||
run: python -m py_compile src/*.py tests/*.py
|
run: make release-check
|
||||||
- name: Unit tests
|
- name: Build Debian package
|
||||||
run: python -m unittest discover -s tests -p 'test_*.py'
|
run: make package-deb
|
||||||
- name: Build artifacts
|
- name: Build Arch package inputs
|
||||||
run: python -m build
|
run: make package-arch
|
||||||
|
- name: Upload packaging artifacts
|
||||||
|
uses: actions/upload-artifact@v4
|
||||||
|
with:
|
||||||
|
name: aman-artifacts
|
||||||
|
path: |
|
||||||
|
dist/*.whl
|
||||||
|
dist/*.tar.gz
|
||||||
|
dist/*.deb
|
||||||
|
dist/arch/PKGBUILD
|
||||||
|
|
|
||||||
2
.gitignore
vendored
2
.gitignore
vendored
|
|
@ -4,3 +4,5 @@ __pycache__/
|
||||||
*.pyc
|
*.pyc
|
||||||
outputs/
|
outputs/
|
||||||
models/
|
models/
|
||||||
|
build/
|
||||||
|
dist/
|
||||||
|
|
|
||||||
11
CHANGELOG.md
11
CHANGELOG.md
|
|
@ -4,6 +4,17 @@ All notable changes to Aman will be documented in this file.
|
||||||
|
|
||||||
The format is based on Keep a Changelog and this project follows Semantic Versioning.
|
The format is based on Keep a Changelog and this project follows Semantic Versioning.
|
||||||
|
|
||||||
|
## [Unreleased]
|
||||||
|
|
||||||
|
### Added
|
||||||
|
- Packaging scripts and templates for Debian (`.deb`) and Arch (`PKGBUILD` + source tarball).
|
||||||
|
- Make targets for build/package/release-check workflows.
|
||||||
|
- Persona and distribution policy documentation.
|
||||||
|
|
||||||
|
### Changed
|
||||||
|
- README now documents package-first installation for non-technical users.
|
||||||
|
- Release checklist now includes packaging artifacts.
|
||||||
|
|
||||||
## [0.1.0] - 2026-02-26
|
## [0.1.0] - 2026-02-26
|
||||||
|
|
||||||
### Added
|
### Added
|
||||||
|
|
|
||||||
44
Makefile
44
Makefile
|
|
@ -1,10 +1,13 @@
|
||||||
|
PYTHON ?= python3
|
||||||
CONFIG := $(HOME)/.config/aman/config.json
|
CONFIG := $(HOME)/.config/aman/config.json
|
||||||
|
DIST_DIR := $(CURDIR)/dist
|
||||||
.PHONY: run doctor self-check install sync test check
|
BUILD_DIR := $(CURDIR)/build
|
||||||
|
|
||||||
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
|
RUN_ARGS := $(wordlist 2,$(words $(MAKECMDGOALS)),$(MAKECMDGOALS))
|
||||||
RUN_CONFIG := $(if $(RUN_ARGS),$(abspath $(firstword $(RUN_ARGS))),$(CONFIG))
|
RUN_CONFIG := $(if $(RUN_ARGS),$(abspath $(firstword $(RUN_ARGS))),$(CONFIG))
|
||||||
|
|
||||||
|
.PHONY: run doctor self-check sync test check build package package-deb package-arch release-check install-local install-service install clean-dist clean-build clean
|
||||||
|
|
||||||
ifneq ($(filter run,$(firstword $(MAKECMDGOALS))),)
|
ifneq ($(filter run,$(firstword $(MAKECMDGOALS))),)
|
||||||
.PHONY: $(RUN_ARGS)
|
.PHONY: $(RUN_ARGS)
|
||||||
$(RUN_ARGS):
|
$(RUN_ARGS):
|
||||||
|
|
@ -24,14 +27,43 @@ sync:
|
||||||
uv sync
|
uv sync
|
||||||
|
|
||||||
test:
|
test:
|
||||||
python3 -m unittest discover -s tests -p 'test_*.py'
|
$(PYTHON) -m unittest discover -s tests -p 'test_*.py'
|
||||||
|
|
||||||
check:
|
check:
|
||||||
python3 -m py_compile src/*.py
|
$(PYTHON) -m py_compile src/*.py
|
||||||
$(MAKE) test
|
$(MAKE) test
|
||||||
|
|
||||||
install:
|
build:
|
||||||
uv pip install --user .
|
$(PYTHON) -m build --no-isolation
|
||||||
|
|
||||||
|
package: package-deb package-arch
|
||||||
|
|
||||||
|
package-deb:
|
||||||
|
./scripts/package_deb.sh
|
||||||
|
|
||||||
|
package-arch:
|
||||||
|
./scripts/package_arch.sh
|
||||||
|
|
||||||
|
release-check:
|
||||||
|
$(PYTHON) -m py_compile src/*.py tests/*.py
|
||||||
|
$(MAKE) test
|
||||||
|
$(MAKE) build
|
||||||
|
|
||||||
|
install-local:
|
||||||
|
$(PYTHON) -m pip install --user ".[x11]"
|
||||||
|
|
||||||
|
install-service:
|
||||||
|
mkdir -p $(HOME)/.config/systemd/user
|
||||||
cp systemd/aman.service $(HOME)/.config/systemd/user/aman.service
|
cp systemd/aman.service $(HOME)/.config/systemd/user/aman.service
|
||||||
systemctl --user daemon-reload
|
systemctl --user daemon-reload
|
||||||
systemctl --user enable --now aman
|
systemctl --user enable --now aman
|
||||||
|
|
||||||
|
install: install-local install-service
|
||||||
|
|
||||||
|
clean-dist:
|
||||||
|
rm -rf $(DIST_DIR)
|
||||||
|
|
||||||
|
clean-build:
|
||||||
|
rm -rf $(BUILD_DIR)
|
||||||
|
|
||||||
|
clean: clean-build
|
||||||
|
|
|
||||||
123
README.md
123
README.md
|
|
@ -3,23 +3,61 @@
|
||||||
|
|
||||||
Python X11 STT daemon that records audio, runs Whisper, applies local AI cleanup, and injects text.
|
Python X11 STT daemon that records audio, runs Whisper, applies local AI cleanup, and injects text.
|
||||||
|
|
||||||
## Requirements
|
## Target User
|
||||||
|
|
||||||
|
The canonical Aman user is a desktop professional who wants dictation and
|
||||||
|
rewriting features without learning Python tooling.
|
||||||
|
|
||||||
|
- End-user path: native OS package install.
|
||||||
|
- Developer path: Python/uv workflows.
|
||||||
|
|
||||||
|
Persona details and distribution policy are documented in
|
||||||
|
[`docs/persona-and-distribution.md`](docs/persona-and-distribution.md).
|
||||||
|
|
||||||
|
## Install (Recommended)
|
||||||
|
|
||||||
|
End users do not need `uv`.
|
||||||
|
|
||||||
|
### Debian/Ubuntu (`.deb`)
|
||||||
|
|
||||||
|
Download a release artifact and install it:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
sudo apt install ./aman_<version>_<arch>.deb
|
||||||
|
```
|
||||||
|
|
||||||
|
Then enable the user service:
|
||||||
|
|
||||||
|
```bash
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now aman
|
||||||
|
```
|
||||||
|
|
||||||
|
### Arch Linux
|
||||||
|
|
||||||
|
Use the generated packaging inputs (`PKGBUILD` + source tarball) in `dist/arch/`
|
||||||
|
or your own packaging pipeline.
|
||||||
|
|
||||||
|
## Distribution Matrix
|
||||||
|
|
||||||
|
| Channel | Audience | Status |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| Debian package (`.deb`) | End users on Ubuntu/Debian | Canonical |
|
||||||
|
| Arch `PKGBUILD` + source tarball | Arch maintainers/power users | Supported |
|
||||||
|
| Python wheel/sdist | Developers/integrators | Supported |
|
||||||
|
|
||||||
|
## Runtime Dependencies
|
||||||
|
|
||||||
- X11
|
- X11
|
||||||
- `sounddevice` (PortAudio)
|
- PortAudio runtime (`libportaudio2` or distro equivalent)
|
||||||
- `faster-whisper`
|
- GTK3 and AppIndicator runtime (`gtk3`, `libayatana-appindicator3`)
|
||||||
- `llama-cpp-python`
|
- Python GTK and X11 bindings (`python3-gi`/`python-gobject`, `python-xlib`)
|
||||||
- Tray icon deps: `gtk3`, `libayatana-appindicator3`
|
|
||||||
- Python deps (core): `numpy`, `pillow`, `faster-whisper`, `llama-cpp-python`, `sounddevice`
|
|
||||||
- X11 extras: `PyGObject`, `python-xlib`
|
|
||||||
|
|
||||||
System packages (example names): `portaudio`/`libportaudio2`.
|
|
||||||
|
|
||||||
<details>
|
<details>
|
||||||
<summary>Ubuntu/Debian</summary>
|
<summary>Ubuntu/Debian</summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo apt install -y portaudio19-dev libportaudio2 python3-gi gir1.2-gtk-3.0 libayatana-appindicator3-1
|
sudo apt install -y libportaudio2 python3-gi python3-xlib gir1.2-gtk-3.0 libayatana-appindicator3-1
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -28,7 +66,7 @@ sudo apt install -y portaudio19-dev libportaudio2 python3-gi gir1.2-gtk-3.0 liba
|
||||||
<summary>Arch Linux</summary>
|
<summary>Arch Linux</summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo pacman -S --needed portaudio gtk3 libayatana-appindicator
|
sudo pacman -S --needed portaudio gtk3 libayatana-appindicator python-gobject python-xlib
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -37,7 +75,7 @@ sudo pacman -S --needed portaudio gtk3 libayatana-appindicator
|
||||||
<summary>Fedora</summary>
|
<summary>Fedora</summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo dnf install -y portaudio portaudio-devel gtk3 libayatana-appindicator-gtk3
|
sudo dnf install -y portaudio gtk3 libayatana-appindicator-gtk3 python3-gobject python3-xlib
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
@ -46,25 +84,15 @@ sudo dnf install -y portaudio portaudio-devel gtk3 libayatana-appindicator-gtk3
|
||||||
<summary>openSUSE</summary>
|
<summary>openSUSE</summary>
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
sudo zypper install -y portaudio portaudio-devel gtk3 libayatana-appindicator3-1
|
sudo zypper install -y portaudio gtk3 libayatana-appindicator3-1 python3-gobject python3-python-xlib
|
||||||
```
|
```
|
||||||
|
|
||||||
</details>
|
</details>
|
||||||
|
|
||||||
## Python Daemon
|
|
||||||
|
|
||||||
Install Python deps:
|
|
||||||
|
|
||||||
X11 (supported):
|
|
||||||
|
|
||||||
```bash
|
|
||||||
uv sync --extra x11
|
|
||||||
```
|
|
||||||
|
|
||||||
## Quickstart
|
## Quickstart
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv run aman run
|
aman run
|
||||||
```
|
```
|
||||||
|
|
||||||
On first launch, Aman opens a graphical settings window automatically.
|
On first launch, Aman opens a graphical settings window automatically.
|
||||||
|
|
@ -191,15 +219,13 @@ STT hinting:
|
||||||
## systemd user service
|
## systemd user service
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
uv pip install --user .
|
make install-service
|
||||||
cp systemd/aman.service ~/.config/systemd/user/aman.service
|
|
||||||
systemctl --user daemon-reload
|
|
||||||
systemctl --user enable --now aman
|
|
||||||
```
|
```
|
||||||
|
|
||||||
Service notes:
|
Service notes:
|
||||||
|
|
||||||
- The user unit launches `aman` from `PATH`; ensure `~/.local/bin` is present in your user PATH.
|
- The user unit launches `aman` from `PATH`.
|
||||||
|
- Package installs should provide the `aman` command automatically.
|
||||||
- Inspect failures with `systemctl --user status aman` and `journalctl --user -u aman -f`.
|
- Inspect failures with `systemctl --user status aman` and `journalctl --user -u aman -f`.
|
||||||
|
|
||||||
## Usage
|
## Usage
|
||||||
|
|
@ -228,21 +254,50 @@ AI processing:
|
||||||
- Default local llama.cpp model.
|
- Default local llama.cpp model.
|
||||||
- Optional external API provider through `llm.provider=external_api`.
|
- Optional external API provider through `llm.provider=external_api`.
|
||||||
|
|
||||||
|
Build and packaging (maintainers):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
make build
|
||||||
|
make package
|
||||||
|
make package-deb
|
||||||
|
make package-arch
|
||||||
|
make release-check
|
||||||
|
```
|
||||||
|
|
||||||
|
`make package-deb` installs Python dependencies while creating the package.
|
||||||
|
For offline packaging, set `AMAN_WHEELHOUSE_DIR` to a directory containing the
|
||||||
|
required wheels.
|
||||||
|
|
||||||
Control:
|
Control:
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
make run
|
make run
|
||||||
|
make run config.example.json
|
||||||
make doctor
|
make doctor
|
||||||
make self-check
|
make self-check
|
||||||
make check
|
make check
|
||||||
```
|
```
|
||||||
|
|
||||||
CLI (internal/support fallback, mostly for automation/tests):
|
Developer setup (optional, `uv` workflow):
|
||||||
|
|
||||||
```bash
|
```bash
|
||||||
|
uv sync --extra x11
|
||||||
uv run aman run --config ~/.config/aman/config.json
|
uv run aman run --config ~/.config/aman/config.json
|
||||||
uv run aman doctor --config ~/.config/aman/config.json --json
|
```
|
||||||
uv run aman self-check --config ~/.config/aman/config.json --json
|
|
||||||
uv run aman version
|
Developer setup (optional, `pip` workflow):
|
||||||
uv run aman init --config ~/.config/aman/config.json --force
|
|
||||||
|
```bash
|
||||||
|
make install-local
|
||||||
|
aman run --config ~/.config/aman/config.json
|
||||||
|
```
|
||||||
|
|
||||||
|
CLI (internal/support fallback):
|
||||||
|
|
||||||
|
```bash
|
||||||
|
aman run --config ~/.config/aman/config.json
|
||||||
|
aman doctor --config ~/.config/aman/config.json --json
|
||||||
|
aman self-check --config ~/.config/aman/config.json --json
|
||||||
|
aman version
|
||||||
|
aman init --config ~/.config/aman/config.json --force
|
||||||
```
|
```
|
||||||
|
|
|
||||||
50
docs/persona-and-distribution.md
Normal file
50
docs/persona-and-distribution.md
Normal file
|
|
@ -0,0 +1,50 @@
|
||||||
|
# Aman Target Persona and Distribution Strategy
|
||||||
|
|
||||||
|
## Primary Persona: Desktop Professional
|
||||||
|
|
||||||
|
This is the canonical Aman user.
|
||||||
|
|
||||||
|
- Uses Linux desktop daily (X11 today), mostly Ubuntu/Debian.
|
||||||
|
- Wants fast dictation and rewriting without learning Python tooling.
|
||||||
|
- Prefers GUI setup and tray usage over CLI.
|
||||||
|
- Expects normal install/uninstall/update behavior from system packages.
|
||||||
|
|
||||||
|
Design implications:
|
||||||
|
|
||||||
|
- End-user install path must not require `uv`.
|
||||||
|
- Runtime defaults should work with minimal input.
|
||||||
|
- Documentation should prioritize package install first.
|
||||||
|
|
||||||
|
## Secondary Persona: Power User
|
||||||
|
|
||||||
|
- Comfortable with CLI, package internals, and model customization.
|
||||||
|
- Uses advanced config, external APIs, or custom models.
|
||||||
|
- Can run diagnostics and debug logs when needed.
|
||||||
|
|
||||||
|
Design implications:
|
||||||
|
|
||||||
|
- Keep Make and Python workflows available.
|
||||||
|
- Keep explicit expert-mode knobs in settings and config.
|
||||||
|
- Keep docs for development separate from standard install docs.
|
||||||
|
|
||||||
|
## Supported Distribution Path (Current)
|
||||||
|
|
||||||
|
Tiered distribution model:
|
||||||
|
|
||||||
|
1. Canonical: Debian package (`.deb`) for Ubuntu/Debian users.
|
||||||
|
2. Secondary: Arch package inputs (`PKGBUILD` + source tarball).
|
||||||
|
3. Developer: wheel/sdist from `python -m build`.
|
||||||
|
|
||||||
|
## Out of Scope for Initial Packaging
|
||||||
|
|
||||||
|
- Wayland production support.
|
||||||
|
- Flatpak/snap-first distribution.
|
||||||
|
- Cross-platform desktop installers outside Linux.
|
||||||
|
|
||||||
|
## Release and Support Policy
|
||||||
|
|
||||||
|
- App versioning follows SemVer (`0.y.z` until API/UX stabilizes).
|
||||||
|
- Config schema versioning is independent (`config_version` in config).
|
||||||
|
- Packaging docs must always separate:
|
||||||
|
- End-user install path (package-first)
|
||||||
|
- Developer setup path (uv/pip/build workflows)
|
||||||
|
|
@ -2,11 +2,16 @@
|
||||||
|
|
||||||
1. Update `CHANGELOG.md` with final release notes.
|
1. Update `CHANGELOG.md` with final release notes.
|
||||||
2. Bump `project.version` in `pyproject.toml`.
|
2. Bump `project.version` in `pyproject.toml`.
|
||||||
3. Run:
|
3. Run quality and build gates:
|
||||||
- `python3 -m py_compile src/*.py tests/*.py`
|
- `make release-check`
|
||||||
- `python3 -m unittest discover -s tests -p 'test_*.py'`
|
4. Build packaging artifacts:
|
||||||
4. Build artifacts:
|
- `make package`
|
||||||
- `python3 -m build`
|
5. Verify artifacts:
|
||||||
5. Tag release:
|
- `dist/*.whl`
|
||||||
|
- `dist/*.tar.gz`
|
||||||
|
- `dist/*.deb`
|
||||||
|
- `dist/arch/PKGBUILD`
|
||||||
|
6. Tag release:
|
||||||
- `git tag vX.Y.Z`
|
- `git tag vX.Y.Z`
|
||||||
- `git push origin vX.Y.Z`
|
- `git push origin vX.Y.Z`
|
||||||
|
7. Publish release and upload package artifacts from `dist/`.
|
||||||
|
|
|
||||||
33
packaging/arch/PKGBUILD.in
Normal file
33
packaging/arch/PKGBUILD.in
Normal file
|
|
@ -0,0 +1,33 @@
|
||||||
|
# Maintainer: Aman Maintainers <maintainers@example.com>
|
||||||
|
pkgname=aman
|
||||||
|
pkgver=__VERSION__
|
||||||
|
pkgrel=1
|
||||||
|
pkgdesc="Local amanuensis daemon for X11 desktops"
|
||||||
|
arch=('x86_64')
|
||||||
|
url="https://github.com/example/aman"
|
||||||
|
license=('MIT')
|
||||||
|
depends=('python' 'python-pip' 'python-setuptools' 'portaudio' 'gtk3' 'libayatana-appindicator' 'python-gobject' 'python-xlib')
|
||||||
|
makedepends=('python-build' 'python-installer' 'python-wheel')
|
||||||
|
source=("__TARBALL_NAME__")
|
||||||
|
sha256sums=('__TARBALL_SHA256__')
|
||||||
|
|
||||||
|
prepare() {
|
||||||
|
cd "${srcdir}/aman-${pkgver}"
|
||||||
|
python -m build --wheel
|
||||||
|
}
|
||||||
|
|
||||||
|
package() {
|
||||||
|
cd "${srcdir}/aman-${pkgver}"
|
||||||
|
install -dm755 "${pkgdir}/opt/aman"
|
||||||
|
python -m venv --system-site-packages "${pkgdir}/opt/aman/venv"
|
||||||
|
"${pkgdir}/opt/aman/venv/bin/python" -m pip install --upgrade pip
|
||||||
|
"${pkgdir}/opt/aman/venv/bin/python" -m pip install "dist/aman-${pkgver}-"*.whl
|
||||||
|
|
||||||
|
install -Dm755 /dev/stdin "${pkgdir}/usr/bin/aman" <<'EOF'
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
exec /opt/aman/venv/bin/aman "$@"
|
||||||
|
EOF
|
||||||
|
|
||||||
|
install -Dm644 "systemd/aman.service" "${pkgdir}/usr/lib/systemd/user/aman.service"
|
||||||
|
}
|
||||||
11
packaging/deb/control.in
Normal file
11
packaging/deb/control.in
Normal file
|
|
@ -0,0 +1,11 @@
|
||||||
|
Package: __PACKAGE_NAME__
|
||||||
|
Version: __VERSION__
|
||||||
|
Section: utils
|
||||||
|
Priority: optional
|
||||||
|
Architecture: __ARCH__
|
||||||
|
Maintainer: Aman Maintainers <maintainers@example.com>
|
||||||
|
Depends: python3, python3-venv, python3-gi, python3-xlib, libportaudio2, gir1.2-gtk-3.0, 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
|
||||||
|
first-run graphical settings UI.
|
||||||
10
packaging/deb/postinst
Executable file
10
packaging/deb/postinst
Executable file
|
|
@ -0,0 +1,10 @@
|
||||||
|
#!/bin/sh
|
||||||
|
set -eu
|
||||||
|
|
||||||
|
cat <<'EOF'
|
||||||
|
Aman installed.
|
||||||
|
|
||||||
|
To enable auto-start for your user session:
|
||||||
|
systemctl --user daemon-reload
|
||||||
|
systemctl --user enable --now aman
|
||||||
|
EOF
|
||||||
44
scripts/package_arch.sh
Executable file
44
scripts/package_arch.sh
Executable file
|
|
@ -0,0 +1,44 @@
|
||||||
|
#!/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 git
|
||||||
|
require_command sha256sum
|
||||||
|
|
||||||
|
VERSION="$(project_version)"
|
||||||
|
PACKAGE_NAME="$(project_name)"
|
||||||
|
ARCH_DIST_DIR="${DIST_DIR}/arch"
|
||||||
|
TARBALL_NAME="${PACKAGE_NAME}-${VERSION}.tar.gz"
|
||||||
|
TARBALL_PATH="${ARCH_DIST_DIR}/${TARBALL_NAME}"
|
||||||
|
PKGBUILD_PATH="${ARCH_DIST_DIR}/PKGBUILD"
|
||||||
|
|
||||||
|
mkdir -p "${ARCH_DIST_DIR}"
|
||||||
|
|
||||||
|
git -C "${ROOT_DIR}" archive \
|
||||||
|
--format=tar.gz \
|
||||||
|
--prefix="${PACKAGE_NAME}-${VERSION}/" \
|
||||||
|
HEAD \
|
||||||
|
> "${TARBALL_PATH}"
|
||||||
|
|
||||||
|
TARBALL_SHA256="$(sha256sum "${TARBALL_PATH}" | awk '{print $1}')"
|
||||||
|
|
||||||
|
render_template \
|
||||||
|
"${ROOT_DIR}/packaging/arch/PKGBUILD.in" \
|
||||||
|
"${PKGBUILD_PATH}" \
|
||||||
|
"VERSION=${VERSION}" \
|
||||||
|
"TARBALL_NAME=${TARBALL_NAME}" \
|
||||||
|
"TARBALL_SHA256=${TARBALL_SHA256}"
|
||||||
|
|
||||||
|
echo "generated ${PKGBUILD_PATH}"
|
||||||
|
echo "generated ${TARBALL_PATH}"
|
||||||
|
|
||||||
|
if [[ "${BUILD_ARCH_PACKAGE:-0}" == "1" ]]; then
|
||||||
|
require_command makepkg
|
||||||
|
(
|
||||||
|
cd "${ARCH_DIST_DIR}"
|
||||||
|
makepkg -f
|
||||||
|
)
|
||||||
|
fi
|
||||||
84
scripts/package_common.sh
Executable file
84
scripts/package_common.sh
Executable file
|
|
@ -0,0 +1,84 @@
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
|
||||||
|
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
|
||||||
|
ROOT_DIR="$(cd "${SCRIPT_DIR}/.." && pwd)"
|
||||||
|
DIST_DIR="${ROOT_DIR}/dist"
|
||||||
|
BUILD_DIR="${ROOT_DIR}/build"
|
||||||
|
APP_NAME="aman"
|
||||||
|
|
||||||
|
mkdir -p "${DIST_DIR}" "${BUILD_DIR}"
|
||||||
|
|
||||||
|
require_command() {
|
||||||
|
local cmd="$1"
|
||||||
|
if command -v "${cmd}" >/dev/null 2>&1; then
|
||||||
|
return
|
||||||
|
fi
|
||||||
|
echo "missing required command: ${cmd}" >&2
|
||||||
|
exit 1
|
||||||
|
}
|
||||||
|
|
||||||
|
project_version() {
|
||||||
|
require_command python3
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
text = Path("pyproject.toml").read_text(encoding="utf-8")
|
||||||
|
match = re.search(r'(?m)^version\s*=\s*"([^"]+)"\s*$', text)
|
||||||
|
if not match:
|
||||||
|
raise SystemExit("project version not found in pyproject.toml")
|
||||||
|
print(match.group(1))
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
project_name() {
|
||||||
|
require_command python3
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
text = Path("pyproject.toml").read_text(encoding="utf-8")
|
||||||
|
match = re.search(r'(?m)^name\s*=\s*"([^"]+)"\s*$', text)
|
||||||
|
if not match:
|
||||||
|
raise SystemExit("project name not found in pyproject.toml")
|
||||||
|
print(match.group(1))
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
build_wheel() {
|
||||||
|
require_command python3
|
||||||
|
python3 -m build --wheel --no-isolation
|
||||||
|
}
|
||||||
|
|
||||||
|
latest_wheel_path() {
|
||||||
|
require_command python3
|
||||||
|
python3 - <<'PY'
|
||||||
|
from pathlib import Path
|
||||||
|
import re
|
||||||
|
|
||||||
|
text = Path("pyproject.toml").read_text(encoding="utf-8")
|
||||||
|
name_match = re.search(r'(?m)^name\s*=\s*"([^"]+)"\s*$', text)
|
||||||
|
version_match = re.search(r'(?m)^version\s*=\s*"([^"]+)"\s*$', text)
|
||||||
|
if not name_match or not version_match:
|
||||||
|
raise SystemExit("project metadata not found in pyproject.toml")
|
||||||
|
name = name_match.group(1).replace("-", "_")
|
||||||
|
version = version_match.group(1)
|
||||||
|
candidates = sorted(Path("dist").glob(f"{name}-{version}-*.whl"))
|
||||||
|
if not candidates:
|
||||||
|
raise SystemExit("no wheel artifact found in dist/")
|
||||||
|
print(candidates[-1])
|
||||||
|
PY
|
||||||
|
}
|
||||||
|
|
||||||
|
render_template() {
|
||||||
|
local template_path="$1"
|
||||||
|
local output_path="$2"
|
||||||
|
shift 2
|
||||||
|
cp "${template_path}" "${output_path}"
|
||||||
|
for mapping in "$@"; do
|
||||||
|
local key="${mapping%%=*}"
|
||||||
|
local value="${mapping#*=}"
|
||||||
|
sed -i "s|__${key}__|${value}|g" "${output_path}"
|
||||||
|
done
|
||||||
|
}
|
||||||
64
scripts/package_deb.sh
Executable file
64
scripts/package_deb.sh
Executable file
|
|
@ -0,0 +1,64 @@
|
||||||
|
#!/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 dpkg-deb
|
||||||
|
require_command python3
|
||||||
|
|
||||||
|
VERSION="$(project_version)"
|
||||||
|
PACKAGE_NAME="$(project_name)"
|
||||||
|
ARCH="${DEB_ARCH:-}"
|
||||||
|
if [[ -z "${ARCH}" ]]; then
|
||||||
|
if command -v dpkg >/dev/null 2>&1; then
|
||||||
|
ARCH="$(dpkg --print-architecture)"
|
||||||
|
else
|
||||||
|
ARCH="amd64"
|
||||||
|
fi
|
||||||
|
fi
|
||||||
|
|
||||||
|
build_wheel
|
||||||
|
WHEEL_PATH="$(latest_wheel_path)"
|
||||||
|
|
||||||
|
STAGE_DIR="${BUILD_DIR}/deb/${PACKAGE_NAME}_${VERSION}_${ARCH}"
|
||||||
|
PACKAGE_BASENAME="${PACKAGE_NAME}_${VERSION}_${ARCH}"
|
||||||
|
DEB_PATH="${DIST_DIR}/${PACKAGE_BASENAME}.deb"
|
||||||
|
VENV_DIR="${STAGE_DIR}/opt/${PACKAGE_NAME}/venv"
|
||||||
|
PIP_ARGS=()
|
||||||
|
if [[ -n "${AMAN_WHEELHOUSE_DIR:-}" ]]; then
|
||||||
|
PIP_ARGS+=(--no-index --find-links "${AMAN_WHEELHOUSE_DIR}")
|
||||||
|
fi
|
||||||
|
|
||||||
|
rm -rf "${STAGE_DIR}"
|
||||||
|
mkdir -p "${STAGE_DIR}/DEBIAN"
|
||||||
|
mkdir -p "${STAGE_DIR}/usr/bin"
|
||||||
|
mkdir -p "${STAGE_DIR}/usr/lib/systemd/user"
|
||||||
|
mkdir -p "${STAGE_DIR}/opt/${PACKAGE_NAME}"
|
||||||
|
|
||||||
|
render_template \
|
||||||
|
"${ROOT_DIR}/packaging/deb/control.in" \
|
||||||
|
"${STAGE_DIR}/DEBIAN/control" \
|
||||||
|
"PACKAGE_NAME=${PACKAGE_NAME}" \
|
||||||
|
"VERSION=${VERSION}" \
|
||||||
|
"ARCH=${ARCH}"
|
||||||
|
|
||||||
|
cp "${ROOT_DIR}/packaging/deb/postinst" "${STAGE_DIR}/DEBIAN/postinst"
|
||||||
|
chmod 0755 "${STAGE_DIR}/DEBIAN/postinst"
|
||||||
|
|
||||||
|
python3 -m venv --system-site-packages "${VENV_DIR}"
|
||||||
|
"${VENV_DIR}/bin/python" -m pip install "${PIP_ARGS[@]}" "${WHEEL_PATH}"
|
||||||
|
|
||||||
|
cat >"${STAGE_DIR}/usr/bin/${PACKAGE_NAME}" <<EOF
|
||||||
|
#!/usr/bin/env bash
|
||||||
|
set -euo pipefail
|
||||||
|
exec /opt/${PACKAGE_NAME}/venv/bin/${PACKAGE_NAME} "\$@"
|
||||||
|
EOF
|
||||||
|
chmod 0755 "${STAGE_DIR}/usr/bin/${PACKAGE_NAME}"
|
||||||
|
|
||||||
|
cp "${ROOT_DIR}/systemd/aman.service" "${STAGE_DIR}/usr/lib/systemd/user/aman.service"
|
||||||
|
|
||||||
|
rm -f "${DEB_PATH}"
|
||||||
|
dpkg-deb --build --root-owner-group "${STAGE_DIR}" "${DEB_PATH}"
|
||||||
|
echo "built ${DEB_PATH}"
|
||||||
Loading…
Add table
Add a link
Reference in a new issue