mirror of
https://github.com/tiennm99/DocsGPT.git
synced 2026-10-05 10:13:39 +00:00
pip install docsgpt now brings the web UI with it: `docsgpt api` serves the API and the UI on one port. - scripts/build_frontend.sh builds the frontend into docsgpt/static (gitignored) the way the frontend image does: .env.development as the production baseline, and index.html loading /config.js ahead of the bundle. hatch admits the directory into the wheel and the sdist through `artifacts`; the package workflows run the script before `uv build` and fail if the wheel lacks the UI. The backend image keeps ignoring it. - docsgpt/ui.py serves the build in front of Flask: files as they are, hashed assets immutable, Flask's own path prefixes (taken from its URL map, so new blueprints need no registration) passed through, every other GET rendered as index.html for the client-side router. /config.js is generated per request with VITE_API_HOST and VITE_BASE_URL set to the page's origin, VITE_* environment variables winning. SERVE_UI=false leaves the API alone. - docsgpt api configures gunicorn in code (gunicorn.app.base.Application) instead of rewriting sys.argv, so the SIGUSR2 re-exec that gunicorn uses for zero-downtime upgrades runs the docsgpt console script again and works; verified with a live handover. - Docs: the pip page says the UI is included, that DOCSGPT_HOME and DOCSGPT_ENV_FILE are process environment variables rather than .env entries, and the settings page describes SERVE_UI.
120 lines
4.3 KiB
YAML
120 lines
4.3 KiB
YAML
name: Build the Python package
|
|
|
|
# Builds the sdist and wheel, checks the wheel carries the data the app reads
|
|
# at runtime and nothing from the checkout, then installs it into a clean
|
|
# environment and runs the docsgpt command.
|
|
|
|
on:
|
|
pull_request:
|
|
paths:
|
|
- 'pyproject.toml'
|
|
- 'uv.lock'
|
|
- 'docsgpt/**'
|
|
- 'README.md'
|
|
- 'LICENSE'
|
|
- 'frontend/**'
|
|
- 'scripts/build_frontend.sh'
|
|
- '.github/workflows/package-build.yml'
|
|
push:
|
|
branches: [main]
|
|
paths:
|
|
- 'pyproject.toml'
|
|
- 'uv.lock'
|
|
- 'docsgpt/**'
|
|
- 'README.md'
|
|
- 'LICENSE'
|
|
- 'frontend/**'
|
|
- 'scripts/build_frontend.sh'
|
|
- '.github/workflows/package-build.yml'
|
|
workflow_dispatch:
|
|
|
|
permissions:
|
|
contents: read
|
|
|
|
jobs:
|
|
build:
|
|
runs-on: ubuntu-latest
|
|
steps:
|
|
- uses: actions/checkout@11d5960a326750d5838078e36cf38b85af677262 # v4.4.0
|
|
with:
|
|
persist-credentials: false
|
|
|
|
- uses: astral-sh/setup-uv@d0cc045d04ccac9d8b7881df0226f9e82c39688e # v6.8.0
|
|
|
|
- uses: actions/setup-node@820762786026740c76f36085b0efc47a31fe5020 # v7.0.0
|
|
with:
|
|
node-version: 22
|
|
cache: npm
|
|
cache-dependency-path: frontend/package-lock.json
|
|
|
|
- name: Build the web UI into the package
|
|
env:
|
|
CI: "true"
|
|
run: bash scripts/build_frontend.sh
|
|
|
|
- name: Build the sdist and wheel
|
|
run: uv build
|
|
|
|
- name: Check the archives
|
|
run: |
|
|
uvx twine check --strict dist/*
|
|
python3 - <<'PY'
|
|
import glob
|
|
import zipfile
|
|
|
|
names = set(zipfile.ZipFile(glob.glob("dist/*.whl")[0]).namelist())
|
|
for required in (
|
|
"docsgpt/cli.py",
|
|
"docsgpt/alembic.ini",
|
|
"docsgpt/alembic/env.py",
|
|
"docsgpt/alembic/script.py.mako",
|
|
"docsgpt/prompts/chat_reduce_prompt.txt",
|
|
"docsgpt/prompts/fragments/identity.txt",
|
|
"docsgpt/core/models/_defaults.yaml",
|
|
"docsgpt/seed/config/premade_agents.yaml",
|
|
):
|
|
assert required in names, f"missing from the wheel: {required}"
|
|
for banned in (
|
|
"docsgpt/Dockerfile",
|
|
"docsgpt/requirements.txt",
|
|
"docsgpt/requirements-docling.txt",
|
|
"docsgpt/index.faiss",
|
|
"docsgpt/index.pkl",
|
|
"application/__init__.py",
|
|
):
|
|
assert banned not in names, f"must not ship: {banned}"
|
|
stray = [n for n in names if n.startswith(("docsgpt/indexes/", "docsgpt/inputs/", "docsgpt/vectors/"))]
|
|
assert not stray, stray
|
|
print(f"wheel ok: {len(names)} files")
|
|
PY
|
|
|
|
- name: The wheel carries the web UI and no generated runtime config
|
|
run: |
|
|
python3 - <<'PY'
|
|
import glob
|
|
import zipfile
|
|
|
|
wheel = zipfile.ZipFile(glob.glob("dist/*.whl")[0])
|
|
names = set(wheel.namelist())
|
|
assert "docsgpt/static/index.html" in names, "the UI build is missing from the wheel"
|
|
assert any(n.startswith("docsgpt/static/assets/") for n in names), "no bundle under docsgpt/static/assets/"
|
|
assert "docsgpt/static/config.js" not in names, "config.js is generated by the API at runtime"
|
|
assert b'src="/config.js"' in wheel.read("docsgpt/static/index.html"), "index.html must load /config.js"
|
|
print("ui ok:", sum(n.startswith("docsgpt/static/") for n in names), "files")
|
|
PY
|
|
|
|
- name: Install the wheel into a clean environment and run the command
|
|
run: |
|
|
uv venv "$RUNNER_TEMP/docsgpt-venv"
|
|
uv pip install --python "$RUNNER_TEMP/docsgpt-venv/bin/python" dist/*.whl
|
|
"$RUNNER_TEMP/docsgpt-venv/bin/docsgpt" --version
|
|
"$RUNNER_TEMP/docsgpt-venv/bin/docsgpt" --help
|
|
"$RUNNER_TEMP/docsgpt-venv/bin/docsgpt" api --help
|
|
"$RUNNER_TEMP/docsgpt-venv/bin/python" -c "from docsgpt.core import paths; print('home:', paths.home_dir())"
|
|
"$RUNNER_TEMP/docsgpt-venv/bin/python" -c "from docsgpt.core.paths import package_dir; assert (package_dir() / 'static' / 'index.html').is_file(); print('ui installed')"
|
|
|
|
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
|
|
with:
|
|
name: dist
|
|
path: dist/
|