Skip to content
Draft
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
193 changes: 193 additions & 0 deletions .github/workflows/build-anki.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,193 @@
# SPDX-FileCopyrightText: 2026 The RISE Project
# SPDX-License-Identifier: MIT
---
# This workflow is based on: https://github.com/ankitects/anki/blob/26.08.1/.github/workflows/release.yml
name: Build anki wheels (riscv64)

on:
workflow_dispatch:
inputs:
version:
description: 'Version glob to (re)build; empty builds every version of docs/packages/anki.yaml not released yet'
required: false
default: ''
pull_request:
branches: [main]
paths:
- '.github/workflows/build-anki.yml'
- 'docs/packages/anki.yaml'
- 'patches/anki/**'
push:
branches: [main]
paths:
- '.github/workflows/build-anki.yml'
- 'docs/packages/anki.yaml'
- 'patches/anki/**'

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

permissions:
contents: read

jobs:
setup:
uses: $/.github/workflows/_setup.yml
with:
package: anki
version: ${{ inputs.version }}

build_wheel:
needs: [setup]
if: needs.setup.outputs.versions != '[]'
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
name: Build anki ${{ matrix.version }} cp310-abi3-manylinux_riscv64
runs-on: ubuntu-24.04-riscv
timeout-minutes: 360
container:
image: quay.io/pypa/manylinux_2_39_riscv64
defaults:
run:
shell: bash
env:
ANKI_VERSION: ${{ matrix.version }}
RELEASE: 2

steps:
- name: Resolve the anki tag
id: tag
run: |
IFS=. read -r year month patch <<< "$ANKI_VERSION"
echo "tag=${year}.$(printf %02d "$month")${patch:+.$patch}" >> "$GITHUB_OUTPUT"

