build(dev): enhance local development environment and setup workflow (#358)

Improve the developer experience by providing structured setup scripts,
VS Code workspace configurations, and detailed documentation.

- Add VS Code workspace configuration and recommended settings
- Update README.md with system requirements and VS Code setup instructions
- Refactor `script/setup` to include Python 3.11+ validation and system dependency checks
- Add option to skip PulseAudio wait in `docker-entrypoint.sh` for local development
- Update `.gitignore` to ensure consistent version file handling
This commit is contained in:
Florian Asche authored and GitHub committed 2026-07-20 17:38:52 +02:00
1 parent a66c0e4f28
commit 486ff4bb53
9 files changed
+199 -26

No files matched your search

+1 -1
View File
@@ -23,4 +23,4 @@ venv/
.env .env
dev.env dev.env
version.txt version.txt
version_githash.txt version_githash.txt
+7
View File
@@ -0,0 +1,7 @@
{
"recommendations": [
"ms-python.python",
"ms-python.vscode-pylance",
"kilo.kilocode"
]
}
+14
View File
@@ -0,0 +1,14 @@
{
"version": "0.2.0",
"configurations": [
{
"name": "Run Linux Voice Assistant",
"type": "python",
"request": "launch",
"program": "${workspaceFolder}/linux_voice_assistant/__main__.py",
"console": "integratedTerminal",
"justMyCode": true,
"envFile": "${workspaceFolder}/.env"
}
]
}
+13
View File
@@ -0,0 +1,13 @@
{
"python.defaultInterpreterPath": "./.venv/bin/python",
"python.testing.pytestEnabled": true,
"python.testing.pytestArgs": ["tests"],
"python.testing.pytestAutoInclude": false,
"python.linting.enabled": true,
"python.linting.flake8Enabled": true,
"python.linting.mypyEnabled": true,
"python.formatting.provider": "none",
"python.terminal.activateEnvironment": true,
"python.terminal.executeInShell": true,
"python.pythonPath": "./.venv/bin/python"
}
+62
View File
@@ -0,0 +1,62 @@
{
"version": "2.0.0",
"tasks": [
{
"label": "Setup (incl. dev dependencies)",
"type": "shell",
"command": "./script/setup --dev",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new",
"clear": true
},
"problemMatcher": []
},
{
"label": "Run Linter",
"type": "shell",
"command": "./script/lint",
"group": "build",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
},
"problemMatcher": []
},
{
"label": "Run Tests",
"type": "shell",
"command": "./script/tests",
"group": "test",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
},
"problemMatcher": []
},
{
"label": "Run Application (Docker)",
"type": "shell",
"command": "docker-compose",
"args": ["-f", "docker-compose.dev.yml", "up"],
"group": "none",
"presentation": {
"echo": true,
"reveal": "always",
"focus": false,
"panel": "new"
},
"problemMatcher": [],
"options": {
"envFile": "${workspaceFolder}/.env"
}
}
]
}
+30
View File
@@ -131,6 +131,36 @@ The documentation for the build process can be found in the [GitHub Actions Work
## Development ## Development
### System Requirements
**System packages (Linux):**
- libmpv-dev (`sudo apt install libmpv-dev`)
- PulseAudio/PipeWire (`sudo apt install pulseaudio pipewire`)
- ALSA utils (`sudo apt install alsa-utils`)
**Python:**
- Python 3.11+ must be installed on your system (`python3 --version` should show 3.11+)
- On Ubuntu/Debian: `sudo apt install python3.11 python3.11-venv python3.11-dev`
### VS Code Setup
VS Code development uses a local Python virtual environment (`.venv/`):
1. Open workspace in VS Code
2. Accept Workspace Trust (bottom-left status bar)
3. Install recommended extensions when prompted:
- `ms-python.python` - Python language support
- `ms-python.vscode-pylance` - Python language server
- `kilo.kilocode` - Kilo AI assistant
4. **Terminal** → **New Terminal** (or `` Ctrl+` ``) - Opens integrated terminal (uses .venv when available)
5. Run `./script/setup --dev` to create `.venv` and install dev dependencies
6. VS Code automatically detects `.venv/bin/python` as the Python interpreter
**Available VS Code Tasks:**
- `Ctrl+Shift+B` - Run Setup or Linter
- `Ctrl+Shift+T` - Run Tests
- `Ctrl+Shift+P` → "Tasks: Run Task" - Show all tasks (Setup, Linter, Tests, Run App)
### Code Quality Checks ### Code Quality Checks
The project uses the following tools to ensure code quality: The project uses the following tools to ensure code quality:
+20 -18
View File
@@ -145,26 +145,28 @@ fi
### Wait for PulseAudio ### Wait for PulseAudio
# Wait for PulseAudio to be available before starting the application # Skip wait if SKIP_PULSE_AUDIO_WAIT is set (for local development)
CP_MAX_RETRIES=30 if [ "$SKIP_PULSE_AUDIO_WAIT" != "1" ]; then
CP_RETRY_DELAY=1 CP_MAX_RETRIES=30
### while maybe besser? CP_RETRY_DELAY=1
echo "Checking PulseAudio service status..." ### while maybe besser?
for i in $(seq 1 $CP_MAX_RETRIES); do echo "Checking PulseAudio service status..."
# Check if PulseAudio is running for i in $(seq 1 $CP_MAX_RETRIES); do
if pactl info >/dev/null 2>&1; then # Check if PulseAudio is running
echo "✅ PulseAudio is running" if pactl info >/dev/null 2>&1; then
break echo "✅ PulseAudio is running"
fi break
fi
if [ $i -eq $CP_MAX_RETRIES ]; then if [ $i -eq $CP_MAX_RETRIES ]; then
echo "❌ PulseAudio did not start after $CP_MAX_RETRIES seconds" echo "❌ PulseAudio did not start after $CP_MAX_RETRIES seconds"
exit 2 exit 2
fi fi
echo "⏳ PulseAudio not running yet, retrying in $CP_RETRY_DELAY s..." echo "⏳ PulseAudio not running yet, retrying in $CP_RETRY_DELAY s..."
sleep $CP_RETRY_DELAY sleep $CP_RETRY_DELAY
done done
fi
### Start application ### Start application
+7
View File
@@ -0,0 +1,7 @@
{
"folders": [
{
"path": "."
}
]
}
+45 -7
View File
@@ -1,8 +1,10 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
import argparse import argparse
import subprocess import subprocess
import venv
import os import os
import shutil
import sys
import ctypes.util
from pathlib import Path from pathlib import Path
_DIR = Path(__file__).parent _DIR = Path(__file__).parent
@@ -15,10 +17,47 @@ parser.add_argument("--cxxflags", type=str, help="CXXFLAGS for compilation (defa
parser.add_argument("--makeflags", type=str, help="MAKEFLAGS for compilation (default: -j1)") parser.add_argument("--makeflags", type=str, help="MAKEFLAGS for compilation (default: -j1)")
args = parser.parse_args() args = parser.parse_args()
# Find Python 3.11+ interpreter
def find_python_311():
for candidate in ["python3.13", "python3.12", "python3.11"]:
path = shutil.which(candidate)
if path:
result = subprocess.run([path, "--version"], capture_output=True, text=True)
version_str = result.stdout.strip() or result.stderr.strip()
ver_parts = version_str.replace("Python ", "").split(".")
if len(ver_parts) >= 2 and int(ver_parts[0]) == 3 and int(ver_parts[1]) >= 11:
return path
return None
python_311_exe = find_python_311()
if not python_311_exe:
print("ERROR: Python 3.11+ is required but not found on this system.")
print(" Ubuntu/Debian: sudo apt install python3.11 python3.11-venv python3.11-dev")
print(" macOS: brew install python@3.11")
sys.exit(1)
print(f"Using Python: {python_311_exe}")
subprocess.run([python_311_exe, "--version"])
# Check system dependencies (warning only, CI installs them separately)
missing = []
try:
if not ctypes.util.find_library("mpv"):
missing.append("libmpv-dev")
if not ctypes.util.find_library("asound"):
missing.append("libasound2-dev")
except Exception:
pass # Ignore errors in environments without ctypes
if missing:
print(f"\nWARNING: Missing system libraries (required for audio): {', '.join(missing)}")
print("For Docker/local dev: sudo apt install libmpv-dev pulseaudio pipewire alsa-utils")
# Create virtual environment # Create virtual environment
builder = venv.EnvBuilder(with_pip=True) if _VENV_DIR.exists():
context = builder.ensure_directories(_VENV_DIR) shutil.rmtree(_VENV_DIR)
builder.create(_VENV_DIR)
subprocess.check_call([python_311_exe, "-m", "venv", str(_VENV_DIR), "--symlinks"])
# Set environment variables # Set environment variables
env = {} env = {}
@@ -28,17 +67,16 @@ if args.makeflags:
env["MAKEFLAGS"] = args.makeflags env["MAKEFLAGS"] = args.makeflags
# Upgrade dependencies # Upgrade dependencies
pip = [context.env_exe, "-m", "pip"] pip = [str(_VENV_DIR / "bin" / "python"), "-m", "pip"]
subprocess.check_call(pip + ["install", "--upgrade", "pip"]) subprocess.check_call(pip + ["install", "--upgrade", "pip"])
subprocess.check_call(pip + ["install", "--upgrade", "setuptools", "wheel"]) subprocess.check_call(pip + ["install", "--upgrade", "setuptools", "wheel"])
# Install requirements # Install requirements
subprocess.check_call(pip + ["install", "-e", str(_PROGRAM_DIR)]) subprocess.check_call(pip + ["install", "-e", str(_PROGRAM_DIR)])
# Install dev requirements # Install dev requirements
if args.dev: if args.dev:
subprocess.check_call( subprocess.check_call(
pip + ["install", "-e", f"{_PROGRAM_DIR}[dev]"], pip + ["install", "-e", f"{_PROGRAM_DIR}[dev]"],
env={**dict(os.environ), **env} env={**dict(os.environ), **env}
) )