From 6ee02c21a8a12793a1df8d937e3c352c112f2981 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Tue, 13 Oct 2026 01:16:01 +0000 Subject: [PATCH 1/3] anki: Add version 26.8.1 --- .github/workflows/build-anki.yml | 193 ++++++++++++++++++ docs/packages/anki.yaml | 5 + ...001-build-add-riscv64-linux-platform.patch | 115 +++++++++++ 3 files changed, 313 insertions(+) create mode 100644 .github/workflows/build-anki.yml create mode 100644 docs/packages/anki.yaml create mode 100644 patches/anki/26.8.1/0001-build-add-riscv64-linux-platform.patch diff --git a/.github/workflows/build-anki.yml b/.github/workflows/build-anki.yml new file mode 100644 index 000000000000..f6c38b7a1239 --- /dev/null +++ b/.github/workflows/build-anki.yml @@ -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 diff --git a/docs/packages/anki.yaml b/docs/packages/anki.yaml new file mode 100644 index 000000000000..3a40991293e0 --- /dev/null +++ b/docs/packages/anki.yaml @@ -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 diff --git a/patches/anki/26.8.1/0001-build-add-riscv64-linux-platform.patch b/patches/anki/26.8.1/0001-build-add-riscv64-linux-platform.patch new file mode 100644 index 000000000000..9fcda6ec4a30 --- /dev/null +++ b/patches/anki/26.8.1/0001-build-add-riscv64-linux-platform.patch @@ -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", From 2010ac2c35fb49768c02ac722b3bd77526e34b34 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Thu, 22 Oct 2026 10:38:05 +0000 Subject: [PATCH 2/3] anki: install ruff into the offline pyenv pylib/tools/genhooks.py runs 'python -m ruff format' on hooks_gen.py, so the hand-built OFFLINE_BUILD venv needs ruff alongside mypy-protobuf. Pin to upstream's uv.lock version (0.13.3, which ships a riscv64 wheel). --- .github/workflows/build-anki.yml | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/.github/workflows/build-anki.yml b/.github/workflows/build-anki.yml index f6c38b7a1239..5ef4da3742b3 100644 --- a/.github/workflows/build-anki.yml +++ b/.github/workflows/build-anki.yml @@ -92,11 +92,12 @@ jobs: # 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. + # supply the venv instead, and pylib only needs protoc-gen-mypy and ruff + # (genhooks.py formats hooks_gen.py) 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 + out/pyenv/bin/pip install mypy-protobuf==3.6.0 ruff==0.13.3 uv==0.11.8 # pylib/ has no LICENSE of its own, so hatchling's licence glob finds nothing # and upstream's wheels ship none. From 4763a9ef677c620a9eb9324992f20e897a2edbd7 Mon Sep 17 00:00:00 2001 From: Ludovic Henry Date: Fri, 23 Oct 2026 15:32:46 +0000 Subject: [PATCH 3/3] anki: pin hatchling below 1.32.1 for the wheel build hatchling 1.32.1 started writing the version string as written in .version (26.08.1) into the wheel METADATA instead of the normalized one (26.8.1). The publish step reads the version from METADATA and could not find 26.08.1 in docs/packages/anki.yaml. Upstream's 26.8.1 wheels were built with hatchling 1.31.0 and record 26.8.1. --- .github/workflows/build-anki.yml | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/.github/workflows/build-anki.yml b/.github/workflows/build-anki.yml index 5ef4da3742b3..bd62ff6e3c42 100644 --- a/.github/workflows/build-anki.yml +++ b/.github/workflows/build-anki.yml @@ -104,13 +104,18 @@ jobs: - name: Stage licence run: cp LICENSE pylib/ + # hatchling 1.32.1+ writes `.version` unnormalized (26.08.1) into METADATA; + # upstream's wheels were built with an earlier one and record 26.8.1. - name: Build wheel env: OFFLINE_BUILD: '1' PROTOC_BINARY: /usr/bin/protoc UV_BINARY: ${{ github.workspace }}/out/pyenv/bin/uv + UV_BUILD_CONSTRAINT: ${{ github.workspace }}/out/build-constraints.txt UV_PYTHON: ${{ github.workspace }}/out/pyenv/bin/python - run: ./ninja wheels:anki + run: | + echo 'hatchling<1.32.1' > "$UV_BUILD_CONSTRAINT" + ./ninja wheels:anki - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 with: