Repository navigation
docs: Update projects #6
Workflow file for this run
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| # SPDX-FileCopyrightText: 2026 The RISE Project | |
| # SPDX-License-Identifier: MIT | |
| --- | |
| # This workflow is based on: | |
| # https://github.com/google/yggdrasil-decision-forests/blob/main/.github/workflows/python_build_test.yml | |
| # and yggdrasil_decision_forests/port/python/tools/{build_test_linux.sh,package_linux.sh,collect_pip_files.py} | |
| name: Build ydf wheels (riscv64) | |
| on: | |
| workflow_dispatch: | |
| inputs: | |
| version: | |
| description: 'Version glob to (re)build; empty builds every version of docs/packages/ydf.yaml not released yet' | |
| required: false | |
| default: '' | |
| pull_request: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/build-ydf.yml' | |
| - 'docs/packages/ydf.yaml' | |
| push: | |
| branches: [main] | |
| paths: | |
| - '.github/workflows/build-ydf.yml' | |
| - 'docs/packages/ydf.yaml' | |
| concurrency: | |
| group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} | |
| cancel-in-progress: true | |
| permissions: | |
| contents: read | |
| env: | |
| # Upstream cuts PYDF releases without git tags; pin the release commit | |
| # ("[YDF] Release PYDF 0.16.1"). Update alongside YDF_VERSION. | |
| YDF_REF: cd115b404c35aa3e15879fc64addb50da0fa8aa7 | |
| MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 | |
| BAZEL_VERSION: '7.5.0' | |
| RULES_PYTHON_VERSION: '0.33.2' | |
| RULES_JAVA_VERSION: '7.6.5' | |
| PROJECT_RULES_PYTHON_VERSION: '1.6.0' | |
| jobs: | |
| setup: | |
| uses: $/.github/workflows/_setup.yml | |
| with: | |
| package: ydf | |
| version: ${{ inputs.version }} | |
| bazel: | |
| needs: [setup] | |
| if: needs.setup.outputs.versions != '[]' | |
| name: Bootstrap bazel (riscv64) | |
| runs-on: ubuntu-24.04-riscv | |
| timeout-minutes: 120 | |
| steps: | |
| - name: Restore bazel binary | |
| id: cache | |
| uses: actions/cache@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: bazel-bin | |
| key: bazel-${{ env.BAZEL_VERSION }}-manylinux_riscv64 | |
| - name: Bootstrap bazel ${{ env.BAZEL_VERSION }} | |
| if: steps.cache.outputs.cache-hit != 'true' | |
| run: | | |
| mkdir -p bazel-bin | |
| docker run --rm -i --network=host \ | |
| -v "${GITHUB_WORKSPACE}:/work" \ | |
| -w /work \ | |
| -e BAZEL_VERSION="${BAZEL_VERSION}" \ | |
| -e RULES_PYTHON_VERSION="${RULES_PYTHON_VERSION}" \ | |
| -e RULES_JAVA_VERSION="${RULES_JAVA_VERSION}" \ | |
| "${MANYLINUX_RISCV64_IMAGE}" \ | |
| bash <<'SCRIPT' | |
| set -eux | |
| dnf install -y --disablerepo=extras --setopt=install_weak_deps=False java-21-openjdk-devel zip unzip | |
| JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$(command -v javac)")")")" | |
| export JAVA_HOME | |
| # rules_python 0.33.2's PLATFORMS has no riscv64 entry, aborting the bootstrap | |
| # (bazelbuild/bazel#23018). Any linux entry is a safe stand-in: the toolchain it | |
| # names is never selected on a riscv64 host. Fixed in bazel 8.2.0. | |
| mkdir -p /tmp/rules_python | |
| curl -fsSLo /tmp/rules_python.tar.gz "https://github.com/bazel-contrib/rules_python/releases/download/${RULES_PYTHON_VERSION}/rules_python-${RULES_PYTHON_VERSION}.tar.gz" | |
| tar -xzf /tmp/rules_python.tar.gz -C /tmp/rules_python --strip-components=1 | |
| sed -i 's|fail("No platform declared for host OS {} on arch {}".format(os_name, arch))|return "x86_64-unknown-linux-gnu"|' \ | |
| /tmp/rules_python/python/private/toolchains_repo.bzl | |
| # rules_java 7.x maps riscv64 to a stray-colon include path for jni_md.h. | |
| mkdir -p /tmp/rules_java | |
| curl -fsSLo /tmp/rules_java.tar.gz "https://github.com/bazelbuild/rules_java/releases/download/${RULES_JAVA_VERSION}/rules_java-${RULES_JAVA_VERSION}.tar.gz" | |
| tar -xzf /tmp/rules_java.tar.gz -C /tmp/rules_java | |
| sed -i 's|\[":include/linux"\]|["include/linux"]|g' /tmp/rules_java/toolchains/BUILD | |
| mkdir -p /tmp/bazel-src | |
| cd /tmp/bazel-src | |
| curl -fsSLo dist.zip "https://github.com/bazelbuild/bazel/releases/download/${BAZEL_VERSION}/bazel-${BAZEL_VERSION}-dist.zip" | |
| unzip -q dist.zip | |
| EXTRA_BAZEL_ARGS="--tool_java_runtime_version=local_jdk \ | |
| --override_module=rules_python=/tmp/rules_python \ | |
| --override_module=rules_java=/tmp/rules_java" \ | |
| bash ./compile.sh | |
| install -m 0755 output/bazel /work/bazel-bin/bazel | |
| SCRIPT | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: bazel-${{ env.BAZEL_VERSION }}-riscv64 | |
| path: bazel-bin/bazel | |
| if-no-files-found: error | |
| build_wheels: | |
| name: Build ydf ${{ matrix.version }} ${{ matrix.tag }}-manylinux_riscv64 | |
| needs: [setup, bazel] | |
| if: needs.setup.outputs.versions != '[]' | |
| runs-on: ubuntu-24.04-riscv | |
| timeout-minutes: 720 | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| version: ${{ fromJSON(needs.setup.outputs.versions) }} | |
| # ydf 0.16.1 ships no cp314 wheel on any platform and its own setup.py classifiers | |
| # and CI matrix stop at 3.13 (CLAUDE.md gotcha 614); cp314 segfaults in | |
| # pybind11_protobuf, not this port. | |
| include: | |
| - {tag: cp312, python: '3.12'} | |
| - {tag: cp313, python: '3.13'} | |
| env: | |
| YDF_VERSION: ${{ matrix.version }} | |
| steps: | |
| - name: Checkout yggdrasil-decision-forests ${{ env.YDF_VERSION }} | |
| uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 | |
| with: | |
| repository: google/yggdrasil-decision-forests | |
| ref: ${{ env.YDF_REF }} | |
| path: yggdrasil-decision-forests | |
| fetch-depth: 1 | |
| persist-credentials: false | |
| - name: Download bazel | |
| uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1 | |
| with: | |
| name: bazel-${{ env.BAZEL_VERSION }}-riscv64 | |
| path: bazel-bin | |
| - name: Restore bazel disk cache | |
| id: bazel-cache | |
| uses: actions/cache/restore@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: .bazel-cache | |
| key: ydf-bazel-cache-${{ matrix.tag }}-manylinux_riscv64-${{ github.sha }} | |
| restore-keys: | | |
| ydf-bazel-cache-${{ matrix.tag }}-manylinux_riscv64- | |
| - name: Build and package the wheel | |
| env: | |
| PYTHON_VERSION: ${{ matrix.python }} | |
| run: | | |
| mkdir -p wheelhouse | |
| set -o pipefail | |
| docker run --rm -i --network=host \ | |
| -v "${GITHUB_WORKSPACE}:/work" \ | |
| -w /work \ | |
| -e PYTHON_VERSION \ | |
| -e PROJECT_RULES_PYTHON_VERSION \ | |
| "${MANYLINUX_RISCV64_IMAGE}" \ | |
| bash <<'SCRIPT' 2>&1 | tee build.log | |
| set -eux | |
| dnf install -y --disablerepo=extras --setopt=install_weak_deps=False java-21-openjdk-devel zip unzip rsync clang lld | |
| JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$(command -v javac)")")")" | |
| export JAVA_HOME | |
| # com_google_highway's riscv64 copts unconditionally add the Clang-only | |
| # -menable-experimental-extensions flag (CLAUDE.md gotcha 567); gcc rejects it. | |
| CC=clang | |
| CXX=clang++ | |
| export CC CXX | |
| install -m 0755 /work/bazel-bin/bazel /usr/local/bin/bazel | |
| PYTAG="cp${PYTHON_VERSION/./}" | |
| PYTHON_BIN="/opt/python/${PYTAG}-${PYTAG}/bin/python3" | |
| export PATH="/opt/python/${PYTAG}-${PYTAG}/bin:${PATH}" | |
| ln -sf "${PYTHON_BIN}" /usr/local/bin/python3 | |
| "${PYTHON_BIN}" -m pip install -q -U pip setuptools build auditwheel | |
| # protobuf/googletest declare pip hubs for CPython versions | |
| # python-build-standalone does not publish for riscv64; rules_python | |
| # aborts resolving them even though the build never uses those hubs. | |
| mkdir -p /tmp/rules_python | |
| curl -fsSLo /tmp/rules_python.tar.gz \ | |
| "https://github.com/bazel-contrib/rules_python/releases/download/${PROJECT_RULES_PYTHON_VERSION}/rules_python-${PROJECT_RULES_PYTHON_VERSION}.tar.gz" | |
| tar -xzf /tmp/rules_python.tar.gz -C /tmp/rules_python --strip-components=1 | |
| python3 - <<'PATCH' | |
| path = "/tmp/rules_python/python/private/pypi/extension.bzl" | |
| source = open(path).read() | |
| old = " if python_name not in available_interpreters:\n fail((" | |
| new = ( | |
| " if python_name not in available_interpreters:\n" | |
| " python_name = sorted(available_interpreters)[0]\n" | |
| " if False:\n" | |
| " fail((" | |
| ) | |
| assert old in source | |
| open(path, "w").write(source.replace(old, new, 1)) | |
| PATCH | |
| cd /work/yggdrasil-decision-forests/yggdrasil_decision_forests/port/python | |
| { | |
| echo "build --override_module=rules_python=/tmp/rules_python" | |
| echo "common --curses=no --show_progress_rate_limit=60" | |
| } >> .bazelrc | |
| "${PYTHON_BIN}" -m pip install -q --only-binary=:all: \ | |
| --extra-index-url https://pypi.riseproject.dev/simple/ \ | |
| -r requirements.txt | |
| mkdir -p /work/.bazel-cache/disk /work/.bazel-cache/repository | |
| bazel build --config=linux_cpp17 --features=-fully_static_link \ | |
| --disk_cache=/work/.bazel-cache/disk \ | |
| --repository_cache=/work/.bazel-cache/repository \ | |
| --action_env PYTHON_BIN_PATH="${PYTHON_BIN}" -- //ydf/...:all | |
| "${PYTHON_BIN}" tools/collect_pip_files.py | |
| OUTPUT_BASE="$(bazel info output_base)" | |
| mkdir -p tmp_package/licenses | |
| for dir in "${OUTPUT_BASE}"/external/*/; do | |
| name="$(basename "${dir}")" | |
| case "${name}" in | |
| rules_*|bazel_skylib*|platforms*|bazel_tools*|googletest*|emsdk*|local_python3*|local_toolchains*|pybind11_bazel*) | |
| continue | |
| ;; | |
| esac | |
| file="$(find "${dir}" -maxdepth 1 -type f \( -iname 'LICENSE*' -o -iname 'COPYING*' \) | head -1)" | |
| [ -n "${file}" ] || continue | |
| clean_name="${name%%~*}" | |
| cp "${file}" "tmp_package/licenses/LICENSE.${clean_name}" | |
| done | |
| cd tmp_package | |
| cp licenses/LICENSE.* . | |
| rm -rf licenses | |
| "${PYTHON_BIN}" -m build | |
| cd .. | |
| PACKAGE="$("${PYTHON_BIN}" -c 'import sys; print(f"{sys.version_info.major}{sys.version_info.minor}")')" | |
| cp tmp_package/dist/ydf-*-cp${PACKAGE}-cp${PACKAGE}*-linux_*.whl . | |
| auditwheel repair --plat manylinux_2_39_riscv64 -w /work/wheelhouse ydf-*.whl | |
| SCRIPT | |
| - name: Save bazel disk cache | |
| if: always() && steps.bazel-cache.outputs.cache-hit != 'true' | |
| uses: actions/cache/save@55cc8345863c7cc4c66a329aec7e433d2d1c52a9 # v6.1.0 | |
| with: | |
| path: .bazel-cache | |
| key: ydf-bazel-cache-${{ matrix.tag }}-manylinux_riscv64-${{ github.sha }} | |
| - name: Upload build log | |
| if: failure() | |
| uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ydf-${{ matrix.version }}-${{ matrix.tag }}-build-log | |
| path: build.log | |
| - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 | |
| with: | |
| name: ydf-${{ matrix.version }}-${{ matrix.tag }}-manylinux_riscv64 | |
| path: wheelhouse/*.whl | |
| if-no-files-found: error | |
| - name: Test wheel | |
| env: | |
| PYTHON_VERSION: ${{ matrix.python }} | |
| run: | | |
| docker run --rm -i --network=host \ | |
| -v "${GITHUB_WORKSPACE}:/work" \ | |
| -e PYTHON_VERSION \ | |
| "${MANYLINUX_RISCV64_IMAGE}" \ | |
| bash <<'SCRIPT' | |
| set -eux | |
| PYTAG="cp${PYTHON_VERSION/./}" | |
| PYTHON_BIN="/opt/python/${PYTAG}-${PYTAG}/bin/python3" | |
| "${PYTHON_BIN}" -m pip install -q /work/wheelhouse/*.whl pandas | |
| cd /tmp | |
| "${PYTHON_BIN}" /work/yggdrasil-decision-forests/yggdrasil_decision_forests/port/python/examples/minimal.py | |
| "${PYTHON_BIN}" - <<'PY' | |
| import importlib.metadata | |
| import ydf | |
| print(ydf.__version__) | |
| dist = importlib.metadata.distribution("ydf") | |
| so = next(f for f in dist.files if str(f).endswith(".so")) | |
| with open(dist.locate_file(so), "rb") as f: | |
| header = f.read(20) | |
| assert header[:4] == b"\x7fELF", header | |
| assert header[18] == 0xF3, header # EM_RISCV | |
| licenses = { | |
| str(f).rsplit("/", 1)[-1] | |
| for f in dist.files | |
| if ".dist-info/licenses/" in str(f) | |
| } | |
| print(sorted(licenses)) | |
| assert "LICENSE" in licenses | |
| assert "LICENSE.abseil-cpp" in licenses | |
| assert "LICENSE.protobuf" in licenses | |
| assert "LICENSE.pybind11_protobuf" in licenses | |
| PY | |
| SCRIPT | |
| publish: | |
| name: Publish ydf ${{ matrix.version }} | |
| needs: [setup, build_wheels] | |
| if: needs.setup.outputs.versions != '[]' | |
| strategy: | |
| fail-fast: false | |
| matrix: | |
| version: ${{ fromJSON(needs.setup.outputs.versions) }} | |
| permissions: | |
| contents: write | |
| pull-requests: write | |
| uses: $/.github/workflows/_publish-wheel.yml | |
| secrets: | |
| app-private-key: ${{ secrets.RISEPROJECT_APP_PRIVATE_KEY }} | |
| with: | |
| artifact-pattern: ydf-${{ matrix.version }}-*-manylinux_riscv64 |