Skip to content
Merged
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
156 changes: 156 additions & 0 deletions .github/workflows/build-py-ed25519-zebra-bindings.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,156 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on the `linux`/`sdist` jobs of
# https://github.com/JAMdotTech/py-ed25519/blob/v1.3.0/.github/workflows/CI.yml
name: Build py-ed25519-zebra-bindings wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'py-ed25519-zebra-bindings version to build (git tag without the v prefix, e.g. 1.3.0)'
required: true
default: '1.3.0'
pull_request:
paths:
- '.github/workflows/build-py-ed25519-zebra-bindings.yml'

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

permissions:
contents: read # to fetch code (actions/checkout)

env:
# `inputs.version` is empty on pull_request events; default to 1.3.0 there.
PY_ED25519_ZEBRA_BINDINGS_VERSION: ${{ inputs.version || '1.3.0' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

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

python_sdist:
needs: [setup]
runs-on: ubuntu-latest
outputs:
sdist_artifact_name: ${{ steps.build_sdist.outputs.sdist_artifact_name }}
package_version: ${{ steps.build_sdist.outputs.package_version }}
steps:
- name: Checkout py-ed25519-zebra-bindings v${{ env.PY_ED25519_ZEBRA_BINDINGS_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: JAMdotTech/py-ed25519
ref: v${{ env.PY_ED25519_ZEBRA_BINDINGS_VERSION }}
persist-credentials: false

- name: Install Python
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: '3.12'
activate-environment: true
enable-cache: false

- name: Build sdist
id: build_sdist
run: |
set -euo pipefail
rm -rf dist

uv pip install 'maturin>=1.7.0,<1.10.0' build
python -m build --sdist --outdir dist

sdist_name="$(ls dist)"
{
echo "sdist_artifact_name=${sdist_name}"
echo "package_version=$(echo "${sdist_name}" | sed -En 's/py_ed25519_zebra_bindings-(.+)\.tar\.gz/\1/p')"
} >> "$GITHUB_OUTPUT"

- name: Upload sdist artifact
uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: ${{ steps.build_sdist.outputs.sdist_artifact_name }}
path: dist/${{ steps.build_sdist.outputs.sdist_artifact_name }}
if-no-files-found: error

build_wheels:
needs: [setup, python_sdist]
name: Build py-ed25519-zebra-bindings ${{ inputs.version || '1.3.0' }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 60
strategy:
fail-fast: false
matrix:
# py-ed25519-zebra-bindings' pyo3 dependency has a plain
# `features = ["extension-module"]` (no abi3-pyNN), so every interpreter
# needs its own build.
python: ["cp312", "cp313", "cp314", "cp314t"]

steps:
- name: Fetch sdist artifact
id: fetch_sdist
uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: ${{ needs.python_sdist.outputs.sdist_artifact_name }}

- name: Install uv
uses: astral-sh/setup-uv@20cfd1bf945f4377ade1205e4dbc17946fc9a30d # v10.0.1
with:
python-version: '3.12'
activate-environment: true
enable-cache: false

- name: Build and test wheel
env:
CIBW_ARCHS: riscv64
# musllinux can't build: rustup.rs ships no riscv64 musl toolchain.
CIBW_BUILD: ${{ matrix.python }}-manylinux_riscv64
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# py-ed25519-zebra-bindings ships no [tool.cibuildwheel], so the Rust
# toolchain its maturin backend needs is installed in-container here.
CIBW_BEFORE_ALL_LINUX: >-
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs | sh -s -- -y
CIBW_ENVIRONMENT_LINUX: 'PATH="$PATH:$HOME/.cargo/bin"'
# No python-source in pyproject.toml, so maturin ships an auto-generated
# ed25519_zebra/__init__.py shim around the compiled
# ed25519_zebra/ed25519_zebra.*.so submodule - probe that submodule, not
# the top-level import. Test vectors are upstream's own tests.py
# fixed-key cases, avoiding a dependency on the separately-ported
# py-bip39-bindings.
CIBW_TEST_COMMAND: >-
python -c "import ed25519_zebra.ed25519_zebra as m, importlib.metadata as md;
assert m.__file__.endswith('.so'), m.__file__;
assert any(str(p).endswith('licenses/LICENSE') for p in md.files('py_ed25519_zebra_bindings'));
public_key = bytes.fromhex('fd8c9f401e9b5db50685be590b4f8e5a10d4abfef9a532ee903f125fd11f129c');
signature = bytes.fromhex('33185bf9e8f6b354751ef7c7a7c692b3d51d5096e52c4c08e8b307f804d025f3f14a060b8c1ad11f92fe0be4cae2c620d86d6012f90d071fb8dbadac2774df02');
assert m.ed_verify(signature, b'test', public_key);
assert not m.ed_verify(signature, b'tampered', public_key);
private_key = bytes(32);
expected_public = bytes.fromhex('3b6a27bcceb6a42d62a3a8d02a6f0d73653215771de243a63ac048a18b59da29');
assert m.ed_public_from_secret(private_key) == expected_public;
print('ed25519_zebra OK')"
run: |
set -euo pipefail
mkdir py-ed25519-zebra-bindings
tar zxf "${{ steps.fetch_sdist.outputs.download-path }}/${{ needs.python_sdist.outputs.sdist_artifact_name }}" \
--strip-components=1 -C py-ed25519-zebra-bindings
uv pip install --upgrade cibuildwheel
python -m cibuildwheel --output-dir wheelhouse ./py-ed25519-zebra-bindings

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: py-ed25519-zebra-bindings-${{ env.PY_ED25519_ZEBRA_BINDINGS_VERSION }}-${{ matrix.python }}-manylinux_riscv64
path: ./wheelhouse/*.whl
if-no-files-found: error

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