From 154857482a24e0d21570dc61753cd2078141dda3 Mon Sep 17 00:00:00 2001 From: s-heppner Date: Sun, 1 Mar 2026 11:04:06 +0100 Subject: [PATCH 1/3] Align server/pyproject.toml to sdk package definition Previously, the `server` `pyproject.toml` was located in `server/app`, which is different to how it is done in the `sdk`. This aligns the `server` `pyproject.toml` by moving it up one directory level, fixing the `version.py` path, and adding a top-level `__init__.py` for the `app` package. Fixes #466 --- server/app/__init__.py | 0 server/{app => }/pyproject.toml | 6 +++--- 2 files changed, 3 insertions(+), 3 deletions(-) create mode 100644 server/app/__init__.py rename server/{app => }/pyproject.toml (90%) diff --git a/server/app/__init__.py b/server/app/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/app/pyproject.toml b/server/pyproject.toml similarity index 90% rename from server/app/pyproject.toml rename to server/pyproject.toml index 030be7397..8c40deff9 100644 --- a/server/app/pyproject.toml +++ b/server/pyproject.toml @@ -16,8 +16,8 @@ build-backend = "setuptools.build_meta" # from app.version import version # print(f"Project version: {version}") # ``` -root = "../.." # Defines the path to the root of the repository -version_file = "version.py" +root = ".." # Defines the path to the root of the repository +version_file = "app/version.py" [project] name = "basyx-python-server" @@ -55,7 +55,7 @@ dev = [ "Homepage" = "https://github.com/eclipse-basyx/basyx-python-sdk" [tool.setuptools] -packages = { find = { exclude = ["test*"] } } +packages = { find = { include = ["app*"], exclude = ["test*"] } } [tool.setuptools.package-data] app = ["py.typed"] From 54033ab03f80441755212fb7290364f3cb296d4d Mon Sep 17 00:00:00 2001 From: s-heppner Date: Sun, 1 Mar 2026 13:53:13 +0100 Subject: [PATCH 2/3] Refactor server package structure Previously, the `server` was built only with the `repository` API endpoints in mind. Since we now plan to also support `registry` and `discovery`, we need to adapt the `./server` project structure. For this reason, we did the following refactoring: - This moves the `pyproject.toml` from the `app` directory into the server project's top-level directory (`./server/pyproject.toml`). This aligns the `server` project directory with the `sdk` and the `compliance-tool` project directories. See #466 - This also moves the files for Docker image definition into the `./server/docker` subdirectories, where `./server/docker/common` are shared files across different Docker images, and `./server/docker/repository` and others contain the `Dockerfile`, as well as the image specific configuration files. See #468 - This moves the Docker `compose.yaml` into the `./server/example_configurations` and its subdirectories. Each example configuration should have its own `README.md` specifying what the example contains and how to run it. See #468 - Lastly, this moves the repository specific `main.py` to `.server/app/services/run_repository.py` And this adapts (hopefully) all relevant paths in all the different files accordingly. Fixes #466 Fixes #468 --- .github/workflows/ci.yml | 10 ++-- server/README.md | 46 ++----------------- server/app/interfaces/base.py | 2 +- server/app/interfaces/repository.py | 2 +- server/app/services/__init__.py | 0 .../{main.py => services/run_repository.py} | 2 +- server/{ => docker/common}/stop-supervisor.sh | 0 server/{ => docker/common}/supervisord.ini | 0 server/{ => docker/repository}/Dockerfile | 22 +++++---- server/{ => docker/repository}/entrypoint.sh | 0 server/{ => docker/repository}/uwsgi.ini | 2 +- .../repository_standalone/README.md | 15 ++++++ .../repository_standalone/compose.yaml} | 4 +- server/test/interfaces/test_repository.py | 2 +- 14 files changed, 43 insertions(+), 64 deletions(-) create mode 100644 server/app/services/__init__.py rename server/app/{main.py => services/run_repository.py} (99%) rename server/{ => docker/common}/stop-supervisor.sh (100%) rename server/{ => docker/common}/supervisord.ini (100%) rename server/{ => docker/repository}/Dockerfile (72%) rename server/{ => docker/repository}/entrypoint.sh (100%) rename server/{ => docker/repository}/uwsgi.ini (79%) create mode 100644 server/example_configurations/repository_standalone/README.md rename server/{compose.yml => example_configurations/repository_standalone/compose.yaml} (71%) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index caab87d0b..781c6a7c7 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -292,7 +292,7 @@ jobs: defaults: run: - working-directory: ./server/app + working-directory: ./server steps: - uses: actions/checkout@v4 - name: Set up Python ${{ env.X_PYTHON_MIN_VERSION }} @@ -302,11 +302,11 @@ jobs: - name: Install Python dependencies run: | python -m pip install --upgrade pip - python -m pip install ../../sdk + python -m pip install ../sdk python -m pip install .[dev] - name: Check typing with MyPy run: | - python -m mypy . + python -m mypy app test - name: Check code style with PyCodestyle run: | python -m pycodestyle --count --max-line-length 120 . @@ -316,12 +316,12 @@ jobs: runs-on: ubuntu-latest defaults: run: - working-directory: ./server + working-directory: ./server/docker/repository steps: - uses: actions/checkout@v4 - name: Build the Docker image run: | - docker build -t basyx-python-server -f Dockerfile .. + docker build -t basyx-python-server -f Dockerfile ../../.. - name: Run container run: | docker run -d --name basyx-python-server basyx-python-server diff --git a/server/README.md b/server/README.md index d368d4ae5..0d5203f9b 100644 --- a/server/README.md +++ b/server/README.md @@ -19,7 +19,7 @@ The container image can be built via: $ docker build -t basyx-python-server -f Dockerfile .. ``` -Note that when cloning this repository on Windows, Git may convert the line separators to CRLF. This breaks [`entrypoint.sh`](entrypoint.sh) and [`stop-supervisor.sh`](stop-supervisor.sh). Ensure both files use LF line separators (`\n`) before building. +Note that when cloning this repository on Windows, Git may convert the line separators to CRLF. This breaks [`entrypoint.sh`](docker/repository/entrypoint.sh) and [`stop-supervisor.sh`](docker/common/stop-supervisor.sh). Ensure both files use LF line separators (`\n`) before building. ## Running @@ -60,51 +60,13 @@ This implies the following start-up behaviour: - Any AAS/Submodel *already present* is skipped, unless `STORAGE_OVERWRITE = True`, in which case it is replaced. - Supplementary files (e.g., `File` SubmodelElements) are never persisted by the LocalFileBackend. -### Running Examples - -Putting it all together, the container can be started via the following command: -``` -$ docker run -p 8080:80 -v ./input:/input -v ./storage:/storage basyx-python-server -``` - -Since Windows uses backslashes instead of forward slashes in paths, you'll have to adjust the path to the storage directory there: -``` -> docker run -p 8080:80 -v .\input:/input -v .\storage:/storage basyx-python-server -``` - -By default, the server will use the standard settings described [above](#options). Those settings can be adapted in the following way: -``` -$ docker run -p 8080:80 -v ./input:/input2 -v ./storage:/storage2 -e API_BASE_PATH=/api/v3.1/ -e INPUT=/input2 -e STORAGE=/storage2 -e STORAGE_PERSISTENCY=True -e STORAGE_OVERWRITE=True basyx-python-server -``` - ## Building and Running the Image with Docker Compose -The container image can also be built and run via: -``` -$ docker compose up -``` - -An exemplary [`compose.yml`](compose.yml) file for the server is given [here](compose.yml): -```yaml -name: basyx-python-server -services: - app: - build: - context: .. - dockerfile: server/Dockerfile - ports: - - "8080:80" - volumes: - - ./input:/input - - ./storage:/storage - environment: - STORAGE_PERSISTENCY: True -``` +Example configurations can be found in the `./example_configurations` directory. -Input files are read from `./input` and stored persistently under `./storage` on your host system. The server can be accessed at http://localhost:8080/api/v3.0/ from your host system. -To get a different setup, the [`compose.yml`](compose.yml) file can be adapted using the options described [above](#options), similar to the third [running example](#running-examples). +Currently, we offer: -Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/Dockerfile`, as the build context must be set to the parent directory of `/server` to allow access to the local `/sdk`. +- [repository_standalone](example_configurations/repository_standalone/README.md): Standalone repository server ## Running without Docker (Debugging Only) diff --git a/server/app/interfaces/base.py b/server/app/interfaces/base.py index 360ba8514..8234eddc2 100644 --- a/server/app/interfaces/base.py +++ b/server/app/interfaces/base.py @@ -25,7 +25,7 @@ from basyx.aas.adapter.json import StrictStrippedAASFromJsonDecoder, StrictAASFromJsonDecoder, AASToJsonEncoder from basyx.aas.adapter.xml import xml_serialization, XMLConstructables, read_aas_xml_element from basyx.aas.model import AbstractObjectStore -from util.converters import base64url_decode +from app.util.converters import base64url_decode T = TypeVar("T") diff --git a/server/app/interfaces/repository.py b/server/app/interfaces/repository.py index 18976c81b..c1ee513e5 100644 --- a/server/app/interfaces/repository.py +++ b/server/app/interfaces/repository.py @@ -22,7 +22,7 @@ from basyx.aas import model from basyx.aas.adapter import aasx -from util.converters import IdentifierToBase64URLConverter, IdShortPathConverter, base64url_decode +from app.util.converters import IdentifierToBase64URLConverter, IdShortPathConverter, base64url_decode from .base import ObjectStoreWSGIApp, APIResponse, is_stripped_request, HTTPApiDecoder, T diff --git a/server/app/services/__init__.py b/server/app/services/__init__.py new file mode 100644 index 000000000..e69de29bb diff --git a/server/app/main.py b/server/app/services/run_repository.py similarity index 99% rename from server/app/main.py rename to server/app/services/run_repository.py index 3b37edab2..478e4d215 100644 --- a/server/app/main.py +++ b/server/app/services/run_repository.py @@ -14,7 +14,7 @@ from basyx.aas.adapter.aasx import DictSupplementaryFileContainer from basyx.aas.backend.local_file import LocalFileIdentifiableStore from basyx.aas.model.provider import DictIdentifiableStore -from interfaces.repository import WSGIApp +from app.interfaces.repository import WSGIApp from typing import Tuple, Union diff --git a/server/stop-supervisor.sh b/server/docker/common/stop-supervisor.sh similarity index 100% rename from server/stop-supervisor.sh rename to server/docker/common/stop-supervisor.sh diff --git a/server/supervisord.ini b/server/docker/common/supervisord.ini similarity index 100% rename from server/supervisord.ini rename to server/docker/common/supervisord.ini diff --git a/server/Dockerfile b/server/docker/repository/Dockerfile similarity index 72% rename from server/Dockerfile rename to server/docker/repository/Dockerfile index 7ad70bc66..eed1c1abe 100644 --- a/server/Dockerfile +++ b/server/docker/repository/Dockerfile @@ -15,10 +15,14 @@ RUN apk update && \ pip install uwsgi && \ apk del git bash - -COPY server/uwsgi.ini /etc/uwsgi/ -COPY server/supervisord.ini /etc/supervisor/conf.d/supervisord.ini -COPY server/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh +# Copy only the files we need to run the container +# The argumentation for this is to keep the containers as slim as possible. +COPY ./sdk /sdk +COPY ./server/app /server/app +COPY ./server/pyproject.toml /server/pyproject.toml +COPY ./server/docker/repository/uwsgi.ini /etc/uwsgi/ +COPY ./server/docker/common/supervisord.ini /etc/supervisor/conf.d/supervisord.ini +COPY ./server/docker/common/stop-supervisor.sh /etc/supervisor/stop-supervisor.sh RUN chmod +x /etc/supervisor/stop-supervisor.sh # Makes it possible to use a different configuration @@ -41,17 +45,15 @@ ENV STORAGE_OVERWRITE=False VOLUME ["/input", "/storage"] # Copy the entrypoint that will generate Nginx additional configs -COPY server/entrypoint.sh /entrypoint.sh +COPY server/docker/repository/entrypoint.sh /entrypoint.sh RUN chmod +x /entrypoint.sh ENTRYPOINT ["/entrypoint.sh"] ENV SETUPTOOLS_SCM_PRETEND_VERSION=1.0.0 -COPY ./sdk /sdk -COPY ./server/app /app -WORKDIR /app -RUN pip install ../sdk -RUN pip install . +WORKDIR /server/app +RUN pip install ../../sdk +RUN pip install .. CMD ["/usr/bin/supervisord", "-c", "/etc/supervisor/conf.d/supervisord.ini"] diff --git a/server/entrypoint.sh b/server/docker/repository/entrypoint.sh similarity index 100% rename from server/entrypoint.sh rename to server/docker/repository/entrypoint.sh diff --git a/server/uwsgi.ini b/server/docker/repository/uwsgi.ini similarity index 79% rename from server/uwsgi.ini rename to server/docker/repository/uwsgi.ini index 9c54ae1cc..ac4294aca 100644 --- a/server/uwsgi.ini +++ b/server/docker/repository/uwsgi.ini @@ -1,5 +1,5 @@ [uwsgi] -wsgi-file = /app/main.py +wsgi-file = /server/app/services/run_repository.py socket = /tmp/uwsgi.sock chown-socket = nginx:nginx chmod-socket = 664 diff --git a/server/example_configurations/repository_standalone/README.md b/server/example_configurations/repository_standalone/README.md new file mode 100644 index 000000000..67992e0be --- /dev/null +++ b/server/example_configurations/repository_standalone/README.md @@ -0,0 +1,15 @@ +# Repository Standalone + +This example Docker compose configuration starts a repository server. + +The container image can also be built and run via: +``` +$ docker compose up +``` + +Input files are read from `./input` and stored persistently under `./storage` on your host system. +The server can be accessed at http://localhost:8080/api/v3.0/ from your host system. +To get a different setup, the `compose.yaml` file can be adapted using the options described in the main server [README.md](../../README.md#options), similar to the third [running example](#running-examples). + +Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/Dockerfile`, as the build context must be set to the parent directory of `/server` to allow access to the local `/sdk`. + diff --git a/server/compose.yml b/server/example_configurations/repository_standalone/compose.yaml similarity index 71% rename from server/compose.yml rename to server/example_configurations/repository_standalone/compose.yaml index f7e014c37..10e8b17f1 100644 --- a/server/compose.yml +++ b/server/example_configurations/repository_standalone/compose.yaml @@ -2,8 +2,8 @@ name: basyx-python-server services: app: build: - context: .. - dockerfile: server/Dockerfile + context: ../../.. + dockerfile: server/docker/repository/Dockerfile ports: - "8080:80" volumes: diff --git a/server/test/interfaces/test_repository.py b/server/test/interfaces/test_repository.py index 0c7f4c1a8..5cf421a51 100644 --- a/server/test/interfaces/test_repository.py +++ b/server/test/interfaces/test_repository.py @@ -34,7 +34,7 @@ from basyx.aas import model from basyx.aas.adapter.aasx import DictSupplementaryFileContainer -from server.app.interfaces.repository import WSGIApp +from app.interfaces.repository import WSGIApp from basyx.aas.examples.data.example_aas import create_full_example from typing import Set From 0daaa4944679054900517703b31715d09e81e3a9 Mon Sep 17 00:00:00 2001 From: zrgt Date: Sun, 1 Mar 2026 15:27:45 +0100 Subject: [PATCH 3/3] server: Fix review issues in server package restructure - Rename CI job server-package -> server-repository-docker to reflect that it specifically builds the repository Docker image - Narrow pycodestyle scope to 'app test' to match mypy, avoiding scanning docker/ and example_configurations/ directories - Fix broken #running-examples anchor and stale Dockerfile path in example_configurations/repository_standalone/README.md --- .github/workflows/ci.yml | 4 ++-- server/example_configurations/repository_standalone/README.md | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 781c6a7c7..dedb827a0 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -309,9 +309,9 @@ jobs: python -m mypy app test - name: Check code style with PyCodestyle run: | - python -m pycodestyle --count --max-line-length 120 . + python -m pycodestyle --count --max-line-length 120 app test - server-package: + server-repository-docker: # This job checks if we can build our server package runs-on: ubuntu-latest defaults: diff --git a/server/example_configurations/repository_standalone/README.md b/server/example_configurations/repository_standalone/README.md index 67992e0be..ce84c5f67 100644 --- a/server/example_configurations/repository_standalone/README.md +++ b/server/example_configurations/repository_standalone/README.md @@ -9,7 +9,7 @@ $ docker compose up Input files are read from `./input` and stored persistently under `./storage` on your host system. The server can be accessed at http://localhost:8080/api/v3.0/ from your host system. -To get a different setup, the `compose.yaml` file can be adapted using the options described in the main server [README.md](../../README.md#options), similar to the third [running example](#running-examples). +To get a different setup, the `compose.yaml` file can be adapted using the options described in the main server [README.md](../../README.md#options). -Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/Dockerfile`, as the build context must be set to the parent directory of `/server` to allow access to the local `/sdk`. +Note that the `Dockerfile` has to be specified explicitly via `dockerfile: server/docker/repository/Dockerfile`, as the build context must be set to the repository root to allow access to the local `/sdk`.