Skip to content

Commit 1d37181

Browse files
committed
Refactor Docker build process for Python bindings and enhance version extraction
1 parent 9696a16 commit 1d37181

4 files changed

Lines changed: 131 additions & 149 deletions

File tree

bindings/python/Dockerfile.build

Lines changed: 37 additions & 50 deletions
Original file line numberDiff line numberDiff line change
@@ -1,53 +1,32 @@
11
# Multi-stage Docker build for ArcadeDB Python bindings
22
# Supports building all three distributions: headless, minimal, full
3+
#
4+
# REQUIRED BUILD ARGS (passed from build-all.sh):
5+
# DISTRIBUTION - one of: headless, minimal, full
6+
# ARCADEDB_TAG - version tag from pom.xml (e.g., 25.10.1-SNAPSHOT)
37

4-
ARG DISTRIBUTION=headless
5-
6-
# Stage 1: Build Java components
7-
FROM maven:3.9-eclipse-temurin-21 AS java-builder
8-
9-
WORKDIR /arcadedb
10-
11-
# Copy git for buildnumber-maven-plugin (needs git revision info)
12-
COPY .git .git/
13-
14-
# Copy all necessary files for Java build (excluding Python docs/build artifacts)
15-
COPY pom.xml .
16-
COPY README.md .
17-
COPY LICENSE .
18-
COPY console console/
19-
COPY coverage coverage/
20-
COPY e2e e2e/
21-
COPY e2e-perf e2e-perf/
22-
COPY engine engine/
23-
COPY graphql graphql/
24-
COPY gremlin gremlin/
25-
COPY grpc grpc/
26-
COPY grpc-client grpc-client/
27-
COPY grpcw grpcw/
28-
COPY integration integration/
29-
COPY metrics metrics/
30-
COPY mongodbw mongodbw/
31-
COPY network network/
32-
COPY package package/
33-
COPY postgresw postgresw/
34-
COPY redisw redisw/
35-
COPY server server/
36-
COPY studio studio/
37-
COPY bindings/python/src bindings/python/src/
38-
COPY bindings/python/tests bindings/python/tests/
39-
COPY bindings/python/*.py bindings/python/
40-
COPY bindings/python/pyproject.toml bindings/python/
41-
42-
# Build all Java modules (skip tests for faster build)
43-
RUN echo "🔨 Building Java components..." && \
44-
mvn clean package -DskipTests -B -q && \
45-
echo "✅ Java build complete!"
8+
ARG DISTRIBUTION
9+
ARG ARCADEDB_TAG
10+
11+
# Stage 1: Use prebuilt ArcadeDB image to obtain compiled JARs
12+
# ArcadeDB publishes three separate images:
13+
# - arcadedata/arcadedb-headless (minimal JARs, no Studio)
14+
# - arcadedata/arcadedb-minimal (includes Studio, excludes Gremlin/GraphQL/MongoDB/Redis)
15+
# - arcadedata/arcadedb (full distribution with all features)
16+
FROM arcadedata/arcadedb:${ARCADEDB_TAG} AS java-builder-full
17+
FROM arcadedata/arcadedb-minimal:${ARCADEDB_TAG} AS java-builder-minimal
18+
FROM arcadedata/arcadedb-headless:${ARCADEDB_TAG} AS java-builder-headless
19+
20+
# Select the right builder based on DISTRIBUTION arg (passed from build-all.sh)
21+
FROM java-builder-${DISTRIBUTION} AS java-builder
22+
23+
# nothing to do here; jars will be copied from /home/arcadedb/lib in the python-builder stage
4624

4725
# Stage 2: Build Python wheel
26+
4827
FROM python:3.11-slim AS python-builder
4928

50-
# Install system dependencies
29+
# Install system dependencies (JDK needed for JPype at build/test time)
5130
RUN apt-get update && apt-get install -y \
5231
openjdk-21-jdk \
5332
&& rm -rf /var/lib/apt/lists/*
@@ -58,8 +37,10 @@ ENV PATH=$PATH:$JAVA_HOME/bin
5837

5938
WORKDIR /build
6039

61-
# Copy Java build artifacts
62-
COPY --from=java-builder /arcadedb /arcadedb
40+
# Copy compiled JARs from the prebuilt ArcadeDB image
41+
# The official image places JARs under /home/arcadedb/lib
42+
RUN mkdir -p /build/jars
43+
COPY --from=java-builder /home/arcadedb/lib /build/jars/
6344

6445
# Copy Python binding source
6546
COPY bindings/python/src ./src
@@ -69,23 +50,29 @@ COPY bindings/python/extract_version.py .
6950
COPY bindings/python/write_version.py .
7051
COPY bindings/python/pyproject.toml ./
7152

53+
# Also copy repository pom.xml so extract_version.py can read it
54+
COPY ../../pom.xml /arcadedb/pom.xml
55+
7256
# Install Python build dependencies
7357
RUN pip install --no-cache-dir build wheel setuptools jpype1
7458

75-
# Build argument for distribution type
76-
ARG DISTRIBUTION=headless
59+
# Re-declare build args for this stage (required after FROM)
60+
ARG DISTRIBUTION
61+
ARG ARCADEDB_TAG
7762
ENV ARCADEDB_DISTRIBUTION=${DISTRIBUTION}
7863

7964
# Extract version and copy JARs
65+
# Version is passed from build-all.sh via ARCADEDB_TAG build arg
8066
RUN echo "📌 Building distribution: ${DISTRIBUTION}" && \
81-
export ARCADEDB_VERSION=$(python3 extract_version.py) && \
82-
echo "📌 Version: ${ARCADEDB_VERSION}" && \
67+
echo "📌 Docker tag used: ${ARCADEDB_TAG}" && \
8368
python3 write_version.py /arcadedb/pom.xml && \
8469
python3 setup_jars.py && \
8570
echo "📦 JAR files copied"
8671

8772
# Build the wheel
88-
RUN export ARCADEDB_VERSION=$(python3 extract_version.py /arcadedb/pom.xml) && \
73+
# Extract PEP 440 version from pom.xml
74+
RUN export ARCADEDB_VERSION=$(python3 extract_version.py --format=pep440 /arcadedb/pom.xml) && \
75+
echo "📦 Python package version: ${ARCADEDB_VERSION}" && \
8976
case ${DISTRIBUTION} in \
9077
headless) \
9178
PACKAGE_NAME="arcadedb-embedded-headless" && \

bindings/python/build-all.sh

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,6 +55,13 @@ if [[ ! "$DISTRIBUTION" =~ ^(all|headless|minimal|full)$ ]]; then
5555
exit 1
5656
fi
5757

58+
# Auto-detect Docker tag from pom.xml
59+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
60+
echo -e "${CYAN}🔍 Detecting version from pom.xml...${NC}"
61+
DOCKER_TAG=$(python3 "$SCRIPT_DIR/extract_version.py" --format=docker)
62+
echo -e "${CYAN}📌 Docker tag: ${YELLOW}${DOCKER_TAG}${NC}"
63+
echo ""
64+
5865
# Check if Docker is available
5966
if ! command -v docker &> /dev/null; then
6067
echo -e "${RED}❌ Docker is not installed or not in PATH${NC}"
@@ -95,6 +102,7 @@ build_distribution() {
95102
echo -e "${CYAN}📦 Building Docker image for $dist distribution...${NC}"
96103
docker build \
97104
--build-arg DISTRIBUTION=$dist \
105+
--build-arg ARCADEDB_TAG=$DOCKER_TAG \
98106
--target export \
99107
-t arcadedb-python-bindings-$dist-export \
100108
-f Dockerfile.build \
@@ -104,6 +112,7 @@ build_distribution() {
104112
echo -e "${CYAN}🧪 Running tests in Docker...${NC}"
105113
docker build \
106114
--build-arg DISTRIBUTION=$dist \
115+
--build-arg ARCADEDB_TAG=$DOCKER_TAG \
107116
--target tester \
108117
-t arcadedb-python-bindings-$dist \
109118
-f Dockerfile.build \

bindings/python/extract_version.py

Lines changed: 67 additions & 33 deletions
Original file line numberDiff line numberDiff line change
@@ -1,61 +1,95 @@
11
#!/usr/bin/env python3
2-
"""Extract ArcadeDB version from parent pom.xml"""
2+
"""Extract ArcadeDB version from parent pom.xml
3+
4+
Usage:
5+
python extract_version.py [pom_path] [--format=pep440|docker]
6+
7+
--format=pep440 : Convert to PEP 440 for Python packaging (default)
8+
25.10.1-SNAPSHOT -> 25.10.1.dev0
9+
--format=docker : Raw Maven version for Docker tags
10+
25.10.1-SNAPSHOT -> 25.10.1-SNAPSHOT
11+
"""
312
import re
413
import sys
514
from pathlib import Path
615

716
# Python-specific patch version (increment for Python-only releases)
817
PYTHON_PATCH = 0 # Change this to 2, 3, etc. for subsequent Python patches
918

10-
def extract_version_from_pom(pom_path):
11-
"""Extract version from Maven pom.xml and convert to PEP 440 format"""
12-
with open(pom_path, 'r') as f:
19+
20+
def extract_raw_version_from_pom(pom_file):
21+
"""Extract the raw version string from pom.xml (no conversion)"""
22+
with open(pom_file, 'r', encoding='utf-8') as f:
1323
content = f.read()
1424

1525
# Find the first <version> tag after <artifactId>arcadedb-parent</artifactId>
1626
pattern = r'<artifactId>arcadedb-parent</artifactId>.*?<version>(.*?)</version>'
1727
match = re.search(pattern, content, re.DOTALL)
1828

1929
if match:
20-
version = match.group(1).strip()
21-
# Convert Maven version to PEP 440 compatible version
22-
# Examples:
23-
# 25.10.1-SNAPSHOT -> 25.10.1.dev0
24-
# 25.10.1-RC1 -> 25.10.1rc1
25-
# 25.10.1 -> 25.10.1
26-
27-
if '-SNAPSHOT' in version:
28-
version = version.replace('-SNAPSHOT', '.dev0')
29-
elif '-RC' in version:
30-
version = version.replace('-RC', 'rc')
31-
elif '-' in version:
32-
# Generic replacement for other pre-release identifiers
33-
version = version.replace('-', '')
34-
35-
# Add Python patch version for Python-specific releases
36-
# PYTHON_PATCH = 0 → 25.9.1 (matches Java version)
37-
# PYTHON_PATCH = 1 → 25.9.1.1 (first Python-only patch)
38-
# PYTHON_PATCH = 2 → 25.9.1.2 (second Python-only patch)
39-
# Not applied to dev versions (e.g., 25.9.1.dev0)
40-
if PYTHON_PATCH > 0 and '.dev' not in version and 'rc' not in version.lower():
41-
version = f"{version}.{PYTHON_PATCH}"
42-
43-
return version
30+
return match.group(1).strip()
4431

4532
raise ValueError("Could not find version in pom.xml")
4633

4734

35+
def extract_version_from_pom(pom_file, fmt='pep440'):
36+
"""Extract version from Maven pom.xml
37+
38+
Args:
39+
pom_file: Path to pom.xml file
40+
fmt: 'pep440' for Python packaging, 'docker' for Docker tags
41+
"""
42+
ver = extract_raw_version_from_pom(pom_file)
43+
44+
if fmt == 'docker':
45+
# Return raw Maven version for Docker tags
46+
return ver
47+
48+
# Convert Maven version to PEP 440 compatible version
49+
# Examples:
50+
# 25.10.1-SNAPSHOT -> 25.10.1.dev0
51+
# 25.10.1-RC1 -> 25.10.1rc1
52+
# 25.10.1 -> 25.10.1
53+
54+
if '-SNAPSHOT' in ver:
55+
ver = ver.replace('-SNAPSHOT', '.dev0')
56+
elif '-RC' in ver:
57+
ver = ver.replace('-RC', 'rc')
58+
elif '-' in ver:
59+
# Generic replacement for other pre-release identifiers
60+
ver = ver.replace('-', '')
61+
62+
# Add Python patch version for Python-specific releases
63+
# PYTHON_PATCH = 0 → 25.9.1 (matches Java version)
64+
# PYTHON_PATCH = 1 → 25.9.1.1 (first Python-only patch)
65+
# PYTHON_PATCH = 2 → 25.9.1.2 (second Python-only patch)
66+
# Not applied to dev versions (e.g., 25.9.1.dev0)
67+
if PYTHON_PATCH > 0 and '.dev' not in ver and 'rc' not in ver.lower():
68+
ver = f"{ver}.{PYTHON_PATCH}"
69+
70+
return ver
71+
72+
4873
if __name__ == "__main__":
4974
# Default to parent pom.xml (two levels up from bindings/python)
5075
script_dir = Path(__file__).parent
5176
pom_path = script_dir / "../../pom.xml"
77+
output_format = 'pep440'
5278

53-
if len(sys.argv) > 1:
54-
pom_path = Path(sys.argv[1])
79+
# Parse arguments
80+
for arg in sys.argv[1:]:
81+
if arg.startswith('--format='):
82+
output_format = arg.split('=', 1)[1]
83+
if output_format not in ['pep440', 'docker']:
84+
print("Error: Invalid format. Use 'pep440' or 'docker'",
85+
file=sys.stderr)
86+
sys.exit(1)
87+
elif not arg.startswith('--'):
88+
pom_path = Path(arg)
5589

5690
try:
57-
version = extract_version_from_pom(pom_path)
58-
print(version)
59-
except Exception as e:
91+
ver = extract_version_from_pom(pom_path, output_format)
92+
print(ver)
93+
except (ValueError, FileNotFoundError, OSError) as e:
6094
print(f"Error: {e}", file=sys.stderr)
6195
sys.exit(1)

0 commit comments

Comments
 (0)