From b7d223673e3117f80d96a127ff77d431b0e70289 Mon Sep 17 00:00:00 2001 From: thanhnv Date: Tue, 11 Aug 2026 17:51:21 +0700 Subject: [PATCH] chore(install): add reproducible local setup --- README.md | 11 ++-- docs/installation-log-2026-08-11.md | 71 ++++++++++++++++++++++ docs/installation.md | 92 +++++++++++++++++++++++++++++ requirements-optional.txt | 4 ++ requirements.txt | 16 +++++ scripts/install_local.sh | 52 ++++++++++++++++ scripts/run_local.sh | 25 ++++++++ 7 files changed, 267 insertions(+), 4 deletions(-) create mode 100644 docs/installation-log-2026-08-11.md create mode 100644 docs/installation.md create mode 100644 requirements-optional.txt create mode 100644 requirements.txt create mode 100755 scripts/install_local.sh create mode 100755 scripts/run_local.sh diff --git a/README.md b/README.md index 553cf59..fc7b88d 100644 --- a/README.md +++ b/README.md @@ -6,20 +6,23 @@ The Cowork Team owns this product and its stable branch. The FSG AI Core Team co ## Quick start -The imported application is a Python/PySide6 package. Run it from the directory that contains `cowork_local`: +Install the Python/PySide6 desktop application into an isolated environment: ```bash -python -m cowork_local +./scripts/install_local.sh +./scripts/run_local.sh ``` -The source snapshot does not include a complete runtime dependency manifest. Use the Cowork Team's supported runtime environment until that packaging contract is documented. The reliable automated test surface currently checked by CI is: +The installer also creates `~/.local/bin/cowork-local` when that path is available. See [docs/installation.md](docs/installation.md) for prerequisites, configuration, verification, optional document tooling, and platform limitations. + +The reliable automated test surface checked by CI is: ```bash python -m pip install -r cowork_local/requirements-test.txt python -m pytest cowork_local/tests -q ``` -When already inside this repository, run `python -m pytest tests -q`. +When already inside this repository, run `.venv/bin/python -m pytest tests -q`. Configuration and runtime data live under `~/.cowork_local/`. Provider keys and local unlock codes must be supplied through environment variables or an approved secret manager; see `.env.example`. diff --git a/docs/installation-log-2026-08-11.md b/docs/installation-log-2026-08-11.md new file mode 100644 index 0000000..5abebae --- /dev/null +++ b/docs/installation-log-2026-08-11.md @@ -0,0 +1,71 @@ +# Installation Record — 2026-08-11 + +This record captures the successful local source installation performed on an Apple Silicon macOS host. It contains no credentials or machine-specific home-directory path. + +## Environment + +```text +Architecture: arm64 +Python: 3.11.15 +Installer: uv 0.11.8 +Virtual environment: /.venv +Launcher: ~/.local/bin/cowork-local +Installed size: approximately 1.3 GB +``` + +## Steps executed + +From the repository root: + +```bash +git switch -c chore/local-installation +./scripts/install_local.sh +``` + +The installer selected Homebrew Python 3.11, created `.venv`, installed the runtime and test requirement sets, and created the user launcher without replacing any existing path. + +Key resolved packages: + +```text +PySide6==6.11.1 +anyio==4.14.2 +holidays==0.102 +keyring==25.7.0 +mcp==1.29.0 +msal==1.37.0 +networkx==3.6.1 +openpyxl==3.1.5 +psutil==7.2.2 +pydantic==2.13.4 +Pygments==2.20.0 +python-pptx==1.0.2 +requests==2.34.2 +pytest==9.1.1 +``` + +## Verification executed + +```bash +.venv/bin/python -m pytest tests -q +.venv/bin/python -c "import PySide6, pydantic, requests" +``` + +Additional verification imported every module under `cowork_local` with `QT_QPA_PLATFORM=offscreen`, then started the real application entry point with a 1.5-second automatic Qt shutdown. + +Results: + +```text +Tests: 81 passed +Runtime dependency imports: PASS +Application module imports: 0 failures +Headless startup: exit 0 +Launcher in PATH: PASS +``` + +Qt emitted one non-fatal warning while populating the fallback alias for the missing `Sans Serif` family. It did not affect application startup. + +## Optional capabilities + +`pypdf` and `opendataloader-pdf` were intentionally not installed in the baseline environment. Install `requirements-optional.txt` when advanced PDF extraction is required. A `soffice` command was detected on the installation host, but LibreOffice availability remains an external host prerequisite rather than a Python dependency. + +No provider credential, unlock code, token, or application state was written to the repository during installation or verification. diff --git a/docs/installation.md b/docs/installation.md new file mode 100644 index 0000000..3bf42af --- /dev/null +++ b/docs/installation.md @@ -0,0 +1,92 @@ +# Local Installation + +This guide installs the source checkout into an isolated virtual environment. It does not modify the system Python and does not store provider credentials in the repository. + +## Supported baseline + +- Python 3.11 or newer; CI and this installation use Python 3.11. +- macOS, Linux, or Windows for the cross-platform desktop UI. +- Windows-only integrations such as AppContainer, Windows Sandbox, Outlook COM, and Office COM conversion remain unavailable on macOS/Linux and degrade gracefully. + +## Automated installation + +From the repository root: + +```bash +./scripts/install_local.sh +``` + +The installer: + +1. selects `python3.11` when available; +2. creates `.venv/` inside the repository; +3. installs `requirements.txt` and `requirements-test.txt` with `uv` when available, otherwise `pip`; +4. creates `~/.local/bin/cowork-local` when that path is free; +5. never replaces an existing launcher or global Python package. + +Override the interpreter when necessary: + +```bash +COWORK_PYTHON=/path/to/python3.11 ./scripts/install_local.sh +``` + +## Run + +Use the repository launcher: + +```bash +./scripts/run_local.sh +``` + +If `~/.local/bin` is in `PATH`, use: + +```bash +cowork-local +``` + +The launcher changes to the package's parent directory and runs `python -m cowork_local`, which is required by the current source layout. + +## Configure credentials + +Application state is stored outside Git under `~/.cowork_local/`. Export only the variables you need; `.env.example` documents the supported names. For example: + +```bash +export OPENAI_API_KEY="..." +export OPENAI_BASE_URL="https://your-approved-gateway/v1" +export COWORK_SANDBOX_PASSWORD="..." +export COWORK_MS365_UNLOCK_CODE="..." +cowork-local +``` + +Do not put real values in `.env.example` or any tracked file. + +## Verify + +```bash +.venv/bin/python -m pytest tests -q +.venv/bin/python -c "import PySide6, pydantic, requests; print('runtime imports OK')" +``` + +A headless startup smoke test suitable for CI or remote shells is documented in the verification section below: + +```bash +cd .. +QT_QPA_PLATFORM=offscreen cowork_local/.venv/bin/python -c \ + "from PySide6.QtCore import QTimer; from PySide6.QtWidgets import QApplication; app=QApplication([]); QTimer.singleShot(1000, app.quit); from cowork_local.app import run; raise SystemExit(run([]))" +``` + +## Optional document tooling + +Install PDF extraction helpers when needed: + +```bash +uv pip install --python .venv/bin/python -r requirements-optional.txt +``` + +LibreOffice is an external application, not a Python package. Install it separately if headless Office-to-PDF conversion is required. Microsoft Office/Outlook COM integration and `pywin32` are Windows-only. + +## Update or repair + +Pull the desired reviewed revision and rerun `./scripts/install_local.sh`. The operation is idempotent: it reuses `.venv`, reconciles declared packages, and keeps the existing launcher when it already points to this checkout. + +The first verified macOS installation is recorded in [installation-log-2026-08-11.md](installation-log-2026-08-11.md). diff --git a/requirements-optional.txt b/requirements-optional.txt new file mode 100644 index 0000000..9e0b54b --- /dev/null +++ b/requirements-optional.txt @@ -0,0 +1,4 @@ +# Optional document/PDF extraction helpers. The application degrades +# gracefully or can install these on demand when they are not present. +pypdf>=4,<7 +opendataloader-pdf diff --git a/requirements.txt b/requirements.txt new file mode 100644 index 0000000..dd433d0 --- /dev/null +++ b/requirements.txt @@ -0,0 +1,16 @@ +# Cowork Local desktop runtime (Python 3.11). +# Keep optional/heavy document tooling out of this baseline; see +# requirements-optional.txt and docs/installation.md. +PySide6>=6.7,<7 +anyio>=4,<5 +holidays>=0.40,<1 +keyring>=24,<26 +mcp>=1,<2 +msal>=1.28,<2 +networkx>=3.2,<4 +openpyxl>=3.1,<4 +psutil>=5.9,<8 +pydantic>=2,<3 +Pygments>=2.17,<3 +python-pptx>=1,<2 +requests>=2.31,<3 diff --git a/scripts/install_local.sh b/scripts/install_local.sh new file mode 100755 index 0000000..24fe093 --- /dev/null +++ b/scripts/install_local.sh @@ -0,0 +1,52 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir=$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd) +repo_dir=$(cd "$script_dir/.." && pwd) +venv_dir="$repo_dir/.venv" +launcher_dir="$HOME/.local/bin" +launcher_link="$launcher_dir/cowork-local" +launcher_target="$repo_dir/scripts/run_local.sh" + +if [[ -n "${COWORK_PYTHON:-}" ]]; then + python_bin="$COWORK_PYTHON" +elif command -v python3.11 >/dev/null 2>&1; then + python_bin=$(command -v python3.11) +else + python_bin=$(command -v python3) +fi + +"$python_bin" -c 'import sys; assert sys.version_info >= (3, 11), "Cowork Local requires Python 3.11+"' + +if [[ ! -x "$venv_dir/bin/python" ]]; then + if command -v uv >/dev/null 2>&1; then + uv venv --python "$python_bin" "$venv_dir" + else + "$python_bin" -m venv "$venv_dir" + fi +fi + +if command -v uv >/dev/null 2>&1; then + uv pip install --python "$venv_dir/bin/python" \ + -r "$repo_dir/requirements.txt" \ + -r "$repo_dir/requirements-test.txt" +else + "$venv_dir/bin/python" -m pip install --disable-pip-version-check \ + -r "$repo_dir/requirements.txt" \ + -r "$repo_dir/requirements-test.txt" +fi + +mkdir -p "$launcher_dir" +if [[ ! -e "$launcher_link" && ! -L "$launcher_link" ]]; then + ln -s "$launcher_target" "$launcher_link" + echo "Installed launcher: $launcher_link" +elif [[ -L "$launcher_link" && "$(readlink "$launcher_link")" == "$launcher_target" ]]; then + echo "Launcher already installed: $launcher_link" +else + echo "Not replacing existing path: $launcher_link" >&2 + echo "Run Cowork Local with: $launcher_target" >&2 +fi + +echo "Cowork Local environment: $venv_dir" +echo "Run: $launcher_target" +echo "Or add $launcher_dir to PATH and run: cowork-local" diff --git a/scripts/run_local.sh b/scripts/run_local.sh new file mode 100755 index 0000000..44f2cea --- /dev/null +++ b/scripts/run_local.sh @@ -0,0 +1,25 @@ +#!/usr/bin/env bash +set -euo pipefail + +launcher_source=${BASH_SOURCE[0]} +while [[ -L "$launcher_source" ]]; do + launcher_dir=$(cd "$(dirname "$launcher_source")" && pwd) + launcher_target=$(readlink "$launcher_source") + if [[ "$launcher_target" = /* ]]; then + launcher_source="$launcher_target" + else + launcher_source="$launcher_dir/$launcher_target" + fi +done + +script_dir=$(cd "$(dirname "$launcher_source")" && pwd) +repo_dir=$(cd "$script_dir/.." && pwd) +python_bin="$repo_dir/.venv/bin/python" + +if [[ ! -x "$python_bin" ]]; then + echo "Cowork Local is not installed. Run $repo_dir/scripts/install_local.sh first." >&2 + exit 1 +fi + +cd "$(dirname "$repo_dir")" +exec "$python_bin" -m cowork_local "$@" -- 2.54.0