diff --git a/.github/workflows/build_libtestoptimization.yml b/.github/workflows/build_libtestoptimization.yml index 86132a7..d36c89f 100644 --- a/.github/workflows/build_libtestoptimization.yml +++ b/.github/workflows/build_libtestoptimization.yml @@ -11,7 +11,7 @@ on: jobs: mac_job: name: macOS - runs-on: macos-14 + runs-on: macos-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-go@v5 @@ -75,7 +75,7 @@ jobs: linux_arm64_job: name: linux-arm64 - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Set up QEMU @@ -99,8 +99,11 @@ jobs: cp -rf ./native/* external/internal/civisibility/native/ - name: Build and run linux-arm64 run: | - docker buildx build --platform linux/arm64 --build-arg GOARCH=arm64 --build-arg FILE_NAME=linux-arm64-libtestoptimization -t libtestoptimization-builder:arm64 -f ./Dockerfile ../../.. --load - docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder:arm64 + docker buildx build --platform linux/arm64 --build-arg GOARCH=arm64 --build-arg FILE_NAME=linux-arm64-libtestoptimization -t libtestoptimization-builder-static:arm64 -f ./Dockerfile-static ../../.. --load + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-static:arm64 + + docker buildx build --platform linux/arm64 --build-arg GOARCH=arm64 --build-arg FILE_NAME=linux-arm64-libtestoptimization -t libtestoptimization-builder-dynamic:arm64 -f ./Dockerfile-dynamic ../../.. --load + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-dynamic:arm64 working-directory: external/internal/civisibility/native - name: Upload artifact uses: actions/upload-artifact@v4 @@ -108,9 +111,47 @@ jobs: name: linux-arm64-artifact path: external/internal/civisibility/native/output/*.zip* + linux_arm64_musl_job: + name: linux-arm64-musl + runs-on: ubuntu-latest + steps: + - 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: Checkout external repository + uses: actions/checkout@v4 + with: + repository: "DataDog/dd-trace-go" + ref: "main" + token: ${{ secrets.GITHUB_TOKEN }} + path: external + - name: Copy build files + run: | + # Create the directory if it doesn't exist + mkdir -p external/internal/civisibility/native + # Copy the build files + cp -rf ./build/* external/internal/civisibility/native/ + # Copy the native files + cp -rf ./native/* external/internal/civisibility/native/ + - name: Build and run linux-arm64-musl + run: | + docker buildx build --platform linux/arm64 --build-arg GOARCH=arm64 --build-arg FILE_NAME=linux-arm64-libtestoptimization -t libtestoptimization-builder-static:arm64 -f ./Dockerfile-static-alpine ../../.. --load + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-static:arm64 + + docker buildx build --platform linux/arm64 --build-arg GOARCH=arm64 --build-arg FILE_NAME=linux-arm64-libtestoptimization -t libtestoptimization-builder-dynamic:arm64 -f ./Dockerfile-dynamic-alpine ../../.. --load + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-dynamic:arm64 + working-directory: external/internal/civisibility/native + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: linux-arm64-musl-artifact + path: external/internal/civisibility/native/output/*.zip* + linux_amd64_job: name: linux-amd64 - runs-on: ubuntu-24.04 + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - name: Checkout external repository @@ -130,8 +171,11 @@ jobs: cp -rf ./native/* external/internal/civisibility/native/ - name: Build and run linux-amd64 run: | - docker build --platform linux/amd64 --build-arg GOARCH=amd64 --build-arg FILE_NAME=linux-x64-libtestoptimization -t libtestoptimization-builder:amd64 -f ./Dockerfile ../../.. - docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder:amd64 + docker build --platform linux/amd64 --build-arg GOARCH=amd64 --build-arg FILE_NAME=linux-x64-libtestoptimization -t libtestoptimization-builder-static:amd64 -f ./Dockerfile-static ../../.. + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-static:amd64 + + docker build --platform linux/amd64 --build-arg GOARCH=amd64 --build-arg FILE_NAME=linux-x64-libtestoptimization -t libtestoptimization-builder-dynamic:amd64 -f ./Dockerfile-dynamic ../../.. + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-dynamic:amd64 working-directory: external/internal/civisibility/native - name: Build and run android-arm64 run: | @@ -144,6 +188,40 @@ jobs: name: linux-amd64-artifact path: external/internal/civisibility/native/output/*.zip* + linux_amd64_musl_job: + name: linux-amd64-musl + runs-on: ubuntu-latest + steps: + - uses: actions/checkout@v4 + - name: Checkout external repository + uses: actions/checkout@v4 + with: + repository: "DataDog/dd-trace-go" + ref: "main" + token: ${{ secrets.GITHUB_TOKEN }} + path: external + - name: Copy build files + run: | + # Create the directory if it doesn't exist + mkdir -p external/internal/civisibility/native + # Copy the build files + cp -rf ./build/* external/internal/civisibility/native/ + # Copy the native files + cp -rf ./native/* external/internal/civisibility/native/ + - name: Build and run linux-amd64 + run: | + docker build --platform linux/amd64 --build-arg GOARCH=amd64 --build-arg FILE_NAME=linux-x64-libtestoptimization -t libtestoptimization-builder-static:amd64 -f ./Dockerfile-static-alpine ../../.. + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-static:amd64 + + docker build --platform linux/amd64 --build-arg GOARCH=amd64 --build-arg FILE_NAME=linux-x64-libtestoptimization -t libtestoptimization-builder-dynamic:amd64 -f ./Dockerfile-dynamic-alpine ../../.. + docker run --rm -v ./output:/libtestoptimization libtestoptimization-builder-dynamic:amd64 + working-directory: external/internal/civisibility/native + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: linux-amd64-musl-artifact + path: external/internal/civisibility/native/output/*.zip* + windows_job: name: windows runs-on: windows-latest @@ -222,7 +300,7 @@ jobs: collect_artifacts: name: Collect all artifacts - needs: [mac_job, linux_arm64_job, linux_amd64_job, windows_job] + needs: [mac_job, linux_arm64_job, linux_amd64_job, windows_job, linux_arm64_musl_job, linux_amd64_musl_job] runs-on: ubuntu-latest permissions: contents: write @@ -244,6 +322,16 @@ jobs: with: name: linux-amd64-artifact path: artifacts + - name: Download artifacts from linux-arm64-musl job + uses: actions/download-artifact@v4 + with: + name: linux-arm64-musl-artifact + path: artifacts + - name: Download artifacts from linux-amd64-musl job + uses: actions/download-artifact@v4 + with: + name: linux-amd64-musl-artifact + path: artifacts - name: Download artifacts from windows job uses: actions/download-artifact@v4 with: diff --git a/.github/workflows/python-sdk-tests.yml b/.github/workflows/python-sdk-tests.yml index 9c69c6d..40105e2 100644 --- a/.github/workflows/python-sdk-tests.yml +++ b/.github/workflows/python-sdk-tests.yml @@ -195,6 +195,94 @@ jobs: script: | await github.rest.checks.update({ owner: context.repo.owner, repo: context.repo.repo, check_run_id: parseInt("${{ steps.create_check.outputs.check_run_id }}"), status: "completed", conclusion: "${{ job.status }}" === "success" ? "success" : "failure" }); + linux-amd64-musl-test-docker: + name: Run Python SDK Tests on Linux AMD64 Alpine with Docker + needs: download-artifacts + runs-on: ubuntu-latest + defaults: + run: + working-directory: sdks/python/test-optimization-sdk + env: + TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH: ${{ github.workspace }}/build_artifacts + JOB_DISPLAY_NAME: Run Python SDK Tests on Linux AMD64 Alpine with Docker + + steps: + - name: Create Check + id: create_check + uses: actions/github-script@v7 + with: + script: | + const checkRun = await github.rest.checks.create({ owner: context.repo.owner, repo: context.repo.repo, name: process.env.JOB_DISPLAY_NAME, head_sha: context.sha, status: "in_progress" }); + core.setOutput("check_run_id", checkRun.data.id); + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download test artifacts + uses: actions/download-artifact@v4 + with: + name: test-artifacts + path: ${{ github.workspace }}/build_artifacts + + - name: Build and run tests + run: | + docker build -t python-test-optimization-sdk-test -f ./Dockerfile-alpine . + docker run -v ${{ github.workspace }}/build_artifacts:/build_artifacts -e TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH=/build_artifacts python-test-optimization-sdk-test + + - name: Update Check + if: always() + uses: actions/github-script@v7 + with: + script: | + await github.rest.checks.update({ owner: context.repo.owner, repo: context.repo.repo, check_run_id: parseInt("${{ steps.create_check.outputs.check_run_id }}"), status: "completed", conclusion: "${{ job.status }}" === "success" ? "success" : "failure" }); + + linux-arm64-musl-test: + name: Run Python SDK Tests on Linux ARM64 Alpine + needs: download-artifacts + runs-on: ubuntu-latest + defaults: + run: + working-directory: sdks/python/test-optimization-sdk + env: + TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH: ${{ github.workspace }}/build_artifacts + JOB_DISPLAY_NAME: Run Python SDK Tests on Linux ARM64 Alpine + + steps: + - name: Create Check + id: create_check + uses: actions/github-script@v7 + with: + script: | + const checkRun = await github.rest.checks.create({ owner: context.repo.owner, repo: context.repo.repo, name: process.env.JOB_DISPLAY_NAME, head_sha: context.sha, status: "in_progress" }); + core.setOutput("check_run_id", checkRun.data.id); + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download test artifacts + uses: actions/download-artifact@v4 + with: + name: test-artifacts + path: ${{ github.workspace }}/build_artifacts + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and run tests + run: | + docker buildx build --platform linux/arm64 -t python-test-optimization-sdk-test -f ./Dockerfile-alpine . --load + docker run -v ${{ github.workspace }}/build_artifacts:/build_artifacts -e TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH=/build_artifacts python-test-optimization-sdk-test + + - name: Update Check + if: always() + uses: actions/github-script@v7 + with: + script: | + await github.rest.checks.update({ owner: context.repo.owner, repo: context.repo.repo, check_run_id: parseInt("${{ steps.create_check.outputs.check_run_id }}"), status: "completed", conclusion: "${{ job.status }}" === "success" ? "success" : "failure" }); + macos-test: name: Run Python SDK Tests on macOS needs: download-artifacts diff --git a/.github/workflows/rust-sdk-tests.yml b/.github/workflows/rust-sdk-tests.yml index cbc6e8f..67ca796 100644 --- a/.github/workflows/rust-sdk-tests.yml +++ b/.github/workflows/rust-sdk-tests.yml @@ -189,6 +189,93 @@ jobs: script: | await github.rest.checks.update({ owner: context.repo.owner, repo: context.repo.repo, check_run_id: parseInt("${{ steps.create_check.outputs.check_run_id }}"), status: "completed", conclusion: "${{ job.status }}" === "success" ? "success" : "failure" }); + linux-amd64-musl-test-docker: + name: Run Rust SDK Tests on Linux AMD64 Alpine with Docker + needs: download-artifacts + runs-on: ubuntu-latest + defaults: + run: + working-directory: sdks/rust/test-optimization-sdk + env: + TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH: ${{ github.workspace }}/build_artifacts + JOB_DISPLAY_NAME: Run Rust SDK Tests on Linux AMD64 Alpine with Docker + + steps: + - name: Create Check + id: create_check + uses: actions/github-script@v7 + with: + script: | + const checkRun = await github.rest.checks.create({ owner: context.repo.owner, repo: context.repo.repo, name: process.env.JOB_DISPLAY_NAME, head_sha: context.sha, status: "in_progress" }); + core.setOutput("check_run_id", checkRun.data.id); + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download test artifacts + uses: actions/download-artifact@v4 + with: + name: test-artifacts + path: ${{ github.workspace }}/build_artifacts + + - name: Build and run tests + run: | + docker build -t test-optimization-sdk-test -f ./Dockerfile-alpine . + docker run -v ${{ github.workspace }}/build_artifacts:/build_artifacts -e TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH=/build_artifacts test-optimization-sdk-test + + - name: Update Check + if: always() + uses: actions/github-script@v7 + with: + script: | + await github.rest.checks.update({ owner: context.repo.owner, repo: context.repo.repo, check_run_id: parseInt("${{ steps.create_check.outputs.check_run_id }}"), status: "completed", conclusion: "${{ job.status }}" === "success" ? "success" : "failure" }); + + linux-arm64-musl-test: + name: Run Rust SDK Tests on Linux ARM64 Alpine + needs: download-artifacts + runs-on: ubuntu-latest + defaults: + run: + working-directory: sdks/rust/test-optimization-sdk + env: + TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH: ${{ github.workspace }}/build_artifacts + JOB_DISPLAY_NAME: Run Rust SDK Tests on Linux ARM64 Alpine + steps: + - name: Create Check + id: create_check + uses: actions/github-script@v7 + with: + script: | + const checkRun = await github.rest.checks.create({ owner: context.repo.owner, repo: context.repo.repo, name: process.env.JOB_DISPLAY_NAME, head_sha: context.sha, status: "in_progress" }); + core.setOutput("check_run_id", checkRun.data.id); + + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Download test artifacts + uses: actions/download-artifact@v4 + with: + name: test-artifacts + path: ${{ github.workspace }}/build_artifacts + + - name: Set up QEMU + uses: docker/setup-qemu-action@v3 + + - name: Set up Docker Buildx + uses: docker/setup-buildx-action@v3 + + - name: Build and run tests + run: | + docker buildx build --platform linux/arm64 -t test-optimization-sdk-test -f ./Dockerfile-alpine . --load + docker run -v ${{ github.workspace }}/build_artifacts:/build_artifacts -e TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH=/build_artifacts test-optimization-sdk-test + + - name: Update Check + if: always() + uses: actions/github-script@v7 + with: + script: | + await github.rest.checks.update({ owner: context.repo.owner, repo: context.repo.repo, check_run_id: parseInt("${{ steps.create_check.outputs.check_run_id }}"), status: "completed", conclusion: "${{ job.status }}" === "success" ? "success" : "failure" }); + macos-test: name: Run Rust SDK Tests on macOS needs: download-artifacts diff --git a/build/Dockerfile-dynamic b/build/Dockerfile-dynamic new file mode 100644 index 0000000..f2d4830 --- /dev/null +++ b/build/Dockerfile-dynamic @@ -0,0 +1,55 @@ +# Dockerfile-dynamic + +# Stage 1: Build the dynamic library +FROM golang:1.24-bullseye AS builder + +# Install dependencies +RUN apt-get update && apt-get install -y gcc binutils + +# Argument can be set during build time with --build-arg GOARCH=arm64 +ARG GOARCH=amd64 + +# Configure environment variables for CGO, operating system, architecture, and compiler +ENV CGO_ENABLED=1 \ + GOOS=linux \ + GOARCH=$GOARCH \ + CC=gcc \ + CGO_CFLAGS="-O2 -Os -s -DNDEBUG -fdata-sections -ffunction-sections" \ + CGO_LDFLAGS="-s -Wl,--gc-sections" + +WORKDIR /app + +# Copy all files from current directory to the container +COPY . . + +WORKDIR /app/internal/civisibility/native + +# Build the dynamic library +RUN go build -tags civisibility_native -buildmode=c-shared -ldflags="-s -w" -gcflags="all=-l" -o ./output/dynamic/libtestoptimization.so *.go +RUN strip --strip-unneeded ./output/dynamic/libtestoptimization.so + +# Stage 2: Extract the dynamic library +FROM alpine:latest + +ARG FILE_NAME=libtestoptimization + +# Install zip for compression +RUN apk add --no-cache zip + +# Create the output folder +RUN mkdir -p /output/dynamic + +# Copy the dynamic library and header from the builder stage +COPY --from=builder /app/internal/civisibility/native/output/dynamic/libtestoptimization.so /output/dynamic/libtestoptimization.so +COPY --from=builder /app/internal/civisibility/native/output/dynamic/libtestoptimization.h /output/dynamic/libtestoptimization.h + +# Compress the files into a zip archive +RUN cd /output/dynamic && zip -j -9 ../${FILE_NAME}-dynamic.zip *.* +# Create a SHA256 checksum file for the archive +RUN sha256sum /output/${FILE_NAME}-dynamic.zip > /output/${FILE_NAME}-dynamic.zip.sha256sum + +# Remove temporary folder +RUN rm -r /output/dynamic + +# Command to run when the container starts +ENTRYPOINT ["sh", "-c", "ls /output && cp /output/*.* /libtestoptimization && echo 'Dynamic library copied.'"] diff --git a/build/Dockerfile-dynamic-alpine b/build/Dockerfile-dynamic-alpine new file mode 100644 index 0000000..cc429da --- /dev/null +++ b/build/Dockerfile-dynamic-alpine @@ -0,0 +1,56 @@ +# Dockerfile-dynamic-alpine + +# Stage 1: Build the dynamic library using an Alpine-based Golang image +FROM golang:1.24-alpine AS builder + +# Install dependencies (gcc and binutils) +RUN apk update && apk add --no-cache gcc binutils build-base + +# Argument that can be set during build (e.g., --build-arg GOARCH=arm64) +ARG GOARCH=amd64 + +# Set environment variables for CGO, OS, architecture, and compiler +ENV CGO_ENABLED=1 \ + GOOS=linux \ + GOARCH=$GOARCH \ + CC=gcc + #CGO_CFLAGS="-O2 -Os -s -DNDEBUG -fdata-sections -ffunction-sections" \ + #CGO_LDFLAGS="-s -Wl,--gc-sections" + +WORKDIR /app + +# Copy all files from the current directory into the container +COPY . . + +WORKDIR /app/internal/civisibility/native + +# Build the shared library +# -ldflags="-s -w" -gcflags="all=-l" +RUN go build -tags "civisibility_native netgo osusergo static_build" -buildmode=c-shared -o ./output/dynamic/libtestoptimization.so *.go +#RUN strip --strip-unneeded ./output/dynamic/libtestoptimization.so + +# Stage 2: Extract the dynamic library into a minimal Alpine image +FROM alpine:latest + +ARG FILE_NAME=libtestoptimization + +# Install zip for compression +RUN apk add --no-cache zip + +# Create the output directory +RUN mkdir -p /output/dynamic + +# Copy the shared library and header from the builder stage +COPY --from=builder /app/internal/civisibility/native/output/dynamic/libtestoptimization.so /output/dynamic/libtestoptimization.so +COPY --from=builder /app/internal/civisibility/native/output/dynamic/libtestoptimization.h /output/dynamic/libtestoptimization.h + +# Compress the files into a zip archive +RUN cd /output/dynamic && zip -j -9 ../${FILE_NAME}-dynamic-musl.zip *.* +# Generate a SHA256 checksum file for the zip +RUN sha256sum /output/${FILE_NAME}-dynamic-musl.zip > /output/${FILE_NAME}-dynamic-musl.zip.sha256sum + +# Remove the temporary output directory +RUN rm -r /output/dynamic + +# Default command when the container starts +ENTRYPOINT ["sh", "-c", "ls /output && cp /output/*.* /libtestoptimization && echo 'Dynamic library copied.'"] diff --git a/build/Dockerfile b/build/Dockerfile-static similarity index 60% rename from build/Dockerfile rename to build/Dockerfile-static index aa8e567..82dcdf1 100644 --- a/build/Dockerfile +++ b/build/Dockerfile-static @@ -1,5 +1,7 @@ +# Dockerfile-static + # Stage 1: Build the static library -FROM golang:1.24.1-bookworm AS builder +FROM golang:1.24-bookworm AS builder # Install dependencies RUN apt-get update && apt-get install -y gcc binutils @@ -17,43 +19,37 @@ ENV CGO_ENABLED=1 \ WORKDIR /app -# Copy everything from the current directory to the PWD (Present Working Directory) inside the container +# Copy all files from current directory to the container COPY . . WORKDIR /app/internal/civisibility/native -# Build the library +# Build the static library RUN go build -tags civisibility_native -buildmode=c-archive -ldflags="-s -w" -gcflags="all=-l" -o ./output/static/libtestoptimization.a *.go RUN strip --strip-unneeded ./output/static/libtestoptimization.a -RUN go build -tags civisibility_native -buildmode=c-shared -ldflags="-s -w" -gcflags="all=-l" -o ./output/dynamic/libtestoptimization.so *.go -RUN strip --strip-unneeded ./output/dynamic/libtestoptimization.so -# Stage 2: Extract the library +# Stage 2: Extract the static library FROM alpine:latest -# Build arguments for the final archive names; you can override these during build ARG FILE_NAME=libtestoptimization # Install zip for compression RUN apk add --no-cache zip # Create the output folder -RUN mkdir -p /output +RUN mkdir -p /output/static # Copy the static library and header from the builder stage COPY --from=builder /app/internal/civisibility/native/output/static/libtestoptimization.a /output/static/libtestoptimization.a COPY --from=builder /app/internal/civisibility/native/output/static/libtestoptimization.h /output/static/libtestoptimization.h -COPY --from=builder /app/internal/civisibility/native/output/dynamic/libtestoptimization.so /output/dynamic/libtestoptimization.so -COPY --from=builder /app/internal/civisibility/native/output/dynamic/libtestoptimization.h /output/dynamic/libtestoptimization.h -# Compress both files into zip archives +# Compress the files into a zip archive RUN cd /output/static && zip -j -9 ../${FILE_NAME}-static.zip *.* -RUN cd /output/dynamic && zip -j -9 ../${FILE_NAME}-dynamic.zip *.* # Create a SHA256 checksum file for the archive RUN sha256sum /output/${FILE_NAME}-static.zip > /output/${FILE_NAME}-static.zip.sha256sum -RUN sha256sum /output/${FILE_NAME}-dynamic.zip > /output/${FILE_NAME}-dynamic.zip.sha256sum -RUN rm -r /output/static /output/dynamic +# Remove temporary folder +RUN rm -r /output/static # Command to run when the container starts ENTRYPOINT ["sh", "-c", "ls /output && cp /output/*.* /libtestoptimization && echo 'Static library copied.'"] diff --git a/build/Dockerfile-static-alpine b/build/Dockerfile-static-alpine new file mode 100644 index 0000000..0942084 --- /dev/null +++ b/build/Dockerfile-static-alpine @@ -0,0 +1,55 @@ +# Dockerfile-static-alpine + +# Stage 1: Build the static library using an Alpine-based Golang image +FROM golang:1.24-alpine AS builder + +# Install dependencies (gcc and binutils) +RUN apk update && apk add --no-cache gcc binutils build-base + +# Argument that can be set during build (e.g., --build-arg GOARCH=arm64) +ARG GOARCH=amd64 + +# Set environment variables for CGO, OS, architecture, and compiler +ENV CGO_ENABLED=1 \ + GOOS=linux \ + GOARCH=$GOARCH \ + CC=gcc \ + CGO_CFLAGS="-O2 -Os -s -DNDEBUG -fdata-sections -ffunction-sections" \ + CGO_LDFLAGS="-s -Wl,--gc-sections" + +WORKDIR /app + +# Copy all files from the current directory into the container +COPY . . + +WORKDIR /app/internal/civisibility/native + +# Build the static library +RUN go build -tags civisibility_native -buildmode=c-archive -ldflags="-s -w" -gcflags="all=-l" -o ./output/static/libtestoptimization.a *.go +RUN strip --strip-unneeded ./output/static/libtestoptimization.a + +# Stage 2: Extract the static library into a minimal Alpine image +FROM alpine:latest + +ARG FILE_NAME=libtestoptimization + +# Install zip for compression +RUN apk add --no-cache zip + +# Create the output directory +RUN mkdir -p /output/static + +# Copy the static library and header from the builder stage +COPY --from=builder /app/internal/civisibility/native/output/static/libtestoptimization.a /output/static/libtestoptimization.a +COPY --from=builder /app/internal/civisibility/native/output/static/libtestoptimization.h /output/static/libtestoptimization.h + +# Compress the files into a zip archive +RUN cd /output/static && zip -j -9 ../${FILE_NAME}-static-musl.zip *.* +# Generate a SHA256 checksum file for the zip +RUN sha256sum /output/${FILE_NAME}-static-musl.zip > /output/${FILE_NAME}-static-musl.zip.sha256sum + +# Remove the temporary output directory +RUN rm -r /output/static + +# Default command when the container starts +ENTRYPOINT ["sh", "-c", "ls /output && cp /output/*.* /libtestoptimization && echo 'Static library copied.'"] diff --git a/sdks/python/test-optimization-sdk/Dockerfile b/sdks/python/test-optimization-sdk/Dockerfile index 4c0fc56..8ce32d8 100644 --- a/sdks/python/test-optimization-sdk/Dockerfile +++ b/sdks/python/test-optimization-sdk/Dockerfile @@ -1,5 +1,5 @@ # Use the official Python image as the base -FROM python:3.10-slim-bookworm +FROM python:3.10-slim-bullseye # Set the working directory WORKDIR /usr/src/test-optimization-sdk diff --git a/sdks/python/test-optimization-sdk/Dockerfile-alpine b/sdks/python/test-optimization-sdk/Dockerfile-alpine new file mode 100644 index 0000000..c94a674 --- /dev/null +++ b/sdks/python/test-optimization-sdk/Dockerfile-alpine @@ -0,0 +1,28 @@ +# Dockerfile-python-alpine + +# Use the official Python image based on Alpine +FROM python:3.10-alpine + +# Set the working directory +WORKDIR /usr/src/test-optimization-sdk + +# Install build dependencies: +# - pkgconf: equivalent to pkg-config in Alpine +# - openssl-dev: instead of libssl-dev +RUN apk update && apk add --no-cache pkgconf openssl-dev + +# Copy configuration files first to take advantage of Docker cache +COPY setup.py ./ + +# Copy source code and tests +COPY src ./src +COPY tests ./tests + +# Install the package in development mode and also install pytest +RUN pip install --upgrade pip && \ + pip install -e . && \ + pip install pytest + +# Default command to run the tests +CMD ["pytest", "--capture=no"] +#CMD ["sh", "-c", "ls -l /build_artifacts && echo '--- LDD Output ---' && ldd /build_artifacts/libtestoptimization.so && echo '--- Running Pytest ---' && pytest --capture=no"] diff --git a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py index 41c82cc..87c2160 100644 --- a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py +++ b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/lib.py @@ -309,7 +309,7 @@ def _get_library_path() -> str: # Load the library try: lib_path = _get_library_path() - print(f"Loading test optimization library from {lib_path}") + print(f"Loading test optimization library from {lib_path}", flush=True) lib = ffi.dlopen(lib_path) except OSError as e: raise RuntimeError(f"Failed to load test optimization library from {lib_path}: {e}") diff --git a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/native_lib.py b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/native_lib.py index 501fc0e..17baa0a 100644 --- a/sdks/python/test-optimization-sdk/src/test_optimization_sdk/native_lib.py +++ b/sdks/python/test-optimization-sdk/src/test_optimization_sdk/native_lib.py @@ -3,6 +3,7 @@ import sys import urllib.request import zipfile +import re from pathlib import Path from .constants import TEST_OPTIMIZATION_SDK_SKIP_NATIVE_INSTALL, TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH, TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT @@ -27,6 +28,8 @@ def get_library_filename(): if system == "macos": return f"{system}-libtestoptimization-dynamic.zip" + elif system == "linux" and is_alpine_linux(): + return f"{system}-{arch}-libtestoptimization-dynamic-musl.zip" else: return f"{system}-{arch}-libtestoptimization-dynamic.zip" @@ -59,6 +62,29 @@ def download_native_library(): print(f"Failed to extract native library: {e}", file=sys.stderr) sys.exit(1) +def is_alpine_linux(): + """ + Checks if the operating system is Alpine Linux + by reading /etc/os-release. + """ + if platform.system() != 'Linux': + return False # Not Linux, therefore not Alpine + + os_release_path = '/etc/os-release' + if os.path.exists(os_release_path): + try: + with open(os_release_path, 'r') as f: + for line in f: + # Use a regular expression to search for ID=alpine + # ignoring potential quotes and spaces + if re.match(r'^ID\s*=\s*"?alpine"?$', line, re.IGNORECASE): + return True + except Exception as e: + # Handle potential read errors if necessary + print(f"Error reading {os_release_path}: {e}") + pass # Print the error and allow the function to return False + return False + def setup_native_library(): """Setup the native library during package installation.""" # Check for custom search path diff --git a/sdks/rust/test-optimization-sdk/Dockerfile-alpine b/sdks/rust/test-optimization-sdk/Dockerfile-alpine new file mode 100644 index 0000000..86d28f3 --- /dev/null +++ b/sdks/rust/test-optimization-sdk/Dockerfile-alpine @@ -0,0 +1,22 @@ +# Dockerfile-rust-alpine + +# Use the official Rust image based on Alpine +FROM rust:1.85-alpine + +# Update repositories and install necessary dependencies: +# - pkgconf: equivalent to pkg-config on Alpine +# - openssl-dev: equivalent to libssl-dev on Debian +RUN apk update && apk add --no-cache pkgconf openssl-dev + +# Set the working directory +WORKDIR /usr/src/test-optimization-sdk + +# Copy manifest files first to leverage Docker cache +COPY Cargo.toml Cargo.lock ./ + +# Copy source code and build script +COPY src ./src +COPY build.rs ./ + +# Run tests +CMD ["cargo", "test", "--", "--nocapture"] diff --git a/sdks/rust/test-optimization-sdk/build.rs b/sdks/rust/test-optimization-sdk/build.rs index 0b4bcaa..fb9cddf 100644 --- a/sdks/rust/test-optimization-sdk/build.rs +++ b/sdks/rust/test-optimization-sdk/build.rs @@ -16,15 +16,34 @@ const TEST_OPTIMIZATION_DOWNLOAD_URL_FORMAT: &str = "https://github.com/DataDog/ fn main() { let target = env::var("TARGET").expect("Cargo did not provide TARGET"); let out_dir = env::var("OUT_DIR").expect("Cargo did not provide OUT_DIR"); - let platform = if target.contains("apple-darwin") { "macos" } else if target.contains("windows") { "windows" } else if target.contains("linux") { "linux" } else { panic!("Unsupported platform: {}", target) }; - let arch = if target.contains("aarch64") { "arm64" } else { "x64" }; - let lib_name = if platform == "macos" { - format!("{}-libtestoptimization-static.zip", platform) - } else { - format!("{}-{}-libtestoptimization-static.zip", platform, arch) + let target_os = env::var("CARGO_CFG_TARGET_OS").expect("Cargo did not provide CARGO_CFG_TARGET_OS"); + let target_env = env::var("CARGO_CFG_TARGET_ENV").expect("Cargo did not provide CARGO_CFG_TARGET_ENV"); + + let platform = match target_os.as_str() { + "macos" => "macos", + "windows" => "windows", + "linux" => "linux", + _ => panic!("Unsupported platform OS: {}", target_os), }; + let arch = if target.contains("aarch64") { "arm64" } else if target.contains("x86_64") { "x64" } else { panic!("Unsupported architecture in TARGET: {}", target) }; + + let lib_name = match platform { + "macos" => format!("{}-libtestoptimization-static.zip", platform), + "linux" => { + if target_env == "musl" { + println!("cargo:warning=Detected Linux/musl target (Alpine). Using specific musl library."); + format!("{}-{}-libtestoptimization-static-musl.zip", platform, arch) + } else { + format!("{}-{}-libtestoptimization-static.zip", platform, arch) + } + } + "windows" | _ => format!("{}-{}-libtestoptimization-static.zip", platform, arch), + }; + + println!("cargo:warning=Target: {}, Platform: {}, Arch: {}, Env: {}, Lib Filename: {}", target, platform, arch, target_env, lib_name); + // Check for custom native library search path if let Ok(search_path) = env::var(TEST_OPTIMIZATION_SDK_NATIVE_SEARCH_PATH) { link_from_search_path(platform, &lib_name, &search_path); @@ -34,8 +53,7 @@ fn main() { // Check if library files already exist let has_library = match platform { "windows" => lib_dir.join("testoptimization.lib").exists(), - "linux" => lib_dir.join("libtestoptimization.a").exists(), - "macos" => lib_dir.join("libtestoptimization.a").exists(), + "linux" | "macos" => lib_dir.join("libtestoptimization.a").exists(), _ => false, }; @@ -53,7 +71,7 @@ fn main() { println!("cargo:rustc-link-lib=static=testoptimization"); } - other_links(&target); + other_links(&target_os); } fn download_library(out_dir: &str, lib_name: &str, lib_dir: &Path) { @@ -64,20 +82,30 @@ fn download_library(out_dir: &str, lib_name: &str, lib_dir: &Path) { // Download and extract library only if it doesn't exist println!("cargo:warning=Downloading native library from: {}", url); - let mut response = ureq::get(&url) - .call() - .unwrap_or_else(|e| { - eprintln!("Failed to download native library: {}", e); + { + let mut response = ureq::get(&url) + .call() + .unwrap_or_else(|e| { + eprintln!("Failed to download native library: {}", e); + process::exit(1); + }); + + if !response.status().is_success() { + eprintln!("Error: Failed to download native library from {}. Server responded with status: {}", url, response.status()); process::exit(1); - }); + } - let mut reader = response.body_mut().as_body().into_reader(); - let mut file = BufWriter::new(File::create(&lib_zip_path).unwrap()); - io::copy(&mut reader, &mut file).unwrap_or_else(|e| { - eprintln!("Failed to write native library to disk: {}", e); - process::exit(1); - }); - file.flush().unwrap(); + let mut reader = response.body_mut().as_body().into_reader(); + let mut file = BufWriter::new(File::create(&lib_zip_path).unwrap()); + io::copy(&mut reader, &mut file).unwrap_or_else(|e| { + eprintln!("Failed to write native library to disk: {}", e); + process::exit(1); + }); + file.flush().unwrap_or_else(|e| { + eprintln!("Error: Failed to flush file buffer for {:?}: {}", lib_zip_path, e); + process::exit(1); + }); + } extract_zip(&lib_zip_path, lib_dir).expect("Failed to decompress native library"); } @@ -88,7 +116,12 @@ fn extract_zip(zip_path: &Path, target_dir: &Path) -> io::Result<()> { for i in 0..archive.len() { let mut file = archive.by_index(i)?; - let outpath = target_dir.join(file.name()); + let outpath = target_dir.join(file.mangled_name()); + + if !outpath.starts_with(target_dir) { + eprintln!("Security error: Zip entry '{}' tried to write outside the target folder (calculated path: {}). Aborting...", file.name(), outpath.display()); + return Err(io::Error::new(io::ErrorKind::PermissionDenied, "Zip Slip detected: Attempt to write outside target directory")); + } if file.name().ends_with('/') { fs::create_dir_all(&outpath)?; @@ -111,8 +144,7 @@ fn link_from_search_path(platform: &str, lib_name: &str, search_path: &str) { // First check for already extracted library files let has_library = match platform { "windows" => search_path.join("testoptimization.lib").exists(), - "linux" => search_path.join("libtestoptimization.a").exists(), - "macos" => search_path.join("libtestoptimization.a").exists(), + "linux" | "macos" => search_path.join("libtestoptimization.a").exists(), _ => false, }; @@ -134,18 +166,19 @@ fn link_from_search_path(platform: &str, lib_name: &str, search_path: &str) { } } -fn other_links(target: &str) { - if !target.contains("windows") { - // Link to the dynamic dependency - println!("cargo:rustc-link-lib=dylib=resolv"); - } else { - // Windows version requires cc as a build-dependency - #[cfg(target_os = "windows")] - configure_windows(); +fn other_links(target_os: &str) { + match target_os { + "linux" | "macos" => println!("cargo:rustc-link-lib=dylib=resolv"), + "windows" => { + // Windows version requires cc as a build-dependency + #[cfg(target_os = "windows")] + configure_windows(); + } + _ => {} } // If we are in osx, we need to add a couple of frameworks - if target.contains("apple-darwin") { + if target_os == "macos" { println!("cargo:rustc-link-lib=framework=CoreFoundation"); println!("cargo:rustc-link-lib=framework=IOKit"); println!("cargo:rustc-link-lib=framework=Security"); @@ -162,4 +195,4 @@ fn configure_windows() { // Link to the lib println!("cargo:rustc-link-lib=static=cgo"); -} +} \ No newline at end of file