diff --git a/.github/workflows/tests-flows-install.yml b/.github/workflows/tests-flows-install.yml new file mode 100644 index 0000000..209f8dd --- /dev/null +++ b/.github/workflows/tests-flows-install.yml @@ -0,0 +1,117 @@ +name: Flows Install + +on: + pull_request: + push: + branches: [main] + +permissions: + contents: read + +concurrency: + group: flows_install-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +jobs: + github_flows_install: + name: Flows Install GitHub Ubuntu 24 + runs-on: ubuntu-24.04 + env: + DATABASE_URI: postgresql+psycopg://vix_user:vix_password@localhost:5432/vix_db + + services: + postgres: + image: postgres:17 + env: + POSTGRES_USER: vix_user + POSTGRES_PASSWORD: vix_password + POSTGRES_DB: vix_db + options: >- + --health-cmd pg_isready + --health-interval 10s + --health-timeout 5s + --health-retries 5 + ports: + - 5432:5432 + + steps: + - uses: actions/checkout@v4 + - name: Checkout Visionatrix (engine) + uses: actions/checkout@v4 + with: + repository: Visionatrix/Visionatrix + path: visionatrix_main + + - uses: actions/setup-python@v5 + with: + python-version: '3.12' + + - name: Install Visionatrix dependencies + working-directory: visionatrix_main + run: | + python3 -m pip install torch torchvision torchaudio --index-url https://download.pytorch.org/whl/cpu + python3 -m pip install ".[pgsql]" + + - name: Export Visionatrix exclude flows + run: | + value=$(python3 scripts/ci/get_excludes.py flows) + echo "VISIONATRIX_EXCLUDE_FLOWS=$value" >> "$GITHUB_ENV" + + - name: Export Visionatrix exclude nodes + run: | + value=$(python3 scripts/ci/get_excludes.py nodes) + echo "VISIONATRIX_INSTALL_EXCLUDE_NODES=$value" >> "$GITHUB_ENV" + + - name: Run Visionatrix install + working-directory: visionatrix_main + run: python3 -m visionatrix install + + - name: Install flows from CMD + working-directory: visionatrix_main + run: | + echo "Y" | VIX_MODE=SERVER VIX_SERVER_FULL_MODELS=0 python3 -m visionatrix install-flow --name=* + + - name: Generate openapi-flows.json + working-directory: visionatrix_main + run: | + VIX_MODE=SERVER VIX_SERVER_FULL_MODELS=0 python3 -m visionatrix openapi --flows="*" --exclude-base --file=openapi-flows.json + + - name: Create test user + working-directory: visionatrix_main + run: | + VIX_MODE=SERVER VIX_SERVER_FULL_MODELS=0 python3 -m visionatrix create-user --name=user --password=user + + - name: Start Visionatrix in Server mode + working-directory: visionatrix_main + run: | + nohup python3 -m visionatrix run --ui --mode=SERVER > visionatrix.log 2>&1 & + echo "Server started in background with PID $!" + + - name: Wait for Visionatrix server + run: | + max_attempts=30 + for i in $(seq 1 $max_attempts); do + echo "Attempt $i/$max_attempts: Checking Visionatrix server..." + if curl -s http://localhost:8288/whoami > /dev/null; then + echo "Visionatrix server is up!" + exit 0 + fi + echo "Server not ready yet. Sleeping 10s..." + sleep 10 + done + echo "Server not responding after 5 minutes!" + exit 1 + + - name: Download test image + working-directory: visionatrix_main + run: | + wget -O tests/source-cube_rm_background.png "https://github.com/Visionatrix/VixFlowsDocs/blob/main/tests_data/source-cube_rm_background.png?raw=true" + + - name: Create task for each Flow + working-directory: visionatrix_main + run: python3 tests/create_flow_tasks.py + + - name: Display logs + if: ${{ always() }} + working-directory: visionatrix_main + run: cat visionatrix.log diff --git a/Dockerfile b/Dockerfile index 8e15668..d14c96f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -62,12 +62,17 @@ RUN --mount=type=cache,target=/root/.cache/pip \ venv/bin/python -m pip install torch==2.7.0 torchvision torchaudio; \ fi +COPY ex_app/lib/exclude_nodes.py ex_app/lib/exclude_flows.py /ex_app/lib/ +COPY scripts/ci/get_excludes.py /get_excludes.py RUN --mount=type=cache,target=/root/.cache/pip \ cd /Visionatrix && \ venv/bin/python -m pip install "psycopg[binary]" greenlet && \ venv/bin/python -m pip install . && \ - venv/bin/python -m visionatrix install && \ - rm visionatrix.db + VISIONATRIX_INSTALL_EXCLUDE_NODES="$(python /get_excludes.py nodes)" \ + venv/bin/python -m visionatrix install && \ + rm visionatrix.db && \ + rm /get_excludes.py && \ + rm -rf /ex_app # Setup nodejs and npm for building the front-end client RUN apt-get update && \ diff --git a/ex_app/lib/exclude_flows.py b/ex_app/lib/exclude_flows.py new file mode 100644 index 0000000..55d9118 --- /dev/null +++ b/ex_app/lib/exclude_flows.py @@ -0,0 +1,5 @@ +EXCLUDE_FLOWS_IDS = [ + "all_your_life", + "photo_stickers2", + "photomaker_2", +] diff --git a/ex_app/lib/exclude_nodes.py b/ex_app/lib/exclude_nodes.py new file mode 100644 index 0000000..172805f --- /dev/null +++ b/ex_app/lib/exclude_nodes.py @@ -0,0 +1,5 @@ +EXCLUDE_NODES_IDS = [ + "https://github.com/Fannovel16/ComfyUI-Frame-Interpolation", + "https://github.com/shiimizu/ComfyUI-PhotoMaker-Plus", + "https://github.com/FizzleDorf/ComfyUI_FizzNodes", +] diff --git a/ex_app/lib/main.py b/ex_app/lib/main.py index 27f7379..e82b17e 100644 --- a/ex_app/lib/main.py +++ b/ex_app/lib/main.py @@ -17,6 +17,8 @@ from time import sleep import httpx +from exclude_flows import EXCLUDE_FLOWS_IDS +from exclude_nodes import EXCLUDE_NODES_IDS from fastapi import BackgroundTasks, Body, Depends, FastAPI, Request, responses from nc_py_api import NextcloudApp from nc_py_api.ex_app import ( @@ -29,7 +31,7 @@ from nc_py_api.ex_app.providers.task_processing import TaskProcessingProvider from starlette.middleware.base import BaseHTTPMiddleware from starlette.responses import FileResponse, Response -from supported_flows import FLOWS_IDS +from task_processing_flows import FLOWS_IDS # ---------Start of configuration values for manual deploy--------- # Uncommenting the following lines may be useful when installing manually. @@ -74,6 +76,9 @@ print("[DEBUG]: PROJECT_ROOT_FOLDER=", PROJECT_ROOT_FOLDER, flush=True) print("[DEBUG]: STATIC_FRONTEND_PRESENT=", STATIC_FRONTEND_PRESENT, flush=True) +os.environ["VISIONATRIX_INSTALL_EXCLUDE_NODES"] = EXCLUDE_NODES_IDS +os.environ["VISIONATRIX_EXCLUDE_FLOWS"] = EXCLUDE_FLOWS_IDS + def _(text): return current_translator.get().gettext(text) diff --git a/ex_app/lib/supported_flows.py b/ex_app/lib/task_processing_flows.py similarity index 100% rename from ex_app/lib/supported_flows.py rename to ex_app/lib/task_processing_flows.py diff --git a/scripts/ci/get_excludes.py b/scripts/ci/get_excludes.py new file mode 100644 index 0000000..f18c40a --- /dev/null +++ b/scripts/ci/get_excludes.py @@ -0,0 +1,74 @@ +#!/usr/bin/env python3 +""" +Tiny helper for GitHub Actions *and* Docker builds. + +Usage: + python3 get_excludes.py flows # → prints "id1;id2;…" + python3 get_excludes.py nodes # → prints "url1;url2;…" + +It echoes a semicolon-joined list so callers can assign it to an environment variable, e.g.: + + VISIONATRIX_INSTALL_EXCLUDE_NODES="$(python /get_excludes.py nodes)" +""" + +from __future__ import annotations + +import importlib.util +import sys +from pathlib import Path +from collections.abc import Sequence + + +def _import_list(file: Path, var: str) -> Sequence[str]: + spec = importlib.util.spec_from_file_location(file.stem, file) + module = importlib.util.module_from_spec(spec) # type: ignore[arg-type] + spec.loader.exec_module(module) # type: ignore[attr-defined] + value: list[str] | Sequence[str] = getattr(module, var, []) + if not isinstance(value, list | tuple): + raise TypeError(f"{file}:{var} is not a list/tuple") + return value + + +def find_repo_root() -> Path: + """ + Locate directory that contains ex_app/lib/ . + Strategy (first match wins): + 1. Walk up from this script's directory. + 2. Walk up from CWD (useful if script is executed via absolute path). + 3. Fallback to filesystem root (where Dockerfile copies it as /ex_app/lib). + """ + def search_up(start: Path) -> Path | None: + for p in [start, *list(start.parents)]: + if (p / "ex_app" / "lib").is_dir(): + return p + return None + + here = Path(__file__).resolve().parent + return search_up(here) or search_up(Path.cwd()) or Path("/") + + +def main() -> None: + if len(sys.argv) != 2 or sys.argv[1] not in {"flows", "nodes"}: + print("Usage: get_excludes.py [flows|nodes]", file=sys.stderr) + sys.exit(1) + + mode = sys.argv[1] + repo_root = find_repo_root() # or "/" inside container + + if mode == "flows": + py_file = repo_root / "ex_app" / "lib" / "exclude_flows.py" + var_name = "EXCLUDE_FLOWS_IDS" + else: + py_file = repo_root / "ex_app" / "lib" / "exclude_nodes.py" + var_name = "EXCLUDE_NODES_IDS" + + if not py_file.is_file(): + print(f"Error: cannot locate {py_file}", file=sys.stderr) + sys.exit(2) + + items = _import_list(py_file, var_name) + print(";".join(items)) + + +if __name__ == "__main__": + main()