diff --git a/.github/workflows/build_docs.yml b/.github/workflows/build_docs.yml
new file mode 100644
index 00000000..95a1c40d
--- /dev/null
+++ b/.github/workflows/build_docs.yml
@@ -0,0 +1,72 @@
+name: Build Docs
+
+on:
+ workflow_dispatch:
+ inputs:
+ publish_doc:
+ type: boolean
+ description: 'If set to true, then the documentation will be published.'
+ default: false
+ required: false
+ workflow_call:
+ inputs:
+ publish_doc:
+ type: boolean
+ description: 'If set to true, then the documentation will be published.'
+ default: false
+ required: false
+
+jobs:
+ build_docs:
+ name: Build Documentation
+ runs-on: [self-hosted, Linux, Ubuntu24.04]
+ steps:
+ - name: Checkout repository and submodules
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+
+ - name: Install Python Dependencies
+ run: |
+ python3 -m venv env
+ source env/bin/activate
+ pip install sphinx
+
+ - name: Get Current Python Version
+ run: |
+ version="$(( python3 --version 2>&1 || echo ) | grep -Po '(?<=Python )\d+\.\d+' || true)"
+ echo "Found version $version"
+ echo "PYTHON_VERSION=$version" >> $GITHUB_ENV
+
+ - name: Download Python Artifact
+ uses: actions/download-artifact@v4
+ with:
+ name: pymgclient-linux-${{ env.PYTHON_VERSION }}
+ path: ./dist
+
+ - name: Install Pymgclient
+ run: |
+ source env/bin/activate
+ pip install dist/*.whl
+
+ - name: Build docs
+ run: |
+ source env/bin/activate
+ cd docs
+ make html
+ rm build/html/.buildinfo
+ touch build/html/.nojekyll
+
+ - name: Deploy docs
+ if: ${{ github.event.inputs.publish_doc == 'true' }}
+ uses: peaceiris/actions-gh-pages@v3
+ with:
+ github_token: ${{ secrets.GITHUB_TOKEN }}
+ publish_dir: ./docs/build/html
+
+ - name: Cleanup
+ if: always()
+ run: |
+ rm -rf env || true
\ No newline at end of file
diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml
index 4bff4761..df197471 100644
--- a/.github/workflows/ci.yml
+++ b/.github/workflows/ci.yml
@@ -1,188 +1,69 @@
name: CI
+concurrency:
+ group: ${{ github.head_ref || github.sha }}
+ cancel-in-progress: true
on:
- push:
+ pull_request:
workflow_dispatch:
+ inputs:
+ test_linux:
+ type: boolean
+ default: true
+ description: "Run Linux Build and Test"
+ test_windows:
+ type: boolean
+ default: true
+ description: "Run Windows Build and Test"
+ test_macintosh:
+ type: boolean
+ default: true
+ description: "Run Mac OS Build"
+ build_source_dist:
+ type: boolean
+ default: true
+ description: "Build Source Distribution"
+ upload_artifacts:
+ type: boolean
+ default: true
+ description: "Upload Artifacts"
+
schedule:
- - cron: "0 1 * * *"
-
+ - cron: "0 0 * * 0"
jobs:
- build_and_test_ubuntu:
- strategy:
- matrix:
- include:
- - {platform: 'ubuntu-20.04', python_version: '3.8', mgversion: '2.0.1'}
- - {platform: 'ubuntu-20.04', python_version: '3.8', mgversion: '2.5.2'}
- - {platform: 'ubuntu-20.04', python_version: '3.8', mgversion: '2.10.1'}
- - {platform: 'ubuntu-22.04', python_version: '3.10', mgversion: '2.5.2'}
- - {platform: 'ubuntu-22.04', python_version: '3.10', mgversion: '2.10.1'}
- runs-on: ${{ matrix.platform }}
- steps:
- - name: Cache Memgraph community installer
- id: cache-memgraph-community
- uses: actions/cache@v1
- with:
- path: ~/memgraph
- key: cache-memgraph-v${{ matrix.mgversion }}-${{ matrix.platform }}-community-installer-v3
- - name: Download Memgraph
- if: steps.cache-memgraph-community.outputs.cache-hit != 'true'
- run: |
- mkdir ~/memgraph
- MEMGRAPH_PACKAGE_NAME="memgraph_${{ matrix.mgversion }}-1_amd64.deb"
- curl -L https://download.memgraph.com/memgraph/v${{ matrix.mgversion }}/${{ matrix.platform }}/${MEMGRAPH_PACKAGE_NAME} > ~/memgraph/memgraph.deb
- - name: Install system dependencies
- run: |
- sudo apt install -y libpython${{ matrix.python_version }} python3-pip python3-setuptools
- sudo pip3 install --upgrade networkx pytest pyopenssl sphinx
- sudo ln -s /dev/null /etc/systemd/system/memgraph.service # Prevents Memgraph from starting.
- sudo dpkg -i ~/memgraph/memgraph.deb
- - uses: actions/checkout@v2
- with:
- submodules: true
- - name: Build source distribution
- run: python3 setup.py sdist
- - name: Install pymgclient with dynamic OpenSSL for Memgraph 1.3.0
- if: matrix.mgversion == '1.3.0'
- run: python3 -m pip install --global-option=build_ext --global-option="--static-openssl=false" ./dist/pymgclient-*
- - name: Install pymgclient
- if: matrix.mgversion != '1.3.0'
- run: python3 -m pip install ./dist/pymgclient-*
- - name: Import mgclient to validate installation
- run: python3 -c "import mgclient"
- - name: Run tests
- run: |
- MEMGRAPH_PORT=10000
- if [[ "${{ matrix.mgversion }}" != 1* ]]; then
- python3 -m pytest -v
- else
- python3 -m pytest -v -m "not temporal"
- fi
- - name: Build docs
- run: |
- cd docs
- make html
- - name: Save source distribution package
- uses: actions/upload-artifact@v2
- with:
- name: pymgclient
- path: dist/
+ weekly_build:
+ if: ${{ github.event_name == 'schedule' }}
+ name: Weekly Build
+ uses: "./.github/workflows/reusable_buildtest.yml"
+ with:
+ test_linux: true
+ test_windows: true
+ test_macintosh: true
+ build_source_dist: false
+ upload_artifacts: false
+ secrets: inherit
- build_and_test_windows:
- runs-on: windows-2019
- strategy:
- matrix:
- arch:
- - { mingw: "64", msys: x86_64, python: "x64" }
- python_version:
- - '3.7'
- - '3.10'
- env:
- # TODO(gitbuda): Fix "The file cannot be accessed by the system... rocksdb_durability"
- MG_VERSION: 2.8.0
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: true
- - name: Setup python
- uses: actions/setup-python@v2.2.2
- with:
- python-version: ${{ matrix.python_version }}
- architecture: ${{ matrix.arch.python }}
- - uses: msys2/setup-msys2@v2
- with:
- msystem: MINGW${{ matrix.arch.mingw }}
- update: true
- release: false
- install: git mingw-w64-${{ matrix.arch.msys }}-toolchain mingw-w64-${{ matrix.arch.msys }}-cmake mingw-w64-${{ matrix.arch.msys }}-openssl
- - name: Add mingw${{ matrix.arch.mingw }} to PATH
- run: |
- # First make sure python would resolve to the windows native python, not mingw one
- echo "C:\msys64\mingw${{ matrix.arch.mingw }}\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- echo "${{ env.pythonLocation }}" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- - name: Print OpenSSL version
- shell: msys2 {0}
- run: |
- openssl version -a
- - uses: Vampire/setup-wsl@v1
- with:
- distribution: Ubuntu-20.04
- - name: Download, install and run Memgraph under WSL
- shell: wsl-bash {0} # root shell
- run: |
- mkdir ~/memgraph
- curl -L https://download.memgraph.com/memgraph/v${{ env.MG_VERSION }}/ubuntu-20.04/memgraph_${{ env.MG_VERSION }}-1_amd64.deb --output ~/memgraph/memgraph.deb
- dpkg -i ~/memgraph/memgraph.deb
- openssl req -x509 -newkey rsa:4096 -days 3650 -nodes -keyout key.pem -out cert.pem -subj "/C=GB/ST=London/L=London/O=Testing Corp./CN=PymgclientTest"
- nohup /usr/lib/memgraph/memgraph --bolt-port 7687 --bolt-cert-file="cert.pem" --bolt-key-file="key.pem" --data-directory="~/memgraph/data" --storage-properties-on-edges=true --storage-snapshot-interval-sec=0 --storage-wal-enabled=false --storage-recover-on-startup=false --storage-snapshot-on-exit=false --telemetry-enabled=false --log-file='' &
- sleep 1 # Wait for Memgraph a bit.
- - run: python -m pip install -U pip wheel setuptools pytest pyopenssl
- - name: Build pymgclient
- run: python setup.py bdist_wheel
- - name: Install pymgclient
- run: python -m pip install --verbose -f dist --no-index pymgclient
- env:
- VERBOSE: 1
- - name: Run tests
- run: |
- python3 -m pytest -v
- env:
- MEMGRAPH_HOST: "localhost"
- MEMGRAPH_STARTED_WITH_SSL:
- - name: Save wheel package
- uses: actions/upload-artifact@v2
- with:
- name: pymgclient-win${{ matrix.arch.mingw }}-${{ matrix.python_version }}
- path: dist/
+ pr_test:
+ if: ${{ github.event_name == 'pull_request' }}
+ name: Pull Request Tests
+ uses: "./.github/workflows/reusable_buildtest.yml"
+ with:
+ test_linux: true
+ test_windows: true
+ test_macintosh: true
+ build_source_dist: true
+ upload_artifacts: false
+ secrets: inherit
- build_macos:
- strategy:
- fail-fast: false
- matrix:
- platform: [macos-13, macos-12, macos-11]
- python_version:
- - '3.8'
- - '3.10'
- include:
- - {platform: [macOS-12.1, ARM64, self-hosted], python_version: '3.10'}
- - {platform: [macOS-12.1, ARM64, self-hosted], python_version: '3.8'}
- runs-on: ${{ matrix.platform }}
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: true
- - name: Install python and OpenSSL
- run: |
- brew install python@${{ matrix.python_version }} openssl@1.1
- brew link --force --overwrite openssl@1.1
- openssl version -a
- - name: Manage OpenSSL 3 on ARM machines
- if: ${{ contains(matrix.platform, 'ARM64') }}
- run: |
- brew install openssl@3
- brew link --force --overwrite openssl@3
- openssl version -a
- - name: Make used python version default
- run: |
- brew unlink python@3 && brew link --force python@${{ matrix.python_version }}
- python${{ matrix.python_version }} --version
- - name: Install pytest and pyopenssl
- run: python${{ matrix.python_version }} -m pip install pyopenssl pytest
- - name: Build pymgclient
- run: python${{ matrix.python_version }} setup.py bdist_wheel
- - name: Install pymgclient
- run: python${{ matrix.python_version }} -m pip install -f dist --no-index pymgclient
- - name: Import mgclient to validate installation
- run: python${{ matrix.python_version }} -c "import mgclient"
- - name: Save artifact name on x86 machines
- if: ${{ !contains(matrix.platform, 'ARM64') }}
- run: echo "OS_TYPE=${{ matrix.platform }}" >> $GITHUB_ENV
- - name: Save artifact name on ARM64 machines
- if: ${{ contains(matrix.platform, 'ARM64') }}
- # Convert macOS-11.6-ARM64 to macos-11.6-arm64 to be consistent with full lowercase naming
- run: echo OS_TYPE=`echo "${{ matrix.platform[0] }}-${{ matrix.platform[1] }}" | tr "[:upper:]" "[:lower:]"` >> $GITHUB_ENV
- - name: Save wheel package
- uses: actions/upload-artifact@v2
- with:
- name: pymgclient-${{ env.OS_TYPE }}-${{ matrix.python_version }}
- path: dist/
+ manual_test:
+ if: ${{ github.event_name == 'workflow_dispatch' }}
+ name: Manual Test
+ uses: "./.github/workflows/reusable_buildtest.yml"
+ with:
+ test_linux: ${{ inputs.test_linux }}
+ test_windows: ${{ inputs.test_windows }}
+ test_macintosh: ${{ inputs.test_macintosh }}
+ build_source_dist: ${{ inputs.build_source_dist }}
+ upload_artifacts: ${{ inputs.upload_artifacts }}
+ secrets: inherit
diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml
index a2fee6bb..f7485e61 100644
--- a/.github/workflows/release.yml
+++ b/.github/workflows/release.yml
@@ -1,4 +1,6 @@
name: Release
+
+
on:
workflow_dispatch:
inputs:
@@ -7,196 +9,76 @@ on:
type: string
publish_doc:
description: 'If set to true, then the documentation will be published.'
- default: 'false'
+ default: false
required: false
- openssl_1_version:
- description: 'The expected version of OpenSSL 1, e.g.: 1.1.1o.'
- required: true
- type: string
- openssl_3_version:
- description: 'The expected version of OpenSSL 3, e.g.: 3.0.3.'
- required: true
- type: string
+ type: boolean
+ publish_pypi:
+ default: false
+ type: boolean
+ description: "Attempt to publish packages to PyPI"
+ test:
+ default: true
+ description: "Run test release (no docs, publish to test.pypi.org)"
+ type: boolean
+
+
env:
PYMGCLIENT_OVERRIDE_VERSION: "${{ github.event.inputs.version }}"
jobs:
- build_and_test_ubuntu:
- strategy:
- matrix:
- include:
- - {platform: 'ubuntu-20.04', python_version: '3.8', mgversion: '2.10.1'}
- - {platform: 'ubuntu-22.04', python_version: '3.10', mgversion: '2.10.1'}
- runs-on: ${{ matrix.platform }}
- steps:
- - name: Install system dependencies (Ubuntu 20.04)
- run: |
- sudo apt install -y libpython${{ matrix.python_version }} python3-pip python3-setuptools
- sudo pip3 install --upgrade networkx pytest pyopenssl sphinx
- mkdir ~/memgraph
- curl -L https://download.memgraph.com/memgraph/v${{matrix.mgversion}}/ubuntu-20.04/memgraph_${{matrix.mgversion}}-1_amd64.deb > ~/memgraph/memgraph.deb
- sudo ln -s /dev/null /etc/systemd/system/memgraph.service # Prevents Memgraph from starting.
- sudo dpkg -i ~/memgraph/memgraph.deb
- - uses: actions/checkout@v2
- with:
- submodules: true
- - name: Build source distribution
- run: python3 setup.py sdist
- - name: Install pymgclient
- run: python3 -m pip install ./dist/pymgclient-*
- - name: Run tests
- run: MEMGRAPH_PORT=10000 python3 -m pytest
- - name: Build docs
- run: |
- cd docs
- make html
- rm build/html/.buildinfo
- touch build/html/.nojekyll
- - name: Save source distribution package
- uses: actions/upload-artifact@v2
- with:
- name: pymgclient-${{ github.event.inputs.version }}
- path: dist/
- - name: Deploy docs
- if: ${{ github.event.inputs.publish_doc == 'true' }}
- uses: peaceiris/actions-gh-pages@v3
- with:
- github_token: ${{ secrets.GITHUB_TOKEN }}
- publish_dir: ./docs/build/html
+ release_tests:
+ name: Release Package and Test
+ uses: "./.github/workflows/reusable_buildtest.yml"
+ with:
+ test_linux: true
+ test_windows: true
+ test_macintosh: true
+ build_source_dist: true
+ upload_artifacts: true
+ version: ${{ inputs.version }}
+ secrets: inherit
+
+ build_docs:
+ needs: [release_tests]
+ name: Build Docs
+ uses: "./.github/workflows/build_docs.yml"
+ with:
+ publish_doc: false
+ secrets: inherit
+
+ publish_artifacts:
+ name: Collect Artifacts
+ runs-on: ubuntu-24.04
+ needs: [release_tests, build_docs]
- build_windows_and_test:
- runs-on: windows-2019
- strategy:
- matrix:
- arch:
- - { mingw: "64", msys: x86_64, python: "x64"}
- python_version:
- - '3.7'
- - '3.8'
- - '3.9'
- - '3.10'
- # TODO(gitbuda): Fix "The file cannot be accessed by the system... rocksdb_durability"
- mgversion:
- - 2.8.0
steps:
- - uses: actions/checkout@v2
- with:
- submodules: true
- - name: Setup python
- uses: actions/setup-python@v2.2.2
- with:
- python-version: ${{ matrix.python_version }}
- architecture: ${{ matrix.arch.python }}
- - uses: msys2/setup-msys2@v2
+ - name: Download all artifacts
+ uses: actions/download-artifact@v4
with:
- msystem: MINGW${{ matrix.arch.mingw }}
- update: true
- release: false
- install: git mingw-w64-${{ matrix.arch.msys }}-toolchain mingw-w64-${{ matrix.arch.msys }}-cmake mingw-w64-${{ matrix.arch.msys }}-openssl
- - name: Add mingw${{ matrix.arch.mingw }} to PATH
- run: |
- # First make sure python would resolve to the windows native python, not mingw one
- echo "C:\msys64\mingw${{ matrix.arch.mingw }}\bin" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- echo "${{ env.pythonLocation }}" | Out-File -Append -FilePath $env:GITHUB_PATH -Encoding utf8
- - name: Print OpenSSL version
- shell: msys2 {0}
- run: |
- openssl version -a
- - name: Check version
- shell: msys2 {0}
- run: |
- [ `openssl version -v | cut -d ' ' -f 2` == "${{ github.event.inputs.openssl_1_version }}" ]
- - uses: Vampire/setup-wsl@v1
- with:
- distribution: Ubuntu-20.04
- - name: Download, install and run Memgraph under WSL
- shell: wsl-bash {0} # root shell
+ # omit ‘name’ to fetch *every* artifact uploaded earlier
+ path: ./downloaded-artifacts
+
+ - name: Move Artifacts
run: |
- mkdir ~/memgraph
- curl -L https://download.memgraph.com/memgraph/v${{matrix.mgversion}}/ubuntu-20.04/memgraph_${{matrix.mgversion}}-1_amd64.deb --output ~/memgraph/memgraph.deb
- dpkg -i ~/memgraph/memgraph.deb
- openssl req -x509 -newkey rsa:4096 -days 3650 -nodes -keyout key.pem -out cert.pem -subj "/C=GB/ST=London/L=London/O=Testing Corp./CN=PymgclientTest"
- nohup /usr/lib/memgraph/memgraph --bolt-port 7687 --bolt-cert-file="cert.pem" --bolt-key-file="key.pem" --data-directory="~/memgraph/data" --storage-properties-on-edges=true --storage-snapshot-interval-sec=0 --storage-wal-enabled=false --storage-recover-on-startup=false --storage-snapshot-on-exit=false --telemetry-enabled=false --log-file='' &
- sleep 1 # Wait for Memgraph a bit.
- - run: python -m pip install -U pip wheel setuptools pytest pyopenssl
- - name: Build pymgclient
- run: python setup.py bdist_wheel
- - name: Install pymgclient
- run: python -m pip install --verbose -f dist --no-index pymgclient
- env:
- VERBOSE: 1
- - name: Run tests
+ mkdir -p dist
+ mv -v downloaded-artifacts/*/* dist/
+
+ - name: Show contents
run: |
- python -m pytest -v
- env:
- MEMGRAPH_HOST: "localhost"
- MEMGRAPH_STARTED_WITH_SSL:
- - name: Save wheel package
- uses: actions/upload-artifact@v2
+ ls dist/
+
+ - name: Publish Package to PyPI
+ if: ${{ inputs.publish_pypi == true && inputs.test == false }}
+ uses: pypa/gh-action-pypi-publish@v1.4.2
with:
- name: pymgclient-${{ github.event.inputs.version }}-win${{ matrix.arch.mingw }}-${{ matrix.python_version }}
- path: dist/
+ user: __token__
+ password: ${{ secrets.PYPI_API_TOKEN }}
- build_macos:
- strategy:
- matrix:
- platform: [macos-11]
- python_version:
- - '3.7'
- - '3.8'
- - '3.9'
- - '3.10'
- include:
- - {platform: [macOS-11.6, ARM64, self-hosted], python_version: '3.10'}
- - {platform: [macOS-11.6, ARM64, self-hosted], python_version: '3.9'}
- - {platform: [macOS-11.6, ARM64, self-hosted], python_version: '3.8'}
- runs-on: ${{ matrix.platform }}
- steps:
- - uses: actions/checkout@v2
- with:
- submodules: true
- - name: Install python
- run: |
- brew update
- brew install python@${{ matrix.python_version }}
- - name: Manage OpenSSL 1 on x86 machines
- if: ${{ !contains(matrix.platform, 'ARM64') }}
- run: |
- brew uninstall -f openssl@3
- brew install openssl@1.1
- brew upgrade openssl@1.1
- brew link --force --overwrite openssl@1.1
- openssl version -a
- [ `openssl version -v | cut -d ' ' -f 2` == "${{ github.event.inputs.openssl_1_version }}" ]
- - name: Manage OpenSSL 3 on ARM machines
- if: ${{ contains(matrix.platform, 'ARM64') }}
- run: |
- brew install openssl@3
- brew upgrade openssl@3
- brew link --force --overwrite openssl@3
- openssl version -a
- [ `openssl version -v | cut -d ' ' -f 2` == "${{ github.event.inputs.openssl_3_version }}" ]
- - name: Make used python version default
- run: |
- brew unlink python@3 && brew link --force python@${{ matrix.python_version }}
- python${{ matrix.python_version }} --version
- - name: Install pytest and pyopenssl
- run: python${{ matrix.python_version }} -m pip install pyopenssl pytest
- - name: Build pymgclient
- run: python${{ matrix.python_version }} setup.py bdist_wheel
- - name: Install pymgclient
- run: python${{ matrix.python_version }} -m pip install -f dist --no-index pymgclient
- - name: Import mgclient to validate installation
- run: python${{ matrix.python_version }} -c "import mgclient"
- - name: Save artifact name on x86 machines
- if: ${{ !contains(matrix.platform, 'ARM64') }}
- run: echo "OS_TYPE=${{ matrix.platform }}" >> $GITHUB_ENV
- - name: Save artifact name on ARM64 machines
- if: ${{ contains(matrix.platform, 'ARM64') }}
- # Convert macOS-11.6-ARM64 to macos-11.6-arm64 to be consistent with full lowercase naming
- run: echo OS_TYPE=`echo "${{ matrix.platform[0] }}-${{ matrix.platform[1] }}" | tr "[:upper:]" "[:lower:]"` >> $GITHUB_ENV
- - name: Save wheel package
- uses: actions/upload-artifact@v2
+ - name: Publish Package to PyPI (TEST)
+ if: ${{ inputs.publish_pypi == true && inputs.test }}
+ uses: pypa/gh-action-pypi-publish@v1.4.2
with:
- name: pymgclient-${{ github.event.inputs.version }}-${{ env.OS_TYPE }}-${{ matrix.python_version }}
- path: dist/
+ user: __token__
+ password: ${{ secrets.TEST_PYPI_API_TOKEN }}
+ repository_url: https://test.pypi.org/legacy/
+ verbose: true
\ No newline at end of file
diff --git a/.github/workflows/reusable_buildtest.yml b/.github/workflows/reusable_buildtest.yml
new file mode 100644
index 00000000..d3b4cee5
--- /dev/null
+++ b/.github/workflows/reusable_buildtest.yml
@@ -0,0 +1,444 @@
+name: Reusable Build and Test
+
+
+on:
+ workflow_call:
+ inputs:
+ test_linux:
+ type: boolean
+ default: true
+ description: "Run Linux Build and Test"
+ test_windows:
+ type: boolean
+ default: true
+ description: "Run Windows Build and Test"
+ test_macintosh:
+ type: boolean
+ default: true
+ description: "Run Mac OS Build"
+ build_source_dist:
+ type: boolean
+ default: true
+ description: "Build Source Distribution"
+ upload_artifacts:
+ type: boolean
+ default: true
+ description: "Upload Artifacts"
+ version:
+ required: false
+ type: string
+
+jobs:
+ build_and_test_linux:
+ if: ${{ inputs.test_linux }}
+ name: "Build and test on Linux 👍"
+ strategy:
+ fail-fast: false
+ matrix:
+ include:
+ - {platform: 'ubuntu-22.04', python_version: '3.10', mgversion: 'latest'}
+ - {platform: 'ubuntu-24.04', python_version: '3.10', mgversion: 'latest'}
+ - {platform: 'ubuntu-24.04', python_version: '3.11', mgversion: 'latest'}
+ - {platform: 'ubuntu-24.04', python_version: '3.12', mgversion: 'latest'}
+ - {platform: 'ubuntu-24.04', python_version: '3.13', mgversion: 'latest'}
+ - {platform: 'fedora-41', python_version: '3.13', mgversion: 'latest'}
+ runs-on: [self-hosted, X64]
+ steps:
+ - name: Checkout repository and submodules
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Set Memgraph Version
+ run: |
+ if [[ "${{ matrix.mgversion }}" == "latest" ]]; then
+ mgversion=$(./tools/get_memgraph_version.sh)
+ else
+ mgversion="${{ matrix.mgversion }}"
+ fi
+ echo "MGVERSION=$mgversion" >> $GITHUB_ENV
+
+ - name: Download Memgraph
+ run: |
+ if [[ "${{ matrix.platform }}" == "fedora-41" ]]; then
+ MEMGRAPH_PACKAGE_NAME="memgraph-${{ env.MGVERSION }}_1-1.x86_64.rpm"
+ LOCAL_PACKAGE_NAME=memgraph.rpm
+ else
+ MEMGRAPH_PACKAGE_NAME="memgraph_${{ env.MGVERSION }}-1_amd64.deb"
+ LOCAL_PACKAGE_NAME=memgraph.deb
+ fi
+ curl -L "https://download.memgraph.com/memgraph/v${{ env.MGVERSION }}/${{ matrix.platform }}/${MEMGRAPH_PACKAGE_NAME}" > "${LOCAL_PACKAGE_NAME}"
+ echo "LOCAL_PACKAGE_NAME=$LOCAL_PACKAGE_NAME" >> $GITHUB_ENV
+
+ - name: Set up Docker Buildx
+ id: buildx
+ uses: docker/setup-buildx-action@v3
+
+ - name: Log in to Docker Hub
+ uses: docker/login-action@v3
+ with:
+ username: ${{ secrets.DOCKERHUB_USERNAME }}
+ password: ${{ secrets.DOCKERHUB_TOKEN }}
+
+ - name: Launch Docker Container
+ run: |
+ platform="${{ matrix.platform }}"
+ tag=${platform//-/:}
+ if [[ "${{ inputs.version }}" != "" ]]; then
+ echo "Building version ${{ inputs.version }}"
+ docker run -d --rm \
+ -e PYMGCLIENT_OVERRIDE_VERSION=${{ inputs.version }} \
+ --name testcontainer "$tag" sleep infinity
+ else
+ docker run -d --rm --name testcontainer "$tag" sleep infinity
+ fi
+
+ - name: Set Environment Variables
+ run: |
+ break_packages=""
+ static_ssl=""
+ if [[ "${{ matrix.platform }}" == "ubuntu-24.04" ]]; then
+ # this option is specific to ubuntu, not fedora
+ break_packages="--break-system-packages"
+ elif [[ "${{ matrix.platform }}" == fedora* ]]; then
+ # rpm distros do not ship with static lib for openssl
+ static_ssl="build_ext --static-openssl=False"
+ fi
+ echo "BREAK_PACKAGES=$break_packages" >> $GITHUB_ENV
+ echo "STATIC_SSL=$static_ssl" >> $GITHUB_ENV
+
+ - name: Copy Repo Into Container
+ run: |
+ docker cp . testcontainer:/pymgclient
+
+ - name: Install system dependencies
+ run: |
+ docker cp $LOCAL_PACKAGE_NAME testcontainer:/$LOCAL_PACKAGE_NAME
+
+ # Prevents Memgraph from starting.
+ docker exec -i testcontainer \
+ bash -c "mkdir -p /etc/systemd/system && ln -s /dev/null /etc/systemd/system/memgraph.service"
+
+ # Install dependencies
+ docker exec -i testcontainer \
+ bash -c "cd /pymgclient && ./tools/install_linux_deps.sh ${{ matrix.platform }} --python-version ${{ matrix.python_version }} --force-update"
+
+ # Install Memgraph package
+ if [[ "${{ matrix.platform }}" == "fedora-41" ]]; then
+ docker exec -i testcontainer \
+ bash -c "dnf install -y /$LOCAL_PACKAGE_NAME"
+ else
+ docker exec -i testcontainer \
+ bash -c "dpkg -i /$LOCAL_PACKAGE_NAME"
+ fi
+ rm -v $LOCAL_PACKAGE_NAME
+
+
+ - name: Build Python Wheel
+ run: |
+ docker exec -i testcontainer \
+ bash -c "cd /pymgclient && python${{ matrix.python_version }} setup.py ${{ env.STATIC_SSL }} bdist_wheel"
+
+ - name: Audit Wheel
+ if: ${{ matrix.platform == 'ubuntu-24.04' }}
+ run: |
+ docker exec -i testcontainer \
+ bash -c "cd /pymgclient && auditwheel repair dist/*.whl --plat manylinux_2_39_x86_64 -w dist/ && rm dist/*linux_x86_64.whl"
+
+
+ - name: Install pymgclient
+ run: |
+ docker exec -i testcontainer \
+ bash -c "python${{ matrix.python_version }} -m pip install ./pymgclient/dist/pymgclient-* ${{ env.BREAK_PACKAGES }}"
+
+ - name: Import mgclient to validate installation
+ run: |
+ docker exec -i testcontainer \
+ bash -c "python${{ matrix.python_version }} -c 'import mgclient'"
+
+ - name: Run tests
+ run: |
+ MEMGRAPH_PORT=10000 # what's this for?
+
+ docker exec -i testcontainer \
+ bash -c "cd /pymgclient && python${{ matrix.python_version }} -m pytest -v"
+
+ - name: Build docs
+ run: |
+ docker exec -i testcontainer \
+ bash -c "cd /pymgclient/docs && make html"
+
+ - name: Copy Package
+ run: |
+ docker cp testcontainer:/pymgclient/dist .
+
+ - name: Save source distribution package
+ if: ${{ inputs.upload_artifacts && matrix.platform == 'ubuntu-24.04' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: pymgclient-linux-${{ matrix.python_version }}
+ path: dist/
+
+ - name: Cleanup
+ if: always()
+ run: |
+ docker stop testcontainer || echo "Container does not exist"
+ docker wait testcontainer || echo "Container does not exist"
+ docker rmi ${{ env.MGVERSION }} || echo "Image does not exist"
+
+ build_source_dist:
+ if: ${{ inputs.build_source_dist }}
+ name: Build Source Distribution
+ runs-on: [self-hosted, X64, Ubuntu24.04]
+ steps:
+ - name: Checkout repository and submodules
+ uses: actions/checkout@v4
+ with:
+ fetch-depth: 0
+ submodules: recursive
+
+ - name: Set override version if provided
+ if: ${{ inputs.version != '' }}
+ run: |
+ echo "Building version ${{ inputs.version }}"
+ echo "PYMGCLIENT_OVERRIDE_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
+
+ - name: Create Python Virtual Environment
+ run: |
+ python3 -m venv env
+ source env/bin/activate
+ pip install setuptools wheel
+
+ - name: Build Python Source Distribution
+ run: |
+ source env/bin/activate
+ python setup.py sdist
+
+ - name: Save source distribution package
+ if: ${{ inputs.upload_artifacts }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: pymgclient-linux-sdist
+ path: dist
+
+ - name: Cleanup
+ if: always()
+ run: |
+ rm -fr env || true
+ rm -fr dist || true
+
+
+
+ build_and_test_windows:
+ if: ${{ inputs.test_windows }}
+ name: Build and Test on Windows
+ runs-on: ${{ matrix.os }}
+ strategy:
+ fail-fast: false
+ matrix:
+ os: [windows-2022, windows-2025]
+ python-version: ["3.10", "3.11", "3.12", "3.13"]
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v4
+ with:
+ submodules: true
+
+ - name: Set override version if provided
+ if: ${{ inputs.version != '' }}
+ shell: bash
+ run: |
+ echo "Building version ${{ inputs.version }}"
+ echo "PYMGCLIENT_OVERRIDE_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
+
+ - name: Set up MSYS2
+ uses: msys2/setup-msys2@v2
+ with:
+ msystem: MINGW64
+ install: >
+ git
+ mingw-w64-x86_64-gcc
+ mingw-w64-x86_64-cmake
+ mingw-w64-x86_64-make
+ mingw-w64-x86_64-openssl
+
+ - name: Add MSYS2 mingw64/bin to PATH
+ shell: msys2 {0}
+ run: |
+ echo "/mingw64/bin" >> $GITHUB_PATH
+
+
+ - name: Set up Windows Python
+ uses: actions/setup-python@v5
+ with:
+ python-version: ${{ matrix.python-version }}
+ architecture: x64
+
+ - name: Add Windows Python to PATH
+ shell: msys2 {0}
+ run: |
+ echo "$pythonLocation" >> $GITHUB_PATH
+ env:
+ pythonLocation: ${{ env.pythonLocation }}
+
+
+ - name: Install Python build tools
+ shell: msys2 {0}
+ run: |
+ export PATH="$(cygpath -u "$pythonLocation"):$PATH"
+ python -m pip install --upgrade pip setuptools wheel pyopenssl pytest
+ env:
+ pythonLocation: ${{ env.pythonLocation }}
+
+ - name: Build pymgclient Wheel
+ shell: msys2 {0}
+ run: |
+ export PATH="$(cygpath -u "$pythonLocation"):$PATH"
+ python setup.py bdist_wheel
+ env:
+ pythonLocation: ${{ env.pythonLocation }}
+
+ - name: Install built wheel
+ shell: msys2 {0}
+ run: |
+ export PATH="$(cygpath -u "$pythonLocation"):$PATH"
+ python -m pip install dist/*.whl
+ env:
+ pythonLocation: ${{ env.pythonLocation }}
+
+ - name: Setup WSL Ubuntu
+ uses: Vampire/setup-wsl@v5
+ with:
+ distribution: Ubuntu-24.04
+
+ - name: Set Memgraph Version
+ shell: bash -l {0}
+ run: |
+ mgversion=$(./tools/get_memgraph_version.sh)
+ echo "MGVERSION=$mgversion" >> $GITHUB_ENV
+
+ - name: Install and Run Memgraph in WSL
+ shell: wsl-bash {0}
+ run: |
+ mkdir -p $HOME/memgraph/data
+ sudo apt update
+ sudo apt install -y curl
+ curl -L https://download.memgraph.com/memgraph/v${{ env.MGVERSION }}/ubuntu-24.04/memgraph_${{ env.MGVERSION }}-1_amd64.deb -o memgraph.deb
+ sudo mkdir -p /etc/systemd/system && sudo ln -s /dev/null /etc/systemd/system/memgraph.service # Prevents Memgraph from starting.
+ sudo apt install ./memgraph.deb
+ openssl req -x509 -newkey rsa:4096 -days 3650 -nodes -keyout key.pem -out cert.pem -subj "/C=GB/ST=London/L=London/O=Testing Corp./CN=PymgclientTest"
+ nohup /usr/lib/memgraph/memgraph --bolt-port 7687 --bolt-cert-file="cert.pem" --bolt-key-file="key.pem" --data-directory="~/memgraph/data" --storage-properties-on-edges=true --storage-snapshot-interval-sec=0 --storage-wal-enabled=false --storage-snapshot-on-exit=false --telemetry-enabled=false --log-file='' &
+
+ # sleep here instead of using script because it just doesn't work in Windows
+ sleep 3
+ # sed $'s/\r$//' ./tools/wait_for_memgraph.sh > ./tools/wait_for_memgraph.unix.sh
+ # bash ./tools/wait_for_memgraph.unix.sh localhost
+
+
+ - name: Run Tests
+ shell: msys2 {0}
+ run: |
+ export PATH="$(cygpath -u "$pythonLocation"):/mingw64/bin:$PATH"
+ echo $PATH
+ python -m pytest -v
+
+ env:
+ pythonLocation: ${{ env.pythonLocation }}
+ MEMGRAPH_HOST: localhost
+ MEMGRAPH_STARTED_WITH_SSL:
+
+
+ - name: Upload Wheel Artifact
+ if: ${{ inputs.upload_artifacts && matrix.os == 'windows-2025' }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: pymgclient-win-${{ matrix.python-version }}
+ path: dist/
+
+
+ # NOTE: Cannot run tests on Mac OS runners because:
+ # - GitHub hosted runners don't have docker
+ # - self-hosted runner unable to log in to docker due to requiring a password to unlock the keychain
+ # Also - github hosted macos runners after macos-13 are all arm64
+ build_macos:
+ if: ${{ inputs.test_macintosh }}
+ name: Build and test on MacOS
+ strategy:
+ fail-fast: false
+ matrix:
+ platform: [macos-15, macos-14]
+ python_version:
+ - '3.10'
+ - '3.11'
+ - '3.12'
+ - '3.13'
+ include:
+ - {platform: [macos-12, ARM64, self-hosted], python_version: '3.10'}
+ - {platform: [macos-12, ARM64, self-hosted], python_version: '3.11'}
+ - {platform: [macos-12, ARM64, self-hosted], python_version: '3.12'}
+ - {platform: [macos-12, ARM64, self-hosted], python_version: '3.13'}
+ runs-on: ${{ matrix.platform }}
+ steps:
+ - uses: actions/checkout@v2
+ with:
+ submodules: true
+
+ - name: Set override version if provided
+ if: ${{ inputs.version != '' }}
+ run: |
+ echo "Building version ${{ inputs.version }}"
+ echo "PYMGCLIENT_OVERRIDE_VERSION=${{ inputs.version }}" >> $GITHUB_ENV
+
+ - name: Install python and OpenSSL
+ run: |
+ brew install python@${{ matrix.python_version }} openssl@1.1
+ brew link --force --overwrite openssl@1.1
+ openssl version -a
+
+ - name: Manage OpenSSL 3 on ARM machines
+ if: ${{ contains(matrix.platform, 'ARM64') }}
+ run: |
+ brew install openssl@3
+ brew link --force --overwrite openssl@3
+ openssl version -a
+
+ - name: Make used python version default
+ run: |
+ brew unlink python@3 && brew link --force python@${{ matrix.python_version }}
+ python${{ matrix.python_version }} --version
+
+ - name: Create Virtual Env
+ run: |
+ python${{ matrix.python_version }} -m venv env
+
+ - name: Install pytest and pyopenssl
+ run: |
+ export PIP_BREAK_SYSTEM_PACKAGES=1
+ source env/bin/activate
+ python${{ matrix.python_version }} -m pip install pyopenssl pytest setuptools
+
+ - name: Build pymgclient
+ run: |
+ source env/bin/activate
+ python${{ matrix.python_version }} setup.py bdist_wheel
+
+ - name: Install pymgclient
+ run: |
+ export PIP_BREAK_SYSTEM_PACKAGES=1
+ source env/bin/activate
+ python${{ matrix.python_version }} -m pip install dist/*
+
+ - name: Import mgclient to validate installation
+ run: |
+ source env/bin/activate
+ python${{ matrix.python_version }} -c "import mgclient"
+
+ - name: Save wheel package
+ if: ${{ inputs.upload_artifacts }}
+ uses: actions/upload-artifact@v4
+ with:
+ name: pymgclient-${{ matrix.platform[0] || matrix.platform }}-${{ matrix.python_version }}
+ path: dist/
+
diff --git a/docs/source/introduction.rst b/docs/source/introduction.rst
index 3bffc137..85215de8 100644
--- a/docs/source/introduction.rst
+++ b/docs/source/introduction.rst
@@ -17,18 +17,19 @@ the Python programming language.
Installation
#############
-pymgclient has prebuilt binary packages for
+pymgclient has prebuilt binary packages for `Python
+ `_ 3.10 - 3.13 on
-* macOS BigSur (version 11) and newer on x86_64 with `Python
- `_ 3.7+
+* Linux amd64
-* macOS Monterey (version 12) and newer on arm64 with `Python
- `_ 3.8+
+* macOS 12 (Montery), 14 (Sonoma) and 15 (Sequoia) on arm64
-* Windows 10 x86_64 with `Python `_ 3.7+
+* Windows x86_64
-To intall pymgclient binaries on these platforms see `Install binaries`_ section
-or check `Install from source`_ for other platforms.
+To install pymgclient binaries on these platforms see `Install binaries`_ section.
+A source distribution is also provided for other distributions and can be installed
+using ``pip`` after installing `Build prerequisites`_; alternatively -
+see `Install from source`_.
Install binaries
################
@@ -40,7 +41,7 @@ Install binaries
pymgclient can use the latest version of OpenSSL that is installed on your
machine.
-On macOS run::
+On Linux and macOS run::
$ pip3 install --user pymgclient
@@ -52,6 +53,10 @@ Alternatively, on Windows, if the launcher is not installed, just run::
$ pip install --user pymgclient
+.. note::
+ Some platforms may require using the ``--break-system-packages`` flag.
+
+
Install from source
###################
@@ -60,9 +65,10 @@ pymgclient can be installed from source on:
* all platforms that have prebuilt binaries
* on various Linux distributions, including:
- * Ubuntu 18.04+
- * Debian 10+
- * CentOS 8+
+ * Ubuntu 22.04+
+ * Debian 11+
+ * Centos 9+
+ * Fedora 41+
*******************
Build prerequisites
@@ -71,8 +77,8 @@ Build prerequisites
pymgclient is a C wrapper around the `mgclient`_ Memgraph client library. To
build it from you will need:
-* Python 3.7 or newer
-* Python 3.7 or newer header files
+* Python 3.7 (3.9 for Mac OS) or newer
+* Python 3.7 (3.9 for Mac OS) or newer header files
* A C compiler supporting C11 standard
* A C++ compiler (it is not used directly, but necessary for CMake to work)
* Preqrequisites of `mgclient`_:
@@ -88,9 +94,9 @@ First install the prerequisites:
* On Debian/Ubuntu::
$ sudo apt install python3-dev cmake make gcc g++ libssl-dev
-* On CentOS::
+* On CentOS/Fedora::
- $ sudo yum install -y python3-devel cmake3 make gcc gcc-c++ openssl-devel
+ $ sudo dnf install -y python3-devel cmake3 make gcc gcc-c++ openssl-devel
After the prerequisites are installed pymgclient can be installed via pip::
diff --git a/mgclient b/mgclient
index 6f59c8a4..9c1dd792 160000
--- a/mgclient
+++ b/mgclient
@@ -1 +1 @@
-Subproject commit 6f59c8a4216d20e42e0943cf506d0ddb6241c29b
+Subproject commit 9c1dd792ffacb90cd92dac2860a4d927318919af
diff --git a/pytest.ini b/pytest.ini
index 3979e7df..34c53b22 100644
--- a/pytest.ini
+++ b/pytest.ini
@@ -1,3 +1,4 @@
[pytest]
markers =
temporal: mark a test that tests temporal types.
+norecursedirs = memgraph
diff --git a/setup.py b/setup.py
index 88688a17..32e74309 100644
--- a/setup.py
+++ b/setup.py
@@ -233,6 +233,7 @@ def build_mgclient_for(self, extension: Extension):
"-DCMAKE_POSITION_INDEPENDENT_CODE=ON",
f'-DCMAKE_C_FLAGS="{self.get_cflags()}"',
f"-DOPENSSL_USE_STATIC_LIBS={'ON' if self.static_openssl else 'OFF'}",
+ "-DPKG_CONFIG_USE_STATIC_LIBS=ON"
]
finalize_cmake_config_command = getattr(self, "finalize_cmake_config_command_" + sys.platform, None)
@@ -269,7 +270,16 @@ def build_mgclient_for(self, extension: Extension):
if finalize is not None:
finalize(extension)
-
+if sys.platform == "win32":
+ extra_link_args = [
+ "-l:libssl.a",
+ "-l:libcrypto.a",
+ "-lcrypt32",
+ "-lws2_32"
+ ]
+else:
+ extra_link_args = None
+
setup(
name="pymgclient",
version=version,
@@ -278,7 +288,7 @@ def build_mgclient_for(self, extension: Extension):
author="Marin Tomic",
author_email="marin.tomic@memgraph.com",
license="Apache2",
- python_requires=">=3.6",
+ python_requires=">=3.7",
description="Memgraph database adapter for Python language",
long_description=long_description,
long_description_content_type="text/markdown",
@@ -287,11 +297,13 @@ def build_mgclient_for(self, extension: Extension):
"Development Status :: 3 - Alpha",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
- "Programming Language :: Python :: 3.6",
"Programming Language :: Python :: 3.7",
"Programming Language :: Python :: 3.8",
"Programming Language :: Python :: 3.9",
"Programming Language :: Python :: 3.10",
+ "Programming Language :: Python :: 3.11",
+ "Programming Language :: Python :: 3.12",
+ "Programming Language :: Python :: 3.13",
"Programming Language :: Python :: Implementation :: CPython",
"Topic :: Database",
"Topic :: Database :: Front-Ends",
@@ -302,11 +314,15 @@ def build_mgclient_for(self, extension: Extension):
"Operating System :: Microsoft :: Windows",
],
ext_modules=[
- Extension(EXTENSION_NAME, sources=sources, depends=headers, extra_compile_args=["-Werror=all", "-std=c99"])
+ Extension(EXTENSION_NAME, sources=sources, depends=headers, extra_link_args=extra_link_args)
],
project_urls={
"Source": "https://github.com/memgraph/pymgclient",
"Documentation": "https://memgraph.github.io/pymgclient",
},
cmdclass={"build_ext": BuildMgclientExt},
+ install_requires=[
+ "pyopenssl",
+ "networkx"
+ ]
)
diff --git a/test/common.py b/test/common.py
index f3c481c5..bb1a0f44 100644
--- a/test/common.py
+++ b/test/common.py
@@ -88,10 +88,11 @@ def start_memgraph(cert_file="", key_file=""):
"--storage-properties-on-edges=true",
"--storage-snapshot-interval-sec=0",
"--storage-wal-enabled=false",
- "--storage-recover-on-startup=false",
"--storage-snapshot-on-exit=false",
"--telemetry-enabled=false",
"--log-file",
+ "--timezone",
+ "UTC"
"",
]
memgraph_process = subprocess.Popen(cmd)
diff --git a/tools/get_memgraph_version.sh b/tools/get_memgraph_version.sh
new file mode 100755
index 00000000..a42a1455
--- /dev/null
+++ b/tools/get_memgraph_version.sh
@@ -0,0 +1,8 @@
+#!/bin/bash
+mgversion=$(
+ curl -s https://api.github.com/repos/memgraph/memgraph/releases/latest \
+ | grep -m1 '"tag_name":' \
+ | sed -E 's/.*"([^"]+)".*/\1/' \
+ | sed 's/^v//'
+)
+echo "$mgversion"
\ No newline at end of file
diff --git a/tools/install_linux_deps.sh b/tools/install_linux_deps.sh
new file mode 100755
index 00000000..dbc0f7cd
--- /dev/null
+++ b/tools/install_linux_deps.sh
@@ -0,0 +1,196 @@
+#!/bin/bash
+
+# This script is for installing the dependencies required for building,
+# installing and testing pymgclient
+
+#!/usr/bin/env bash
+set -euo pipefail
+
+# defaults
+python_version=""
+distro=""
+force_update=false
+
+usage() {
+ cat <] [--python-version X.Y]
+ Optional distro tag, e.g. ubuntu-24.04, fedora-42
+ --python-version X.Y Optional Python MAJOR.MINOR (e.g. 3.12).
+ Defaults to system python3's MAJOR.MINOR.
+ --force-update Force python packages to be updated
+EOF
+ exit 1
+}
+
+
+# parse flags + optional positional distro
+while [[ $# -gt 0 ]]; do
+ case "$1" in
+ --python-version)
+ if [[ -z "${2-}" ]]; then
+ echo "Error: --python-version needs an argument" >&2
+ usage
+ fi
+ python_version="$2"
+ shift 2
+ ;;
+ --force-update)
+ force_update=true
+ shift 1
+ ;;
+ -h|--help)
+ usage
+ ;;
+ --*) # any other flag
+ echo "Unknown option: $1" >&2
+ usage
+ ;;
+ *) # first non-flag is our distro
+ if [[ -z "$distro" ]]; then
+ distro="$1"
+ shift
+ else
+ echo "Unexpected argument: $1" >&2
+ usage
+ fi
+ ;;
+ esac
+done
+
+
+# detect python_version if not given
+if [[ -z "$python_version" ]]; then
+ python_version="$(python3 --version | grep -Eo '[0-9]+\.[0-9]+' )"
+fi
+python_binary="python${python_version}"
+
+# Detect distro from /etc/os-release
+detect_distro() {
+ if [[ -r /etc/os-release ]]; then
+ . /etc/os-release
+ # Normalize common IDs
+ case "$ID" in
+ ubuntu|debian|linuxmint|fedora|centos|rhel|rocky)
+ # version might be "24.04" or "9" or "42"
+ # some versions include quotes
+ ver="${VERSION_ID//\"/}"
+ echo "${ID} ${ver}"
+ ;;
+ *)
+ echo "unknown-$(uname -s | tr '[:upper:]' '[:lower:]') $(uname -r)" ;;
+ esac
+ else
+ echo "unknown-$(uname -s | tr '[:upper:]' '[:lower:]') $(uname -r)"
+ fi
+}
+
+# Ensure at least one arg
+if [[ $# -gt 1 ]]; then
+ usage
+fi
+
+# If distro not provided, detect it
+if [[ -z "$distro" ]]; then
+ read distro version < <(detect_distro)
+ if [[ "$distro" == unknown* ]]; then
+ echo "Unknown distro detected"
+ exit 1
+ fi
+else
+ # split version from distro, e.g. `ubuntu-24.04` -> `ubuntu` `24.04`
+ version="${distro#*-}"
+ distro="${distro%%-*}"
+fi
+echo "Linux Distro: $distro $version"
+
+# detect if we need sudo or not
+SUDO=()
+if (( EUID != 0 )); then
+ if ! command -v sudo &>/dev/null; then
+ echo "Error: root privileges or sudo required." >&2
+ exit 1
+ fi
+ SUDO=(sudo)
+fi
+
+DEB_DEPS=(
+ python${python_version}
+ python${python_version}-dev
+ python3-pip
+ python3-setuptools
+ python3-wheel
+ libpython${python_version}
+ cmake
+ g++
+ libssl-dev
+ netcat-traditional
+ patchelf
+)
+
+RPM_DEPS=(
+ python${python_version}
+ python${python_version}-devel
+ python3-pip
+ python3-setuptools
+ python3-wheel
+ cmake
+ g++
+ openssl-devel
+ nmap-ncat
+)
+
+install_deb() {
+ echo "Installing DEB dependencies..."
+ installed_python_version="$(( python3 --version 2>&1 || echo ) | grep -Po '(?<=Python )\d+\.\d+' || true)"
+ if [[ "$python_version" != "$installed_python_version" ]]; then
+ echo "Installed Python version ${installed_python_version} does not match requested version ${python_version}"
+ if [[ "$distro" == "debian" ]]; then
+ exit 1
+ else
+ echo "Adding deadsnakes PPA"
+ "${SUDO[@]}" apt-get update
+ "${SUDO[@]}" apt-get install -y software-properties-common
+ "${SUDO[@]}" add-apt-repository -y ppa:deadsnakes/ppa
+ fi
+ fi
+ if [[ ("$distro" == "ubuntu" && ${version#*.} -ge 24) \
+ || ("$distro" == "linuxmint" && ${version#*.} -ge 22) ]]; then
+ DEB_DEPS+=( libcurl4t64 )
+ else
+ DEB_DEPS+=( libcurl4 )
+ fi
+ "${SUDO[@]}" apt-get update
+ "${SUDO[@]}" apt-get install -y ${DEB_DEPS[*]}
+}
+
+install_rpm() {
+ echo "Installing RPM dependencies..."
+ "${SUDO[@]}" dnf install -y ${RPM_DEPS[*]}
+}
+
+case "$distro" in
+ debian|ubuntu|linuxmint)
+ install_deb
+ ;;
+ centos|fedora|rocky|rhel)
+ install_rpm
+ ;;
+ *)
+ echo "Unsupported Distro: $distro" >&2
+ exit 1
+ ;;
+esac
+
+# install python dependencies
+export PIP_BREAK_SYSTEM_PACKAGES=1
+pkgs=(networkx pytest pyopenssl sphinx setuptools wheel auditwheel)
+if [[ $force_update == true ]]; then
+ "$python_binary" -m pip install --upgrade --ignore-installed ${pkgs[@]}
+else
+ for pkg in "${pkgs[@]}"; do
+ echo "Installing/upgrading $pkg..."
+ if ! "$python_binary" -m pip install --upgrade "$pkg"; then
+ echo "Warning: pip failed on $pkg, continuing…" >&2
+ fi
+ done
+fi
\ No newline at end of file
diff --git a/tools/wait_for_memgraph.sh b/tools/wait_for_memgraph.sh
new file mode 100755
index 00000000..b7e35ebc
--- /dev/null
+++ b/tools/wait_for_memgraph.sh
@@ -0,0 +1,107 @@
+#!/usr/bin/env bash
+set -euo pipefail
+
+TIMEOUT=10
+MEMGRAPH_CONSOLE_BINARY=${MEMGRAPH_CONSOLE_BINARY:-}
+
+usage() {
+ cat < [port]
+ --timeout SECONDS Max seconds to wait (default: $TIMEOUT)
+ Memgraph host to check
+ [port] Memgraph port (default: 7687)
+EOF
+ exit 1
+}
+
+# --- parse flags ---
+while [[ $# -gt 0 ]]; do
+ case $1 in
+ --timeout)
+ [[ -n "${2-}" ]] || { echo "Error: --timeout needs an argument" >&2; usage; }
+ TIMEOUT=$2
+ shift 2
+ ;;
+ -h|--help)
+ usage
+ ;;
+ *)
+ break
+ ;;
+ esac
+done
+
+# --- positional args ---
+(( $# >= 1 && $# <= 2 )) || usage
+HOST=$1
+PORT=${2-7687}
+
+# --- locate mgconsole ---
+if [[ -z "$MEMGRAPH_CONSOLE_BINARY" ]]; then
+ if command -v mgconsole &>/dev/null; then
+ MEMGRAPH_CONSOLE_BINARY=$(command -v mgconsole)
+ HAVE_MGCONSOLE=1
+ else
+ HAVE_MGCONSOLE=0
+ fi
+else
+ if [[ ! -x "$MEMGRAPH_CONSOLE_BINARY" ]]; then
+ echo "Error: \$MEMGRAPH_CONSOLE_BINARY set to '$MEMGRAPH_CONSOLE_BINARY', but not executable." >&2
+ exit 1
+ fi
+ HAVE_MGCONSOLE=1
+fi
+
+# --- wait for a port on memgraph host with timeout ---
+wait_port() {
+ local host=$1 port=$2 timeout=$3 start now
+ start=$(date +%s)
+ while true; do
+ if timeout 1 bash -c "(exec 3<>'/dev/tcp/${host}/${port}')" 2>/dev/null; then
+ return 0
+ fi
+ now=$(date +%s)
+ (( now - start >= timeout )) && {
+ echo "Timeout ($timeout s) waiting for $host:$port" >&2
+ return 1
+ }
+ done
+}
+
+# --- wait for memgraph console to respond with timeout ---
+wait_for_memgraph() {
+ local host=$1 port=$2 timeout=$3 start now
+ start=$(date +%s)
+ while true; do
+ if timeout 1 bash -c "echo 'RETURN 1;' | \"$MEMGRAPH_CONSOLE_BINARY\" --host \"$host\" --port \"$port\" >/dev/null 2>&1"; then
+ return 0
+ fi
+ now=$(date +%s)
+ (( now - start >= timeout )) && {
+ echo "Timeout ($timeout s) waiting for memgraph at $host:$port" >&2
+ return 1
+ }
+ sleep 0.1
+ done
+}
+
+# --- run checks ---
+echo "Waiting for TCP port $HOST:$PORT (timeout ${TIMEOUT}s)..."
+if wait_port "$HOST" "$PORT" "$TIMEOUT"; then
+ timed_out=0
+else
+ timed_out=1
+fi
+
+if (( HAVE_MGCONSOLE )); then
+ echo "Waiting for memgraph console on $HOST:$PORT (timeout ${TIMEOUT}s)..."
+ wait_for_memgraph "$HOST" "$PORT" "$TIMEOUT"
+else
+ if [[ $timed_out == 1 ]]; then
+ echo "mgconsole not found"
+ exit 1
+ fi
+ echo "Note: mgconsole not found; skipping memgraph-console check."
+fi
+
+echo "Memgraph Started."