diff --git a/.github/workflows/build-pyvrl.yml b/.github/workflows/build-pyvrl.yml new file mode 100644 index 00000000000..5cbcb404720 --- /dev/null +++ b/.github/workflows/build-pyvrl.yml @@ -0,0 +1,151 @@ +# SPDX-FileCopyrightText: 2026 The RISE Project +# SPDX-License-Identifier: MIT +--- +# This workflow is based on: https://github.com/crowdalert/pyvrl/blob/v0.0.2/.github/workflows/ci.yaml +name: Build pyvrl wheels (riscv64) + +on: + workflow_dispatch: + inputs: + version: + description: 'Version glob to (re)build; empty builds every version of docs/packages/pyvrl.yaml not released yet' + required: false + default: '' + pull_request: + branches: [main] + paths: + - '.github/workflows/build-pyvrl.yml' + - 'docs/packages/pyvrl.yaml' + push: + branches: [main] + paths: + - '.github/workflows/build-pyvrl.yml' + - 'docs/packages/pyvrl.yaml' + +concurrency: + group: ${{ github.workflow }}-${{ github.head_ref || github.run_id }} + cancel-in-progress: true + +permissions: + contents: read # to fetch code (actions/checkout) + +env: + MANYLINUX_RISCV64_IMAGE: quay.io/pypa/manylinux_2_39_riscv64 + +jobs: + setup: + uses: $/.github/workflows/_setup.yml + with: + package: pyvrl + version: ${{ inputs.version }} + + build_wheels: + needs: [setup] + if: needs.setup.outputs.versions != '[]' + name: Build pyvrl ${{ matrix.version }} ${{ matrix.python }}-manylinux_riscv64 + runs-on: ubuntu-24.04-riscv + timeout-minutes: 1440 + strategy: + fail-fast: false + matrix: + version: ${{ fromJSON(needs.setup.outputs.versions) }} + # cp312 is pyo3 0.20's ceiling, which is also where upstream's own + # wheels stop. PYO3_USE_ABI3_FORWARD_COMPATIBILITY would lift it, but + # it turns on Py_LIMITED_API, and pyo3 0.20 cfg-removes the chrono + # conversions this crate needs under it. + python: ["cp312"] + + env: + PYVRL_VERSION: ${{ matrix.version }} + + steps: + - name: Checkout pyvrl ${{ env.PYVRL_VERSION }} + uses: actions/checkout@3d3c42e5aac5ba805825da76410c181273ba90b1 # v7.0.1 + with: + repository: crowdalert/pyvrl + ref: v${{ env.PYVRL_VERSION }} + persist-credentials: false + + - name: Build wheels + uses: pypa/cibuildwheel@1828c10ab37f080699c7b81cea34097c684a7074 # v4.2.0 + with: + output-dir: wheelhouse/ + # musllinux is dropped: rustup.rs ships no riscv64 musl toolchain. + only: ${{ matrix.python }}-manylinux_riscv64 + env: + CIBW_MANYLINUX_RISCV64_IMAGE: ${{ env.MANYLINUX_RISCV64_IMAGE }} + # pyvrl 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" + CIBW_TEST_COMMAND: | + python3 - <<'PYEOF' + import datetime + from pyvrl import Transform + program = ''' + .y = "bar" + .x = upcase!(.x) + .n = to_int!(.n) + 1 + .list = push!(.list, "z") + . + ''' + transform = Transform(program) + assert transform.source == program + out = transform.remap({"x": "foo", "n": 41, "list": ["a"]}) + assert out == {"x": "FOO", "y": "bar", "n": 42, "list": ["a", "z"]}, out + unicode = Transform('.s = upcase!(.s)\n.') + assert unicode.remap({"s": "caf\N{LATIN SMALL LETTER E WITH ACUTE} \N{EARTH GLOBE AMERICAS}"}) == {"s": "CAF\N{LATIN CAPITAL LETTER E WITH ACUTE} \N{EARTH GLOBE AMERICAS}"} + parsed = Transform('. = parse_json!(.raw)').remap({"raw": '{"a": [1, 2.5, true, null]}'}) + assert parsed == {"a": [1, 2.5, True, None]}, parsed + stamp = Transform('. = {"t": parse_timestamp!("2024-05-06T07:08:09Z", "%+")}').remap({}) + assert stamp == {"t": datetime.datetime(2024, 5, 6, 7, 8, 9, tzinfo=datetime.timezone.utc)}, stamp + try: + Transform(".x = no_such_function!(.x)") + except ValueError: + pass + else: + raise AssertionError("expected a compile error") + try: + Transform("abort").remap({}) + except ValueError: + pass + else: + raise AssertionError("expected a runtime error") + print("pyvrl OK") + PYEOF + + - name: Check the extension made it into the wheel + run: | + python3 - wheelhouse/*.whl <<'EOF' + import sys, zipfile + for whl in sys.argv[1:]: + names = zipfile.ZipFile(whl).namelist() + assert any(n.startswith("pyvrl/pyvrl.") and n.endswith(".so") for n in names), names + assert any("licen" in n and n.endswith("LICENSE") for n in names), names + print(whl, "ok") + EOF + + - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 + with: + name: pyvrl-${{ env.PYVRL_VERSION }}-${{ matrix.python }}-manylinux_riscv64 + path: wheelhouse/*.whl + if-no-files-found: error + + publish: + name: Publish pyvrl ${{ matrix.version }} + needs: [setup, build_wheels] + 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: pyvrl-${{ matrix.version }}-*-manylinux_riscv64 diff --git a/docs/packages/pyvrl.yaml b/docs/packages/pyvrl.yaml new file mode 100644 index 00000000000..50f87de5d05 --- /dev/null +++ b/docs/packages/pyvrl.yaml @@ -0,0 +1,5 @@ +package-name: pyVRL +source-code: https://github.com/crowdalert/pyvrl +license: MIT +versions: +- version: 0.0.2