diff --git a/bin/freeze_logstashui.sh b/bin/freeze_logstashui.sh index 8409afdc..19266475 100755 --- a/bin/freeze_logstashui.sh +++ b/bin/freeze_logstashui.sh @@ -5,7 +5,7 @@ # Optional air-gapped freeze. Default `uv build` is unchanged. # Usage: -# ./bin/freeze_logstashui.sh [--wheels] [--docker] [--standalone] [--all] +# ./bin/freeze_logstashui.sh [--wheels] [--docker] [--all] # [--output DIR] [--image NAME] # No artifact flags → --all. @@ -15,22 +15,19 @@ ROOT=$(CDPATH= cd -- "$(dirname "$0")/.." && pwd) TEMPLATES="$ROOT/packaging/offline" DO_WHEELS=0 DO_DOCKER=0 -DO_STANDALONE=0 -EXPLICIT_STANDALONE=0 OUT="" IMAGE_OVERRIDE="" usage() { cat <<'EOF' -Usage: freeze_logstashui.sh [--wheels] [--docker] [--standalone] [--all] +Usage: freeze_logstashui.sh [--wheels] [--docker] [--all] [--output DIR] [--image NAME] Connected-builder freeze for air-gapped hosts. Default uv build is unchanged. --wheels CPython 3.12 manylinux x86_64 wheelhouse zip --docker docker save of a local image (never docker pull) - --standalone experimental PyInstaller onedir (Linux x86_64 only) - --all all three (default if no artifact flags) + --all both wheels & docker (default if no artifact flags) --output DIR default: /dist/offline --image NAME docker save this local tag instead of building @@ -42,8 +39,7 @@ while [[ $# -gt 0 ]]; do case "$1" in --wheels) DO_WHEELS=1 ;; --docker) DO_DOCKER=1 ;; - --standalone) DO_STANDALONE=1; EXPLICIT_STANDALONE=1 ;; - --all) DO_WHEELS=1; DO_DOCKER=1; DO_STANDALONE=1 ;; + --all) DO_WHEELS=1; DO_DOCKER=1; ;; --output) OUT="${2:?--output requires a directory}" shift @@ -65,10 +61,9 @@ while [[ $# -gt 0 ]]; do shift done -if [[ $DO_WHEELS -eq 0 && $DO_DOCKER -eq 0 && $DO_STANDALONE -eq 0 ]]; then +if [[ $DO_WHEELS -eq 0 && $DO_DOCKER -eq 0 ]]; then DO_WHEELS=1 DO_DOCKER=1 - DO_STANDALONE=1 fi OUT="${OUT:-$ROOT/dist/offline}" @@ -220,47 +215,6 @@ freeze_docker() { echo "Wrote $zip" } -freeze_standalone() { - if ! linux_x86_64; then - if [[ "$EXPLICIT_STANDALONE" -eq 1 ]]; then - die "standalone freeze requires Linux x86_64 (PyInstaller binary is per-OS)" - fi - echo "WARNING: skipping --standalone (not Linux x86_64)" >&2 - return 0 - fi - - local venv="$OUT/.standalone-venv" - local work="$OUT/pyinstaller-work" - local dist="$OUT/pyinstaller-dist" - local stage="$OUT/logstashui-${VERSION}-offline-standalone-linux-x86_64" - local zip="$OUT/logstashui-${VERSION}-offline-standalone-linux-x86_64.zip" - echo "==> throwaway venv + PyInstaller (not a project dependency)" - rm -rf "$venv" "$work" "$dist" - uv venv --python 3.12 "$venv" - (cd "$ROOT" && uv pip install --python "$venv" ".[databases,otel]") - uv pip install --python "$venv" pyinstaller - "$venv/bin/pyinstaller" \ - --noconfirm \ - --clean \ - --workpath "$work" \ - --distpath "$dist" \ - "$TEMPLATES/logstashui.spec" - - [[ -x "$dist/logstashui/logstashui" ]] || die "PyInstaller did not produce $dist/logstashui/logstashui" - - rm -rf "$stage" - mkdir -p "$stage" - cp -a "$dist/logstashui" "$stage/logstashui" - subst "$TEMPLATES/standalone-run.sh" "$stage/run.sh" - subst "$TEMPLATES/standalone-README.md" "$stage/README.md" - chmod +x "$stage/run.sh" - cp "$ROOT/LICENSE.txt" "$stage/LICENSE.txt" - cp "$ROOT/NOTICE.txt" "$stage/NOTICE.txt" - sha256_tree "$stage" "$stage/SHA256SUMS.txt" - zip_dir "$stage" "$zip" - echo "Wrote $zip" -} - VERSION=$(version_from_pyproject) [[ -n "$VERSION" ]] || die "could not read version from pyproject.toml" GIT_SHA=$(git -C "$ROOT" rev-parse --short HEAD 2>/dev/null || echo unknown) @@ -277,6 +231,3 @@ fi if [[ $DO_DOCKER -eq 1 ]]; then freeze_docker fi -if [[ $DO_STANDALONE -eq 1 ]]; then - freeze_standalone -fi diff --git a/docs/docs/logstashui/general/offline.md b/docs/docs/logstashui/general/offline.md index e1b5b629..4a7cf80d 100644 --- a/docs/docs/logstashui/general/offline.md +++ b/docs/docs/logstashui/general/offline.md @@ -12,7 +12,6 @@ This is **not** the default packaging path and **not** the recommended install w |---|---|---| | `logstashui-*-offline-wheels-linux-x86_64-cp312.zip` | CPython **3.12** x86_64 + venv | `./install.sh` then `.venv/bin/logstashui serve` | | `logstashui-*-offline-docker-linux-x86_64.zip` | Docker Engine | `./load.sh` then `docker compose -f compose.offline.yml up -d` | -| `logstashui-*-offline-standalone-linux-x86_64.zip` | glibc Linux x86_64 | `./run.sh` (**experimental**) | All three include `LogstashUI[databases]` (psycopg + PyMySQL) and `LogstashUI[otel]` (inert until `LOGSTASHUI_OTEL=true`). SQLite remains the runtime default. **LogstashAgent is not bundled.** arm64 and Windows are later freeze invocations, not this zip. @@ -23,12 +22,11 @@ Requirements: Linux x86_64 (wheels can also be downloaded from another OS via pi ```bash ./bin/freeze_logstashui.sh --wheels ./bin/freeze_logstashui.sh --docker -./bin/freeze_logstashui.sh --standalone # Linux x86_64 only ./bin/freeze_logstashui.sh --all # default if you pass no artifact flags ./bin/freeze_logstashui.sh --docker --image logstashui:offline-0.5.2 ``` -`--image` saves a **local** tag. The script never `docker pull`. `--standalone` on macOS/Windows/ARM **fails** if you passed that flag; `--all` **skips** it with a warning. +`--image` saves a **local** tag. The script never `docker pull`. Wheel policy: **zip contains only `.whl` files**. The builder prefers `manylinux2014` then `manylinux_2_28` cp312 wheels (isolated host needs glibc **2.28+**, e.g. RHEL 8 / Ubuntu 20.04). Pure-Python sdists (no manylinux wheel) are converted to `py3-none-any` on the **connected** builder. A native package with no manylinux wheel fails the freeze — the isolated host has no compiler. Pins come from `uv.lock`. @@ -42,16 +40,15 @@ Optional CI: `.github/workflows/offline-freeze.yml` is `workflow_dispatch` only ## Isolated host -Same env as a normal install: `LOGSTASHUI_DATA_DIR` (default `$(pwd)/logstashui_data`), `LOGSTASHUI_*`, `LOGSTASHUI_DB_*`. HTTPS on **:8443**. Product CA is created on first start under the data dir — do not expect CA files inside the zip. +Same env as a normal install: `LOGSTASHUI_DATA_DIR` (default `$(pwd)/logstashui_data`, but can be set using `LOCAL_LOGSTASHUI_DATA_DIR`), `LOGSTASHUI_*`, `LOGSTASHUI_DB_*`. HTTPS on **:8443**. Product CA is created on first start under the data dir — do not expect CA files inside the zip. **Wheels:** Debian/Ubuntu need `python3.12` and `python3.12-venv`. `install.sh` uses `pip install --no-index --find-links ./wheels 'LogstashUI[databases,otel]'`. It does **not** upgrade pip (that would hit PyPI). uv is not required. -**Docker:** UI-only compose (no Agent, no `embedded` profile). Set `ALLOWED_HOSTS` / `LOGSTASHUI_HOST_*` / `LOGSTASHUI_DB_*` as needed. After a local image build, optional extra check: `IMAGE= bin/test_docker_otel.sh` (imports the OTEL packages; does not start serve). Standalone has no automated smoke. - -**Standalone:** experimental PyInstaller onedir. Treat as a trial until `serve` completes migrate, SNMP official sync, collectstatic, and HTTPS :8443 with no network. Gunicorn stays gevent; do not switch workers as a workaround. No automated smoke. Run on a Linux x86_64 builder if you ship this zip. +**Docker:** UI-only compose (no Agent, no `embedded` profile). Set `ALLOWED_HOSTS` / `LOGSTASHUI_HOST_*` / `LOGSTASHUI_DB_*` as needed. After a local image build, optional extra check: `IMAGE= bin/test_docker_otel.sh` (imports the OTEL packages; does not start serve). After a wheelhouse install, `logstashui systemd` still writes the unit and `/etc/default/logstashui` and does **not** enable it. ## Later -linux/arm64 and Windows x86_64 as extra freeze tags; a sibling LogstashAgent freeze; promoting standalone off experimental after the serve smoke exists. +- linux/arm64 and Windows x86_64 as extra freeze tags +- a sibling LogstashAgent freeze diff --git a/packaging/offline/compose.offline.yml b/packaging/offline/compose.offline.yml index 87e42bcd..87454bfa 100644 --- a/packaging/offline/compose.offline.yml +++ b/packaging/offline/compose.offline.yml @@ -14,7 +14,7 @@ services: ports: - "8443:8443" volumes: - - logstashui_data:/var/lib/logstashui + - ${LOCAL_LOGSTASHUI_DATA_DIR:-./logstashui_data}:/var/lib/logstashui environment: DEBUG: "False" LOGSTASHUI_DATA_DIR: /var/lib/logstashui @@ -33,6 +33,3 @@ services: PUID: ${PUID:-} PGID: ${PGID:-} restart: unless-stopped - -volumes: - logstashui_data: diff --git a/packaging/offline/docker-README.md b/packaging/offline/docker-README.md index c4c4ebbf..186e736e 100644 --- a/packaging/offline/docker-README.md +++ b/packaging/offline/docker-README.md @@ -9,7 +9,10 @@ Linux **x86_64** image tarball. No registry. The image already includes `Logstas docker compose -f compose.offline.yml up -d ``` -`load.sh` never pulls. HTTPS UI is **:8443**. Named volume `logstashui_data` is `LOGSTASHUI_DATA_DIR=/var/lib/logstashui`. Keep `LOGSTASHUI_TLS` on. +`load.sh` never pulls. HTTPS UI is **:8443**. +Mapped volume `logstashui_data` is `LOGSTASHUI_DATA_DIR=/var/lib/logstashui` defaults to `$(pwd)/logstashui_data`, but this can be changed by editing `compose.offline.yml` or setting the environment variable `LOCAL_LOGSTASHUI_DATA_DIR`. + +Keep `LOGSTASHUI_TLS` on. Set `ALLOWED_HOSTS`, `LOGSTASHUI_HOST_*`, and `LOGSTASHUI_DB_*` in the environment or a `.env` next to `compose.offline.yml` as needed. diff --git a/packaging/offline/entry.py b/packaging/offline/entry.py deleted file mode 100644 index c75fff80..00000000 --- a/packaging/offline/entry.py +++ /dev/null @@ -1,10 +0,0 @@ -#Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -#or more contributor license agreements. Licensed under the Elastic License; -#you may not use this file except in compliance with the Elastic License. - -"""PyInstaller entry for the experimental standalone freeze.""" - -from LogstashUI.cli import main - -if __name__ == "__main__": - raise SystemExit(main()) diff --git a/packaging/offline/logstashui.spec b/packaging/offline/logstashui.spec deleted file mode 100644 index e432a1aa..00000000 --- a/packaging/offline/logstashui.spec +++ /dev/null @@ -1,99 +0,0 @@ -#Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -#or more contributor license agreements. Licensed under the Elastic License; -#you may not use this file except in compliance with the Elastic License. - -# PyInstaller onedir spec for LogstashUI. Experimental: gunicorn+gevent+Django -# hidden imports. Do not change product --worker-class gevent from here. -# -*- mode: python ; coding: utf-8 -*- - -from __future__ import annotations - -import os - -from PyInstaller.building.api import COLLECT, EXE, PYZ -from PyInstaller.building.build_main import Analysis -from PyInstaller.utils.hooks import collect_all - -SPECDIR = os.path.dirname(os.path.abspath(SPEC)) -ROOT = os.path.abspath(os.path.join(SPECDIR, "..", "..")) -SRC = os.path.join(ROOT, "src", "logstashui") -ENTRY = os.path.join(SPECDIR, "entry.py") - -PACKAGES = [ - "LogstashUI", - "PipelineManager", - "Management", - "Utilities", - "SNMP", - "Monitoring", - "Site", - "Documentation", - "AI", - "theme", - "Common", - "django", - "gunicorn", - "gevent", - "greenlet", - "cryptography", - "pysnmp", - "lark", - "pygrok", - "whitenoise", - "psycopg", - "pymysql", - "yaml", - "django_htmx", - "tailwind", - "login_required", - "elasticsearch", - "requests", - "markdown", - "packaging", - "django_browser_reload", -] - -datas: list = [] -binaries: list = [] -hiddenimports: list = [] -for pkg in PACKAGES: - try: - d, b, h = collect_all(pkg) - except Exception: - continue - datas += d - binaries += b - hiddenimports += h - -a = Analysis( - [ENTRY], - pathex=[SRC], - binaries=binaries, - datas=datas, - hiddenimports=hiddenimports, - hookspath=[], - runtime_hooks=[], - excludes=[], - noarchive=False, -) -pyz = PYZ(a.pure) -exe = EXE( - pyz, - a.scripts, - [], - exclude_binaries=True, - name="logstashui", - debug=False, - bootloader_ignore_signals=False, - strip=False, - upx=False, - console=True, -) -coll = COLLECT( - exe, - a.binaries, - a.datas, - strip=False, - upx=False, - name="logstashui", -) diff --git a/packaging/offline/standalone-README.md b/packaging/offline/standalone-README.md deleted file mode 100644 index 2c21d2f4..00000000 --- a/packaging/offline/standalone-README.md +++ /dev/null @@ -1,19 +0,0 @@ -# LogstashUI experimental standalone (__VERSION__) - -**Experimental.** PyInstaller onedir for Linux **x86_64**. No Python on the host. Not production-supported until `./logstashui serve` completes migrate, SNMP sync, collectstatic, and HTTPS :8443 without network. - -Gunicorn stays `--worker-class gevent`. If this bundle cannot serve, that is a freeze bug — do not switch workers as a workaround without amending the design spec. - -## Run - -```sh -./run.sh -# or: -./logstashui/logstashui serve -``` - -Data dir default: `$(pwd)/logstashui_data`. Same `LOGSTASHUI_*` env as a wheel/Docker install. - -systemd: set `ExecStart=` to the unpacked `logstashui/logstashui serve`. This zip does not write units. - -Git: `__GIT_SHA__` diff --git a/packaging/offline/standalone-run.sh b/packaging/offline/standalone-run.sh deleted file mode 100755 index 955b8410..00000000 --- a/packaging/offline/standalone-run.sh +++ /dev/null @@ -1,16 +0,0 @@ -#!/bin/sh -#Copyright Elasticsearch B.V. and/or licensed to Elasticsearch B.V. under one -#or more contributor license agreements. Licensed under the Elastic License; -#you may not use this file except in compliance with the Elastic License. - -set -eu - -HERE=$(CDPATH= cd -- "$(dirname "$0")" && pwd) -BIN="$HERE/logstashui/logstashui" - -if [ ! -x "$BIN" ]; then - echo "missing $BIN" >&2 - exit 1 -fi - -exec "$BIN" serve "$@"