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
190 changes: 190 additions & 0 deletions .github/workflows/build-hidapi.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,190 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# Based on the `build_wheels` job of
# https://github.com/trezor/cython-hidapi/blob/0.15.0/.github/workflows/wheels.yml
name: Build hidapi wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'hidapi version to build (git tag, e.g. 0.15.0)'
required: true
default: '0.15.0'
pull_request:
paths:
- '.github/workflows/build-hidapi.yml'

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

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

env:
HIDAPI_VERSION: ${{ inputs.version || '0.15.0' }}
MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64

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

build_wheels:
needs: [setup]
name: Build hidapi ${{ inputs.version || '0.15.0' }} ${{ matrix.python }}-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
strategy:
fail-fast: false
matrix:
python: ["cp312", "cp313", "cp314", "cp314t"]

steps:
- name: Checkout cython-hidapi ${{ env.HIDAPI_VERSION }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: trezor/cython-hidapi
ref: ${{ env.HIDAPI_VERSION }}
submodules: true
persist-credentials: false

- name: Stage the licence-collection script
run: |
cat > collect-licenses.sh <<'COLLECT_EOF'
#!/bin/bash
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
#
# Stage, at the project root, the licence of every shared library
# auditwheel vendors out of the build image alongside libusb/libudev.
# setuptools' default LICENSE* glob copies them into the wheel.
set -euo pipefail

project="${1:?usage: collect-licenses.sh <project-dir>}"

# ldd is transitive, so the two linked libs alone cover their whole
# closure; ldd does not list the root itself, so resolve those too.
mapfile -t libs < <(
{
ldd /usr/lib64/libusb-1.0.so | tr ' ' '\n' | grep '^/'
readlink -f /usr/lib64/libusb-1.0.so
ldd /usr/lib64/libudev.so | tr ' ' '\n' | grep '^/'
readlink -f /usr/lib64/libudev.so
} | sort -u
)

# `rpm -qf` reports unowned files on stdout, so keep only bare package names.
# glibc and the gcc runtime are on auditwheel's manylinux allowlist and
# are never vendored into the wheel.
mapfile -t pkgs < <(
rpm -qf --qf '%{NAME}\n' "${libs[@]}" 2>/dev/null |
grep -E '^[A-Za-z0-9._+-]+$' | sort -u |
grep -vE '^(glibc|libgcc|libstdc\+\+|gcc)$'
)

for pkg in "${pkgs[@]}"; do
mapfile -t files < <(rpm -q --licensefiles "$pkg" 2>/dev/null || true)

# Some subpackages leave the licence to a sibling of the same source RPM.
if [ -z "${files[0]:-}" ]; then
srpm=$(rpm -q --qf '%{SOURCERPM}\n' "$pkg")
mapfile -t files < <(
rpm -qa --qf '%{SOURCERPM} %{NAME}\n' |
awk -v s="$srpm" '$1 == s { print $2 }' |
xargs -r rpm -q --licensefiles 2>/dev/null | sort -u
)
fi

# Others mark it %doc rather than %license, and the image installs no docs.
if [ -z "${files[0]:-}" ]; then
dnf -y --disablerepo=extras reinstall --setopt=tsflags= "$pkg" >/dev/null
mapfile -t files < <(rpm -qd "$pkg" | grep -iE '/(LICEN[CS]E|COPYING|NOTICE)')
fi

for f in "${files[@]}"; do
[ -f "$f" ] || continue
cp "$f" "$project/LICENSE.${pkg}.$(basename "$f")"
done
compgen -G "$project/LICENSE.$pkg.*" >/dev/null ||
{ echo "no licence file found for $pkg" >&2; exit 1; }
done

ls -1 "$project"/LICENSE.* | sed "s|$project/||"
COLLECT_EOF

- name: Build wheels
uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0
with:
output-dir: wheelhouse/
only: ${{ matrix.python }}-manylinux_riscv64
env:
CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }}
# libudev-devel doesn't exist on Rocky 10; systemd-devel provides libudev.so/libudev.h instead (CLAUDE.md gotcha 252)
CIBW_BEFORE_ALL_LINUX: >-
dnf install -y libusb1-devel systemd-devel &&
bash {project}/collect-licenses.sh {project}
# mirrors upstream's tests.py, plus hidraw since upstream's own suite never exercises that backend
CIBW_TEST_COMMAND: >-
python {project}/tests.py &&
python -c "import hidraw; assert len(hidraw.enumerate()) >= 0"

- name: Verify the wheel ships both compiled extensions and licences
run: |
python3 - wheelhouse/*.whl <<'EOF'
import sys, zipfile
names = zipfile.ZipFile(sys.argv[1]).namelist()
sos = sorted(n for n in names if n.endswith(".so"))
print("\n".join(sos))
assert any("/hid." in "/" + n for n in sos), sos
assert any("/hidraw." in "/" + n for n in sos), sos

lic = sorted(n.split("/")[-1] for n in names if ".dist-info/licenses/" in n and not n.endswith("/"))
print("\n".join(lic))
expected_pkgs = {"libcap", "libusb1", "systemd-libs"}
have_pkgs = {f.split(".", 2)[1] for f in lic if f.startswith("LICENSE.")}
assert expected_pkgs <= have_pkgs, expected_pkgs - have_pkgs
EOF

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

gpl_sources:
needs: [setup]
name: Collect GPL sources for hidapi ${{ inputs.version || '0.15.0' }}
runs-on: ubuntu-24.04-riscv

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

# libcap/libusb1/systemd-libs are the copyleft (GPL/LGPL) libraries auditwheel vendors
# out of the build image alongside libusb/libudev.
- uses: ./actions/collect-gpl-sources
with:
image: ${{ env.MANYLINUX_RISCV64_IMAGE }}
packages: gcc libcap libusb1 systemd-libs
output: gpl-sources.tar

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: hidapi-${{ env.HIDAPI_VERSION }}-gpl-sources
path: gpl-sources.tar
if-no-files-found: error

publish:
name: Publish hidapi ${{ inputs.version || '0.15.0' }}
needs: [setup, build_wheels, gpl_sources]
permissions:
contents: write
pull-requests: write
uses: $/.github/workflows/_publish-wheel.yml
with:
artifact-pattern: hidapi-${{ inputs.version || '0.15.0' }}-*-manylinux_riscv64
gpl-sources-artifact: hidapi-${{ inputs.version || '0.15.0' }}-gpl-sources
gpl-sources-description: gcc and the copyleft libraries bundled in the wheel