Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
261 changes: 261 additions & 0 deletions .github/workflows/build-tink.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,261 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
#
# This workflow is based on:
# https://github.com/tink-crypto/tink-py/blob/main/tools/distribution/create_bdist.sh
# and https://github.com/tink-crypto/tink-py/blob/main/tools/distribution/build_linux_binary_wheels.sh
---
name: Build tink wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'tink version to build (e.g. 1.16.1)'
required: true
default: '1.16.1'
pull_request:
paths:
- '.github/workflows/build-tink.yml'
- 'patches/tink/**'

run-name: build-tink - ${{ inputs.version || '1.16.1' }}

concurrency:
group: ${{ github.workflow }}-${{ inputs.version || '1.16.1' }}-${{ github.head_ref || github.run_id }}
cancel-in-progress: true

permissions:
contents: read

env:
TINK_VERSION: ${{ inputs.version || '1.16.1' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64
# Upstream fetches this through bazelisk, which publishes no riscv64 binary,
# so it is bootstrapped from the dist archive instead. Matches .bazelversion
# at the v1.16.1 tag.
BAZEL_VERSION: '9.1.0'

jobs:
setup:
uses: $/.github/workflows/_setup.yml

bazel:
needs: [setup]
name: Bootstrap bazel (riscv64)
runs-on: ubuntu-24.04-riscv
timeout-minutes: 720

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}" \
"${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

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

# rules_python 2.2.0 (the version tink-py's own MODULE.bazel pins) already
# carries a riscv64-unknown-linux-gnu platform entry, so unlike bazel 7.x
# builds elsewhere in this repo, no rules_python/rules_java override is
# needed here.
EXTRA_BAZEL_ARGS="--tool_java_runtime_version=local_jdk" 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 tink ${{ inputs.version || '1.16.1' }} ${{ matrix.tag }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 720
needs: [setup, bazel]

strategy:
fail-fast: false
matrix:
include:
- {tag: cp310, python: '3.10'}
- {tag: cp311, python: '3.11'}
- {tag: cp312, python: '3.12'}
- {tag: cp313, python: '3.13'}
- {tag: cp314, python: '3.14'}

steps:
- name: Checkout tink v${{ env.TINK_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: tink-crypto/tink-py
ref: v${{ env.TINK_VERSION }}
path: tink
fetch-depth: 1
persist-credentials: false

- name: Checkout python-wheels
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
path: python-wheels
fetch-depth: 1
persist-credentials: false

- name: Patch tink source
working-directory: tink
run: git apply ../python-wheels/patches/tink/${{ env.TINK_VERSION }}/00*.patch

- name: Download bazel
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: bazel-${{ env.BAZEL_VERSION }}-riscv64
path: bazel-bin

- name: Build 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/tink \
-e 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
JAVA_HOME="$(dirname "$(dirname "$(readlink -f "$(command -v javac)")")")"
export JAVA_HOME
install -m 0755 /work/bazel-bin/bazel /usr/local/bin/bazel

{
echo "build --tool_java_runtime_version=local_jdk"
# A long build's default curses progress output is large enough
# that GitHub drops the job log, taking the failure with it.
echo "common --curses=no --show_progress_rate_limit=60"
} >> .bazelrc

PYTAG="cp${PYTHON_VERSION/./}"
export PATH="/opt/python/${PYTAG}-${PYTAG}/bin:${PATH}"
python3 -m pip install -q -U setuptools wheel auditwheel

# No protoc release ships a riscv64 binary; setup.py's own main()
# requires one on PATH to (re)generate tink/proto/*_pb2.py, so build
# it from the same protobuf version (30.1) this project's own
# MODULE.bazel already pins for the real extension build.
bazel build @protobuf//:protoc
# `find bazel-bin -name protoc` can miss it: bazel-bin's external/<repo>
# entries are symlinks that plain find does not descend into without -L.
# `cquery --output=files` asks bazel for the real artifact path directly.
install -m 0755 "$(bazel cquery --output=files @protobuf//:protoc 2>/dev/null | tail -1)" /usr/local/bin/protoc
export PROTOC=/usr/local/bin/protoc

# Required to fix https://github.com/pypa/manylinux/issues/357.
export LD_LIBRARY_PATH=/usr/local/lib

python3 -m pip wheel .

mkdir -p /work/wheelhouse
for wheel in ./tink-*.whl; do
auditwheel repair "${wheel}" --plat manylinux_2_39_riscv64 -w /work/wheelhouse
done
SCRIPT

- name: Upload build log
if: failure()
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: tink-${{ env.TINK_VERSION }}-${{ matrix.tag }}-build-log
path: build.log

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: tink-${{ env.TINK_VERSION }}-${{ matrix.tag }}-manylinux_riscv64
path: wheelhouse/*.whl
if-no-files-found: error

- name: Test wheel
env:
PYTHON_VERSION: ${{ matrix.python }}
# tink[all] pulls a real dependency tree (google-auth -> cryptography, plus
# grpcio, protobuf, etc.) straight from public PyPI with no index override --
# PyPI's newest cryptography (50.0.1) ships no riscv64 wheel and falls back to
# a source build needing Rust, which rustup has no riscv64 target for. Our
# registry has cryptography up to 49.0.0 with a real riscv64 wheel.
PIP_EXTRA_INDEX_URL: https://pypi.riseproject.dev/simple/
PIP_ONLY_BINARY: cryptography
run: |
docker run --rm -i --network=host \
-v "${GITHUB_WORKSPACE}:/work" \
-w /work/tink \
-e PYTHON_VERSION \
-e PIP_EXTRA_INDEX_URL \
-e PIP_ONLY_BINARY \
"${MANYLINUX_RISCV64_IMAGE}" \
bash <<'SCRIPT'
set -eux

PYTAG="cp${PYTHON_VERSION/./}"
PYTHON_BIN="/opt/python/${PYTAG}-${PYTAG}/bin/python3"
"${PYTHON_BIN}" -m pip install "$(echo /work/wheelhouse/tink-*.whl)[all]"

# Mirrors tools/distribution/test_dist.sh, minus tink/integration/,
# whose *_test.py files call out to live AWS/GCP/Vault KMS services
# this CI has no credentials for, and tink/testing/helper_test.py,
# whose test_tink_py_testdata_path only passes under `bazel test`
# (it requires TINK_PYTHON_ROOT_PATH or TEST_SRCDIR, both Bazel
# runfiles env vars that don't exist when running the installed
# wheel's tests directly like every other file here).
find tink/ -not -path '*cc/pybind*' -not -path '*integration*' \
-not -path '*testing/helper_test.py' \
-type f -name '*_test.py' -print0 \
| xargs -0 -n1 "${PYTHON_BIN}"

"${PYTHON_BIN}" - <<'PY'
import importlib.metadata

licenses = {
str(f).rsplit("/", 1)[1]
for f in importlib.metadata.files("tink")
if ".dist-info/licenses/" in str(f)
} - {""}
expected = {"LICENSE"} | {
"LICENSE." + name
for name in ["abseil-cpp", "boringssl", "protobuf", "pybind11", "tink_cc"]
}
assert licenses == expected, licenses
PY
SCRIPT

publish:
name: Publish tink ${{ inputs.version || '1.16.1' }}
needs: [setup, build_wheels]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: tink-${{ inputs.version || '1.16.1' }}-*-manylinux_riscv64
Loading