253 lines
11 KiB
YAML
253 lines
11 KiB
YAML
|
A
|
name: Build and Push Holoscan Application
|
||
|
|
|
||
|
|
on:
|
||
|
|
push:
|
||
|
|
paths:
|
||
|
|
- '.riahub/workflows/**'
|
||
|
|
|
||
|
|
env:
|
||
|
|
REGISTRY_HOST:
|
||
|
|
NAMESPACE: ${{ gitea.repository_owner }}
|
||
|
|
REGISTRY_USER: ${{ gitea.actor }}
|
||
|
|
REGISTRY_TOKEN: ${{ secrets.TOKEN_DEV }}
|
||
|
|
SOURCE_REPO: qoherent/RIA_Example
|
||
|
|
SOURCE_BRANCH: main
|
||
|
|
SOURCE_FILE: Applications/Example_Application.json
|
||
|
|
APP_NAME: applications_example_application_json
|
||
|
|
DOCKER_IMAGE_NAME: applications-example-application-json
|
||
|
|
TARGET_IMAGE_TAG: /${{ gitea.repository_owner }}/ria-applications-example-application-json-runtime:latest
|
||
|
|
BUILD_TOOLS_REPO: ${{ gitea.repository_owner }}/qac-mvp
|
||
|
|
RIA_CORE_REPO: ${{ gitea.repository_owner }}/ria-core
|
||
|
|
ENABLE_HOLOSCAN: true
|
||
|
|
|
||
|
|
jobs:
|
||
|
|
build:
|
||
|
|
name: Build and Push RIA
|
||
|
|
runs-on: ubuntu-latest
|
||
|
|
permissions:
|
||
|
|
packages: write
|
||
|
|
contents: read
|
||
|
|
steps:
|
||
|
|
|
||
|
|
- name: Verify Docker is available
|
||
|
|
run: docker info --format '{{.ServerVersion}}'
|
||
|
|
|
||
|
|
- name: Login to RIA Hub Container Registry
|
||
|
|
run: |
|
||
|
|
LOWERCASE_USER=$(echo "$REGISTRY_USER" | tr '[:upper:]' '[:lower:]')
|
||
|
|
echo "$REGISTRY_TOKEN" | docker login $REGISTRY_HOST -u "$LOWERCASE_USER" --password-stdin
|
||
|
|
|
||
|
|
- name: Checkout build tools repository
|
||
|
|
run: |
|
||
|
|
git clone "http://riahub.ai/${{ env.BUILD_TOOLS_REPO }}.git" build-tools
|
||
|
|
cd build-tools && git checkout main
|
||
|
|
|
||
|
|
- name: Checkout ria_core library
|
||
|
|
run: |
|
||
|
|
git clone "http://riahub.ai/${{ env.RIA_CORE_REPO }}.git" ria-core
|
||
|
|
cd ria-core && git checkout main
|
||
|
|
|
||
|
|
- name: Checkout source repository with application
|
||
|
|
run: |
|
||
|
|
ENCODED_USER=$(printf '%s' "$REGISTRY_USER" | sed 's/:/%3A/g; s/@/%40/g; s/#/%23/g; s/?/%3F/g')
|
||
|
|
ENCODED_TOKEN=$(printf '%s' "$REGISTRY_TOKEN" | sed 's/:/%3A/g; s/@/%40/g; s/#/%23/g; s/?/%3F/g')
|
||
|
|
git clone "http://$ENCODED_USER:$ENCODED_TOKEN@riahub.ai/qoherent/RIA_Example.git" source-app
|
||
|
|
cd source-app && git checkout $SOURCE_BRANCH
|
||
|
|
|
||
|
|
- name: Install dependencies
|
||
|
|
run: |
|
||
|
|
command -v jq >/dev/null || { apt-get update && apt-get install -y python3 python3-pip python3-venv jq; }
|
||
|
|
pip install -r build-tools/requirements.txt || true
|
||
|
|
|
||
|
|
- name: Parse Application Config
|
||
|
|
id: parse_config
|
||
|
|
run: |
|
||
|
|
CONFIG_PATH="source-app/$SOURCE_FILE"
|
||
|
|
BACKEND=$(jq -r '.ops[] | select(.type=="inference") | .specs[] | select(.key=="backend") | .default // "onnxrt"' $CONFIG_PATH | head -n 1)
|
||
|
|
MODEL_VAL=$(jq -r '.ops[] | select(.type=="inference") | .specs[] | select(.key=="model_path_map" or .key=="model_path" or .key=="model_file") | .default' $CONFIG_PATH | head -n 1)
|
||
|
|
|
||
|
|
if [ -z "$MODEL_VAL" ] || [ "$MODEL_VAL" == "null" ]; then
|
||
|
|
if [ "$BACKEND" == "trt" ]; then TARGET_NAME="model.engine"; else TARGET_NAME="model.onnx"; fi
|
||
|
|
else
|
||
|
|
TARGET_NAME=$(basename "$MODEL_VAL")
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ "$ENABLE_HOLOSCAN" = "false" ]; then
|
||
|
|
BACKEND="onnxrt"
|
||
|
|
fi
|
||
|
|
|
||
|
|
echo "INFERENCE_BACKEND=$BACKEND" >> $GITHUB_ENV
|
||
|
|
echo "TARGET_MODEL_NAME=$TARGET_NAME" >> $GITHUB_ENV
|
||
|
|
|
||
|
|
- name: Generate C++ code and CMakeLists.txt
|
||
|
|
id: codegen
|
||
|
|
run: |
|
||
|
|
APP_DIR="apps/$APP_NAME"
|
||
|
|
mkdir -p "$APP_DIR"
|
||
|
|
cp "source-app/$SOURCE_FILE" "$APP_DIR/app_flow_graph.json"
|
||
|
|
|
||
|
|
if [ "$ENABLE_HOLOSCAN" = "false" ]; then
|
||
|
|
python3 build-tools/src/patch_for_cpu.py "$APP_DIR/app_flow_graph.json"
|
||
|
|
fi
|
||
|
|
|
||
|
|
python build-tools/src/full_generator.py --config "$APP_DIR/app_flow_graph.json" --output-dir "$APP_DIR"
|
||
|
|
|
||
|
|
if [ -f "$APP_DIR/src/CMakeLists.txt" ]; then
|
||
|
|
mv "$APP_DIR/src/CMakeLists.txt" "$APP_DIR/CMakeLists.txt"
|
||
|
|
fi
|
||
|
|
|
||
|
|
TARGET_NAME=$(grep "add_executable" "$APP_DIR/CMakeLists.txt" | head -1 | cut -d'(' -f2 | cut -d' ' -f1)
|
||
|
|
echo "cmake_target=$TARGET_NAME" >> $GITEA_OUTPUT
|
||
|
|
|
||
|
|
- name: Generate YAML config file
|
||
|
|
run: |
|
||
|
|
python build-tools/src/yaml_generator.py --input apps/$APP_NAME/app_flow_graph.json --output apps/$APP_NAME/config.yaml
|
||
|
|
|
||
|
|
- name: Stage Inference Model
|
||
|
|
run: |
|
||
|
|
APP_DIR="apps/$APP_NAME"
|
||
|
|
mkdir -p "$APP_DIR/assets"
|
||
|
|
|
||
|
|
FOUND_FILE=""
|
||
|
|
if [ -f "source-app/$TARGET_MODEL_NAME" ]; then
|
||
|
|
FOUND_FILE="source-app/$TARGET_MODEL_NAME"
|
||
|
|
else
|
||
|
|
if [ "$INFERENCE_BACKEND" == "trt" ]; then
|
||
|
|
FOUND_FILE=$(find source-app -name "*.trt" -o -name "*.engine" -o -name "*.onnx" | head -n 1)
|
||
|
|
else
|
||
|
|
FOUND_FILE=$(find source-app -name "*.onnx" | head -n 1)
|
||
|
|
fi
|
||
|
|
fi
|
||
|
|
|
||
|
|
if [ -n "$FOUND_FILE" ]; then
|
||
|
|
echo "Staging model: $FOUND_FILE -> $APP_DIR/assets/$TARGET_MODEL_NAME"
|
||
|
|
cp "$FOUND_FILE" "$APP_DIR/assets/$TARGET_MODEL_NAME"
|
||
|
|
else
|
||
|
|
HAS_INFERENCE=$(jq '[.ops[] | select(.type=="inference")] | length' "source-app/$SOURCE_FILE")
|
||
|
|
if [ "$HAS_INFERENCE" -gt 0 ]; then
|
||
|
|
echo "ERROR: Pipeline has inference operator(s) but no model file found in source repository."
|
||
|
|
echo "Looked for: $TARGET_MODEL_NAME, *.onnx, *.trt, *.engine"
|
||
|
|
exit 1
|
||
|
|
fi
|
||
|
|
echo "No inference model needed for this pipeline."
|
||
|
|
fi
|
||
|
|
|
||
|
|
- name: Prepare Docker Context
|
||
|
|
run: |
|
||
|
|
APP_DIR="apps/$APP_NAME"
|
||
|
|
cp -r ria-core "$APP_DIR/ria_core"
|
||
|
|
rm -rf "$APP_DIR/ria_core/.git" "$APP_DIR/ria_core/build"
|
||
|
|
if [ "$ENABLE_HOLOSCAN" = "false" ]; then
|
||
|
|
cp build-tools/src/install_dummy_driver.py "$APP_DIR/"
|
||
|
|
fi
|
||
|
|
|
||
|
|
- name: Generate Dockerfile
|
||
|
|
run: |
|
||
|
|
APP_DIR="apps/$APP_NAME"
|
||
|
|
CMAKE_TARGET="${{ steps.codegen.outputs.cmake_target }}"
|
||
|
|
DF="$APP_DIR/Dockerfile"
|
||
|
|
|
||
|
|
|
||
|
|
cat > "$DF" <<'DOCKERFILE_BUILD'
|
||
|
|
# syntax=docker/dockerfile:1
|
||
|
|
FROM nvcr.io/nvidia/clara-holoscan/holoscan:v3.4.0-dgpu AS build
|
||
|
|
WORKDIR /workspace
|
||
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
||
|
|
RUN apt-get update && apt-get -y install --no-install-recommends pkg-config build-essential cmake git wget libiio-dev libiio0 libad9361-dev libad9361-0 libfftw3-dev && apt-get clean
|
||
|
|
RUN wget -q https://github.com/microsoft/onnxruntime/releases/download/v1.14.1/onnxruntime-linux-x64-1.14.1.tgz && \
|
||
|
|
tar -xzf onnxruntime-linux-x64-1.14.1.tgz && \
|
||
|
|
mkdir -p /usr/include/onnxruntime && \
|
||
|
|
cp -r onnxruntime-linux-x64-1.14.1/include/* /usr/include/onnxruntime/ && \
|
||
|
|
cp -r onnxruntime-linux-x64-1.14.1/lib/* /usr/lib/ && \
|
||
|
|
rm -rf onnxruntime-linux-x64-1.14.1*
|
||
|
|
RUN wget -q https://github.com/Kitware/CMake/releases/download/v3.31.0/cmake-3.31.0-linux-x86_64.tar.gz && \
|
||
|
|
tar -xzf cmake-3.31.0-linux-x86_64.tar.gz -C /opt && \
|
||
|
|
mv /opt/cmake-3.31.0-linux-x86_64 /opt/cmake && \
|
||
|
|
rm cmake-3.31.0-linux-x86_64.tar.gz
|
||
|
|
DOCKERFILE_BUILD
|
||
|
|
|
||
|
|
if [ "$CPU_ONLY_MODE" = "true" ]; then
|
||
|
|
echo 'COPY install_dummy_driver.py .' >> "$DF"
|
||
|
|
echo 'RUN python3 install_dummy_driver.py && rm install_dummy_driver.py' >> "$DF"
|
||
|
|
fi
|
||
|
|
|
||
|
|
cat >> "$DF" <<DOCKERFILE_APP
|
||
|
|
COPY CMakeLists.txt .
|
||
|
|
COPY src/ src/
|
||
|
|
COPY ria_core/ ria_core/
|
||
|
|
COPY app_flow_graph.json .
|
||
|
|
COPY assets/ assets/
|
||
|
|
RUN /opt/cmake/bin/cmake -S . -B build && /opt/cmake/bin/cmake --build build --target "$CMAKE_TARGET" --parallel
|
||
|
|
RUN cp \$(find build -name "$CMAKE_TARGET" -type f | head -n 1) .
|
||
|
|
DOCKERFILE_APP
|
||
|
|
|
||
|
|
cat >> "$DF" <<'DOCKERFILE_RUNTIME'
|
||
|
|
|
||
|
|
FROM nvcr.io/nvidia/clara-holoscan/holoscan:v3.4.0-dgpu
|
||
|
|
WORKDIR /app
|
||
|
|
ENV DEBIAN_FRONTEND=noninteractive
|
||
|
|
LABEL ria.app="applications_example_application_json" ria.profile="nvidia-x86" ria.hardware="pluto" ria.operators="PlutoTXOp,inference_builtin,model_mapper,pluto_source,postprocessor,preprocessor,spectrogram,spectrogram_dashboard" ria.version="${{ gitea.sha }}"
|
||
|
|
RUN (CODENAME=$(. /etc/os-release && echo $VERSION_CODENAME) && grep -q 'universe' /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null && true || sed -i '/^Components:/ s/$/ universe/' /etc/apt/sources.list.d/ubuntu.sources 2>/dev/null || echo "deb http://archive.ubuntu.com/ubuntu $CODENAME universe" >> /etc/apt/sources.list) && apt-get update && apt-get -y install --no-install-recommends libiio-dev libiio0 libad9361-dev libad9361-0 libfftw3-3 && apt-get clean && rm -rf /var/lib/apt/lists/*
|
||
|
|
COPY --from=build /usr/lib/libonnxruntime* /usr/lib/
|
||
|
|
ENV LD_LIBRARY_PATH=\$LD_LIBRARY_PATH:/usr/lib/x86_64-linux-gnu
|
||
|
|
DOCKERFILE_RUNTIME
|
||
|
|
|
||
|
|
echo 'USER root' >> "$DF"
|
||
|
|
|
||
|
|
cat >> "$DF" <<DOCKERFILE_FINAL
|
||
|
|
COPY --from=build /workspace/$CMAKE_TARGET /app/$CMAKE_TARGET
|
||
|
|
COPY app_flow_graph.json .
|
||
|
|
COPY config.yaml .
|
||
|
|
COPY --from=build /workspace/assets/$TARGET_MODEL_NAME /app/$TARGET_MODEL_NAME
|
||
|
|
RUN chmod +x /app/$CMAKE_TARGET
|
||
|
|
ENTRYPOINT ["/app/$CMAKE_TARGET"]
|
||
|
|
DOCKERFILE_FINAL
|
||
|
|
|
||
|
|
- name: Build Docker image
|
||
|
|
run: |
|
||
|
|
APP_DIR="apps/$APP_NAME"
|
||
|
|
TAG="${{ gitea.run_number }}"
|
||
|
|
echo "Building Docker image: $DOCKER_IMAGE_NAME:$TAG"
|
||
|
|
docker build -t "$DOCKER_IMAGE_NAME:$TAG" -f "$APP_DIR/Dockerfile" "$APP_DIR" 2>&1 | tee build.log
|
||
|
|
|
||
|
|
- name: Push to Registry
|
||
|
|
run: |
|
||
|
|
TAG="${{ gitea.run_number }}"
|
||
|
|
TARGET_LATEST="${{ env.TARGET_IMAGE_TAG }}"
|
||
|
|
TARGET_SHA="${REGISTRY_HOST}/${NAMESPACE}/ria-${DOCKER_IMAGE_NAME}-runtime:${{ gitea.sha }}"
|
||
|
|
docker tag "$DOCKER_IMAGE_NAME:$TAG" "$TARGET_LATEST"
|
||
|
|
docker tag "$DOCKER_IMAGE_NAME:$TAG" "$TARGET_SHA"
|
||
|
|
echo "Pushing: $TARGET_LATEST"
|
||
|
|
docker push "$TARGET_LATEST"
|
||
|
|
echo "Pushing: $TARGET_SHA"
|
||
|
|
docker push "$TARGET_SHA"
|
||
|
|
|
||
|
|
- name: Build Summary
|
||
|
|
if: always()
|
||
|
|
run: |
|
||
|
|
echo "## Build Summary" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "| Field | Value |" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "|-------|-------|" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "| Application | $APP_NAME |" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "| Source | $SOURCE_REPO/$SOURCE_FILE ($SOURCE_BRANCH) |" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "| Image | ${{ env.TARGET_IMAGE_TAG }} |" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "| Mode | Holoscan |" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "| Run | #${{ gitea.run_number }} |" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "" >> $GITHUB_STEP_SUMMARY
|
||
|
|
if [ "$ENABLE_HOLOSCAN" = "false" ]; then
|
||
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "docker pull ${{ env.TARGET_IMAGE_TAG }}" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "docker run -it --rm --net=host --privileged ${{ env.TARGET_IMAGE_TAG }}" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||
|
|
else
|
||
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "docker pull ${{ env.TARGET_IMAGE_TAG }}" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "docker run --runtime=nvidia --gpus all -it --rm --net=host --privileged ${{ env.TARGET_IMAGE_TAG }}" >> $GITHUB_STEP_SUMMARY
|
||
|
|
echo "\`\`\`" >> $GITHUB_STEP_SUMMARY
|
||
|
|
fi
|
||
|
|
|
||
|
|
- uses: actions/upload-artifact@v3
|
||
|
|
with:
|
||
|
|
name: deployment_instructions_${{ gitea.run_number }}
|
||
|
|
path: build.log
|