- name: Checkout anki ${{ steps.tag.outputs.tag }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ankitects/anki
ref: ${{ steps.tag.outputs.tag }}
submodules: true
persist-credentials: false

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

- name: Patch anki source
run: git apply python-wheels/patches/anki/${{ env.ANKI_VERSION }}/00*.patch

- name: Setup build environment
run: |
git config --global --add safe.directory '*'
dnf -y install protobuf-compiler
curl --proto '=https' --tlsv1.2 -sSf https://sh.rustup.rs \
| sh -s -- -y --default-toolchain none --profile minimal
echo "$HOME/.cargo/bin" >> "$GITHUB_PATH"
"$HOME/.cargo/bin/rustup" toolchain install
PATH="$HOME/.cargo/bin:$PATH" tools/install-n2

# Upstream's pyenv is a `uv sync --extra qt` of the whole workspace, which
# needs PyQt6/WebEngine; OFFLINE_BUILD is upstream's documented way to
# supply the venv instead, and pylib only needs protoc-gen-mypy from it.
- name: Create the Python environment
run: |
/opt/python/cp313-cp313/bin/python -m venv out/pyenv
out/pyenv/bin/pip install mypy-protobuf==3.6.0 uv==0.11.8

# pylib/ has no LICENSE of its own, so hatchling's licence glob finds nothing
# and upstream's wheels ship none.
- name: Stage licence
run: cp LICENSE pylib/

- name: Build wheel
env:
OFFLINE_BUILD: '1'
PROTOC_BINARY: /usr/bin/protoc
UV_BINARY: ${{ github.workspace }}/out/pyenv/bin/uv
UV_PYTHON: ${{ github.workspace }}/out/pyenv/bin/python
run: ./ninja wheels:anki

- uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1
with:
name: anki-${{ env.ANKI_VERSION }}-cp310-abi3-manylinux_riscv64
path: out/wheels/anki-*.whl
if-no-files-found: error

test_wheel:
needs: [setup, build_wheel]
if: needs.setup.outputs.versions != '[]'
strategy:
fail-fast: false
matrix:
version: ${{ fromJSON(needs.setup.outputs.versions) }}
python: ["cp312", "cp313", "cp314"]
name: Test anki ${{ matrix.version }} ${{ matrix.python }}
runs-on: ubuntu-24.04-riscv
timeout-minutes: 60
container:
image: quay.io/pypa/manylinux_2_39_riscv64
defaults:
run:
shell: bash
env:
ANKI_VERSION: ${{ matrix.version }}

steps:
- name: Resolve the anki tag
id: tag
run: |
IFS=. read -r year month patch <<< "$ANKI_VERSION"
echo "tag=${year}.$(printf %02d "$month")${patch:+.$patch}" >> "$GITHUB_OUTPUT"

- name: Checkout anki ${{ steps.tag.outputs.tag }}
uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1
with:
repository: ankitects/anki
ref: ${{ steps.tag.outputs.tag }}
persist-credentials: false

- uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v8.0.1
with:
name: anki-${{ env.ANKI_VERSION }}-cp310-abi3-manylinux_riscv64
path: dist

- name: Install wheel
env:
PIP_EXTRA_INDEX_URL: https://pypi.riseproject.dev/simple/
PIP_ONLY_BINARY: orjson,protobuf
run: |
/opt/python/${{ matrix.python }}-${{ matrix.python }}/bin/python -m venv /venv
/venv/bin/pip install dist/anki-*.whl pytest==8.4.1
/venv/bin/python -c "import anki._rsbridge as m; assert m.__file__.endswith('.so'), m.__file__"

# Upstream's check:pytest:pylib. Staged out of the checkout so pylib/anki does
# not shadow the wheel; test_create_open expects '/' to be unwritable, and the
# container runs as root.
- name: Run pylib tests
run: |
mkdir /stage
cp -a pylib/tests /stage/
cd /stage
ANKI_TEST_MODE=1 /venv/bin/python -m pytest -p no:cacheprovider tests \
--deselect tests/test_collection.py::test_create_open

publish:
name: Publish anki ${{ matrix.version }}
needs: [setup, build_wheel, test_wheel]
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: anki-${{ matrix.version }}-*-manylinux_riscv64
5 changes: 5 additions & 0 deletions docs/packages/anki.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package-name: anki
source-code: https://github.com/ankitects/anki
license: AGPL-3.0-or-later
versions:
- version: 26.8.1
115 changes: 115 additions & 0 deletions patches/anki/26.8.1/0001-build-add-riscv64-linux-platform.patch
Original file line number Diff line number Diff line change
@@ -0,0 +1,115 @@
Upstream-Status: To upstream [not yet submitted]

Add a riscv64 Linux platform to the build system

Platform::current() panics on any arch other than x86_64/aarch64, so
configure cannot run on riscv64 at all. Add a LinuxRiscv variant: rustls
like the other Linux targets, the riscv64gc Rust triple, uv's riscv64gc
release archive, and a manylinux_2_39_riscv64 wheel tag. protoc and node
publish no riscv64 binary, so protoc requires PROTOC_BINARY, and node
reuses the x86_64 entry (only reached without NODE_BINARY), the same way
LinuxArm already reuses the x86_64 clang-format binary.

diff --git a/build/configure/src/python.rs b/build/configure/src/python.rs
index 947582f..a8e4fe8 100644
--- a/build/configure/src/python.rs
+++ b/build/configure/src/python.rs
@@ -155,6 +155,7 @@ impl BuildAction for BuildWheel {
let platform_tag = match platform {
Platform::LinuxX64 => "manylinux_2_35_x86_64",
Platform::LinuxArm => "manylinux_2_35_aarch64",
+ Platform::LinuxRiscv => "manylinux_2_39_riscv64",
Platform::MacX64 => "macosx_12_0_x86_64",
Platform::MacArm => "macosx_12_0_arm64",
Platform::WindowsX64 => "win_amd64",
diff --git a/build/ninja_gen/src/archives.rs b/build/ninja_gen/src/archives.rs
index 3d2120b..ca6852d 100644
--- a/build/ninja_gen/src/archives.rs
+++ b/build/ninja_gen/src/archives.rs
@@ -23,6 +23,7 @@ pub struct OnlineArchive {
pub enum Platform {
LinuxX64,
LinuxArm,
+ LinuxRiscv,
MacX64,
MacArm,
WindowsX64,
@@ -36,6 +37,7 @@ impl Platform {
match (os, arch) {
("linux", "x86_64") => Self::LinuxX64,
("linux", "aarch64") => Self::LinuxArm,
+ ("linux", "riscv64") => Self::LinuxRiscv,
("macos", "x86_64") => Self::MacX64,
("macos", "aarch64") => Self::MacArm,
("windows", "x86_64") => Self::WindowsX64,
@@ -49,7 +51,7 @@ impl Platform {
// On Linux, wheels are not allowed to link to OpenSSL, and linking setup
// caused pain for AnkiDroid in the past. On other platforms, we stick to
// native libraries, for smaller binaries.
- Platform::LinuxX64 | Platform::LinuxArm => "rustls",
+ Platform::LinuxX64 | Platform::LinuxArm | Platform::LinuxRiscv => "rustls",
_ => "native-tls",
}
}
@@ -58,6 +60,7 @@ impl Platform {
match self {
Platform::LinuxX64 => "x86_64-unknown-linux-gnu",
Platform::LinuxArm => "aarch64-unknown-linux-gnu",
+ Platform::LinuxRiscv => "riscv64gc-unknown-linux-gnu",
Platform::MacX64 => "x86_64-apple-darwin",
Platform::MacArm => "aarch64-apple-darwin",
Platform::WindowsX64 => "x86_64-pc-windows-msvc",
diff --git a/build/ninja_gen/src/node.rs b/build/ninja_gen/src/node.rs
index b6731e2..608f29b 100644
--- a/build/ninja_gen/src/node.rs
+++ b/build/ninja_gen/src/node.rs
@@ -18,7 +18,8 @@ use crate::input::BuildInput;

pub fn node_archive(platform: Platform) -> OnlineArchive {
match platform {
- Platform::LinuxX64 => OnlineArchive {
+ // todo: nodejs.org publishes no riscv64 build; set NODE_BINARY
+ Platform::LinuxX64 | Platform::LinuxRiscv => OnlineArchive {
url: "https://nodejs.org/dist/v22.17.0/node-v22.17.0-linux-x64.tar.xz",
sha256: "325c0f1261e0c61bcae369a1274028e9cfb7ab7949c05512c5b1e630f7e80e12",
},
diff --git a/build/ninja_gen/src/protobuf.rs b/build/ninja_gen/src/protobuf.rs
index 1198f65..eaba26b 100644
--- a/build/ninja_gen/src/protobuf.rs
+++ b/build/ninja_gen/src/protobuf.rs
@@ -31,6 +31,9 @@ pub fn protoc_archive(platform: Platform) -> OnlineArchive {
sha256: "6c554de11cea04c56ebf8e45b54434019b1cd85223d4bbd25c282425e306ecc2",
}
},
+ Platform::LinuxRiscv => {
+ panic!("protoc publishes no riscv64 binary; set PROTOC_BINARY")
+ }
Platform::MacX64 | Platform::MacArm => {
OnlineArchive {
url: "https://github.com/protocolbuffers/protobuf/releases/download/v31.1/protoc-31.1-osx-universal_binary.zip",
@@ -54,7 +57,7 @@ fn clang_format_archive(platform: Platform) -> OnlineArchive {
sha256: "64060bc4dbca30d0d96aab9344e2783008b16e1cae019a2532f1126ca5ec5449",
}
}
- Platform::LinuxArm => {
+ Platform::LinuxArm | Platform::LinuxRiscv => {
// todo: replace with arm64 binary
OnlineArchive {
url: "https://github.com/ankitects/clang-format-binaries/releases/download/anki-2021-01-09/clang-format_linux_x86_64.zip",
diff --git a/build/ninja_gen/src/python.rs b/build/ninja_gen/src/python.rs
index 16c61f7..755ca13 100644
--- a/build/ninja_gen/src/python.rs
+++ b/build/ninja_gen/src/python.rs
@@ -37,6 +37,12 @@ pub fn uv_archive(platform: Platform) -> OnlineArchive {
sha256: "eee8dd658d20e5ac85fec9c2326b6cbc9d83a1eef09ef07433e58698ac849591",
}
},
+ Platform::LinuxRiscv => {
+ OnlineArchive {
+ url: "https://github.com/astral-sh/uv/releases/download/0.11.8/uv-riscv64gc-unknown-linux-gnu.tar.gz",
+ sha256: "dd43289c567fda3ca59ec714ffca09125f1149289448667f36a4bb7c29c859be",
+ }
+ },
Platform::MacX64 => {
OnlineArchive {
url: "https://github.com/astral-sh/uv/releases/download/0.11.8/uv-x86_64-apple-darwin.tar.gz",
Loading