feat: publish the backend to PyPI as docsgpt

pip install docsgpt (extras: docling, milvus) installs the backend with a
docsgpt command: api, worker, migrate, prefetch-models, verify-offline,
reembed. Second step of the PyPI work after the package rename.

- hatchling build; the version comes from docsgpt/version.py. The wheel is
  the docsgpt package with the data it reads at runtime (prompts, model
  catalogs, seed config, alembic.ini and migrations) and without the
  Dockerfile, the exported requirements, the sample index and local runtime
  data. The application import alias stays checkout-only. uv sync installs
  the package editable now that [tool.uv] package = false is gone.
- docsgpt/cli.py: api (gunicorn + BoundedDrainUvicornWorker with the image's
  flags, --reload for uvicorn), worker (Celery worker with beat embedded,
  --no-beat/-Q/--concurrency/--pool, solo pool on macOS), migrate, and
  argument pass-through to the maintenance scripts. --help imports no app.
- docsgpt/core/paths.py: runtime data lives in a data home (DOCSGPT_HOME,
  else the checkout, else cwd); DOCSGPT_ENV_FILE overrides the env file.
  Settings, the dotenv load, LocalStorage and the internal upload route use
  it instead of "three directories above this file", which is site-packages
  for an installed package. A checkout and the Docker image behave as before.
- [project] dependencies are compatible ranges so the package installs next
  to other packages; uv.lock resolves to the same versions and the exported
  requirements files are unchanged.
- package-build.yml builds and checks the wheel on PRs and installs it into
  a clean venv; pypi-publish.yml publishes on a published release through
  trusted publishing (environment pypi), or to TestPyPI on a manual run.
- Docs: Deploying -> Install with pip. AGENTS.md notes the package.
This commit is contained in:
Alex committed 2026-09-07 14:40:35 +01:00
1 parent af284218a0
commit bea26336f8
18 files changed
+844 -175

No files matched your search

+88
View File
@@ -0,0 +1,88 @@
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'
- '.github/workflows/package-build.yml'
push:
branches: [main]
paths:
- 'pyproject.toml'
- 'uv.lock'
- 'docsgpt/**'
- 'README.md'
- 'LICENSE'
- '.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
- 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: 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())"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
+86
View File
@@ -0,0 +1,86 @@
name: Publish to PyPI
# Trusted publishing: PyPI trusts this workflow file in arc53/DocsGPT running
# in the `pypi` environment, so no API token is stored. A published release
# (which the backend-release workflow creates when docsgpt/version.py changes
# on main) publishes to PyPI; a manual run publishes to TestPyPI by default.
on:
release:
types: [published]
workflow_dispatch:
inputs:
target:
description: Package index to publish to
type: choice
options: [testpypi, pypi]
default: testpypi
permissions:
contents: read
jobs:
build:
if: github.repository == 'arc53/DocsGPT'
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
with:
# A publishing workflow must not restore a cache another job could have poisoned.
enable-cache: false
- name: Build the sdist and wheel
run: uv build
- name: The archives carry the released version
if: github.event_name == 'release'
env:
TAG: ${{ github.event.release.tag_name }}
run: |
ls "dist/docsgpt-${TAG}.tar.gz" "dist/docsgpt-${TAG}-py3-none-any.whl"
- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: dist
path: dist/
if-no-files-found: error
publish-pypi:
if: github.event_name == 'release' || inputs.target == 'pypi'
needs: build
runs-on: ubuntu-latest
environment:
name: pypi
url: https://pypi.org/project/docsgpt/
permissions:
id-token: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
publish-testpypi:
if: github.event_name == 'workflow_dispatch' && inputs.target == 'testpypi'
needs: build
runs-on: ubuntu-latest
environment:
name: testpypi
url: https://test.pypi.org/project/docsgpt/
permissions:
id-token: write
steps:
- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: dist
path: dist/
- uses: pypa/gh-action-pypi-publish@dc37677b2e1c63e2034f94d8a5b11f265b73ba33 # v1.14.2
with:
repository-url: https://test.pypi.org/legacy/