diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml deleted file mode 100644 index 11c0e554..00000000 --- a/.github/workflows/build.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Build - -on: - push: - branches: [ main, development ] - pull_request: - branches: [ main, development ] - -jobs: - build: - runs-on: ubuntu-latest - - steps: - - uses: actions/checkout@v4 - - - name: Install dependencies and build - run: sudo ./install.sh diff --git a/.github/workflows/docker.yml b/.github/workflows/docker.yml new file mode 100644 index 00000000..1828c119 --- /dev/null +++ b/.github/workflows/docker.yml @@ -0,0 +1,42 @@ +name: Build and Publish Multi-Arch Docker Image + +on: + push: + branches: [ main, development ] + pull_request: + branches: [ main, development ] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + permissions: + contents: read + packages: write + + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Login to GHCR + uses: docker/login-action@v3 + with: + registry: ghcr.io + username: ${{ secrets.GHCR_USERNAME }} + password: ${{ secrets.GHCR_TOKEN }} + + - name: Build and Push Multi-Arch Image + uses: docker/build-push-action@v6 + with: + context: . + push: true + platforms: linux/amd64,linux/arm64,linux/arm/v7 + tags: | + ghcr.io/autonomy-logic/openplc-runtime:latest + ghcr.io/autonomy-logic/openplc-runtime:${{ github.sha }} diff --git a/.gitignore b/.gitignore index 1d594b08..663e2838 100644 --- a/.gitignore +++ b/.gitignore @@ -6,6 +6,7 @@ .vscode/ #.*/ /venvs/ +.venv/ __pycache__/ .clinerules diff --git a/Dockerfile b/Dockerfile index 6d6636a1..5528a3ff 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,19 +1,24 @@ -# Dockerfile -FROM debian:bookworm-slim +# syntax=docker/dockerfile:1 -RUN apt-get update && apt-get install -y \ - python3 python3-venv python3-pip bash \ - pkg-config \ - && rm -rf /var/lib/apt/lists/* +FROM debian:bookworm-slim WORKDIR /workdir + +# Copy source code COPY . . -RUN mkdir -p /var/run/runtime + +# Setup runtime directory and permissions +RUN mkdir -p /var/run/runtime && \ + chmod +x install.sh scripts/* start_openplc.sh + # Clean any existing build artifacts to ensure clean Docker build -RUN rm -rf build/ venvs/ 2>/dev/null || true -RUN chmod +x install.sh scripts/* start_openplc.sh +RUN rm -rf build/ venvs/ .venv/ 2>/dev/null || true + +# Run installation script RUN ./install.sh +# Expose webserver port EXPOSE 8443 +# Default execution - Start OpenPLC Runtime CMD ["bash", "./start_openplc.sh"] diff --git a/core/src/drivers/plugin_driver.c b/core/src/drivers/plugin_driver.c index 26ad4a14..c10a400a 100644 --- a/core/src/drivers/plugin_driver.c +++ b/core/src/drivers/plugin_driver.c @@ -160,7 +160,9 @@ int plugin_driver_init(plugin_driver_t *driver) // Call the Python init function with proper capsule PyObject *result = PyObject_CallFunctionObjArgs(plugin->python_plugin->pFuncInit, args, NULL); - Py_SET_REFCNT(args, UINT64_MAX); + + // Store the capsule reference for the lifetime of the plugin + plugin->python_plugin->args_capsule = args; if (!result) { @@ -639,6 +641,7 @@ static void python_plugin_cleanup(plugin_instance_t *plugin) Py_XDECREF(plugin->python_plugin->pFuncStop); Py_XDECREF(plugin->python_plugin->pFuncCleanup); Py_XDECREF(plugin->python_plugin->pModule); + Py_XDECREF(plugin->python_plugin->args_capsule); free(plugin->python_plugin); plugin->python_plugin = NULL; diff --git a/core/src/drivers/python_plugin_bridge.h b/core/src/drivers/python_plugin_bridge.h index 1c00060d..b0442e0f 100644 --- a/core/src/drivers/python_plugin_bridge.h +++ b/core/src/drivers/python_plugin_bridge.h @@ -15,6 +15,7 @@ typedef struct PyObject *pFuncStart; PyObject *pFuncStop; PyObject *pFuncCleanup; + PyObject *args_capsule; // Capsule containing plugin_runtime_args_t for lifetime management } python_binds_t; -#endif // __PYTHON_PLUGIN_BRIDGE_H \ No newline at end of file +#endif // __PYTHON_PLUGIN_BRIDGE_H diff --git a/install.sh b/install.sh index 48a22872..c3f0d82a 100755 --- a/install.sh +++ b/install.sh @@ -70,6 +70,7 @@ install_deps_apt() { gcc \ make \ cmake \ + pkg-config \ && rm -rf /var/lib/apt/lists/* } @@ -167,4 +168,4 @@ else echo "ERROR: Build process failed!" >&2 echo "Please check the error messages above for details." >&2 exit 1 -fi \ No newline at end of file +fi diff --git a/webserver/app.py b/webserver/app.py index c5504e22..89555228 100644 --- a/webserver/app.py +++ b/webserver/app.py @@ -240,6 +240,7 @@ def run_https(): ssl_context=context, use_reloader=False, log_output=False, + allow_unsafe_werkzeug=True, ) except FileNotFoundError: diff --git a/webserver/debug_websocket.py b/webserver/debug_websocket.py index d85763ed..89c2e430 100644 --- a/webserver/debug_websocket.py +++ b/webserver/debug_websocket.py @@ -10,6 +10,7 @@ from flask_jwt_extended import decode_token from flask_socketio import SocketIO, emit from jwt.exceptions import ExpiredSignatureError, InvalidTokenError + from webserver.logger import get_logger logger, _ = get_logger("debug_ws", use_buffer=True)