Skip to content
Merged
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
70 changes: 40 additions & 30 deletions .github/workflows/build-standalone-env.yml
Original file line number Diff line number Diff line change
Expand Up @@ -213,6 +213,39 @@ jobs:
echo "=== requirements_final.txt ==="
cat requirements_final.txt

# The OpenCV wheel is prepared inside this leg so the other platforms
# never wait for it. build-win-arm64-wheels.yml caches a smoke-tested
# wheel on main under this key; scripts/test_local_tooling.py keeps the
# two workflows' key and path identical.
- name: Restore smoke-tested OpenCV wheel
id: opencv-cache
if: matrix.python_platform == 'aarch64-pc-windows-msvc'
uses: actions/cache/restore@v6
with:
path: build/opencv/wheels
key: opencv-python-win-arm64-${{ hashFiles('scripts/build_opencv_arm64.py') }}

# No entry yet for this recipe, or it was evicted: build the same pinned
# sources here rather than fail the release. The stripped-environment
# smoke test below checks the wheel either way.
- name: Set up Python for the OpenCV build
id: opencv-python
if: matrix.python_platform == 'aarch64-pc-windows-msvc' && steps.opencv-cache.outputs.cache-hit != 'true'
uses: actions/setup-python@v6
with:
python-version: "3.13"
architecture: arm64
# Leave PATH alone so later steps keep using the runner's Python.
update-environment: false

- name: Build OpenCV from pinned public sources
if: matrix.python_platform == 'aarch64-pc-windows-msvc' && steps.opencv-cache.outputs.cache-hit != 'true'
shell: pwsh
env:
BUILD_PYTHON: ${{ steps.opencv-python.outputs.python-path }}
run: |
& $env:BUILD_PYTHON scripts/build_opencv_arm64.py --work-dir build/opencv

- name: Install dependencies into standalone Python
shell: bash
run: |
Expand All @@ -226,30 +259,15 @@ jobs:
"$PYTHON" -m ensurepip --upgrade 2>/dev/null || true
"$PYTHON" -m pip install --upgrade pip

# ComfyUI requires these native Rust extensions, but neither package
# publishes a Windows ARM64 wheel on PyPI yet. Install the verified
# wheels from win-arm64-wheels-v1 first so the later unpinned
# requirements resolve to these already-installed versions.
# Resolve the OpenCV wheel from the steps above with the full
# requirements so NumPy/torch constraints are respected, and never
# compile the Rust extensions from source.
if [[ "${{ runner.os }}" == "Windows" && "${{ runner.arch }}" == "ARM64" ]]; then
WHEEL_RELEASE="https://github.com/Comfy-Org/ComfyUI-Standalone-Environments/releases/download/win-arm64-wheels-v1"
mkdir win-arm64-wheels
# actions/checkout may convert text files to CRLF on Windows.
# Normalize once so the carriage return cannot enter a URL or
# checksum filename.
tr -d '\r' < win-arm64-wheels.sha256 > win-arm64-wheels/checksums.sha256
while read -r _ filename; do
curl -fL --retry 3 \
-o "win-arm64-wheels/$filename" \
"$WHEEL_RELEASE/$filename"
done < win-arm64-wheels/checksums.sha256
(cd win-arm64-wheels && sha256sum -c checksums.sha256)
"$PYTHON" -m pip install --no-cache-dir \
win-arm64-wheels/blake3-1.0.9-cp313-cp313-win_arm64.whl \
win-arm64-wheels/kornia_rs-0.1.14-cp313-cp313-win_arm64.whl
"$PYTHON" -m pip install --no-cache-dir -r requirements_final.txt pygit2 build/opencv/wheels/*.whl
else
"$PYTHON" -m pip install --no-cache-dir -r requirements_final.txt pygit2
fi

"$PYTHON" -m pip install --no-cache-dir -r requirements_final.txt pygit2

- name: Bundle uv
shell: bash
run: |
Expand Down Expand Up @@ -356,15 +374,7 @@ jobs:
"

if [[ "${{ runner.os }}" == "Windows" && "${{ runner.arch }}" == "ARM64" ]]; then
echo "=== Verifying hosted Windows ARM64 wheels ==="
"$PYTHON" -c "
import importlib.metadata
import blake3
import kornia_rs
assert blake3.__version__ == '1.0.9', blake3.__version__
assert importlib.metadata.version('kornia-rs') == '0.1.14'
print('blake3 and kornia-rs ARM64 imports OK')
"
"$PYTHON" scripts/smoke_win_arm64.py
fi

- name: Generate build manifest
Expand Down
205 changes: 45 additions & 160 deletions .github/workflows/build-win-arm64-wheels.yml
Original file line number Diff line number Diff line change
@@ -1,186 +1,71 @@
name: Build win-arm64 wheels
name: Build Windows ARM64 OpenCV

# Native Windows ARM64 wheels for the two ComfyUI dependencies that publish
# none on PyPI yet. Built from the upstream release tags on GitHub's
# windows-11-arm runner and attached to a GitHub release, so the
# win-nvidia-arm64 bundle can pip-install them by URL before
# `-r ComfyUI/requirements.txt`.
# Build public sources; NVIDIA's evaluation archive is not redistributable.
# Rust dependencies now have native PyPI wheels. Historical v1 assets remain
# available for older environment definitions that still reference them.
#
# Drop each package once upstream ships win_arm64 wheels on PyPI:
# blake3 - open PR: https://github.com/oconnor663/blake3-py/pull/112
# kornia-rs - release CI merged in kornia/kornia-rs#1093; lands with the
# first release after v0.1.15-rc.5
#
# Pull requests that touch this file build the wheels and upload them as
# artifacts only. The release is created on workflow_dispatch.

# A wheel that passes the smoke test is cached under a key derived from the
# build script. Environment releases restore that entry from main, so they
# ship the same tested bytes instead of rebuilding OpenCV every time.
on:
workflow_dispatch:
inputs:
tag:
description: Release tag to attach the wheels to
required: true
push:
branches:
- main
paths:
- .github/workflows/build-win-arm64-wheels.yml
- scripts/build_opencv_arm64.py
# GitHub evicts cache entries unused for 7 days, and a restore counts as
# use. Twice a week keeps main's entry alive between releases.
schedule:
- cron: "23 5 * * 1,4"
pull_request:
paths:
- .github/workflows/build-win-arm64-wheels.yml
- .github/workflows/build-standalone-env.yml
- requirements-nvidia-arm64.txt
- scripts/build_opencv_arm64.py
- scripts/smoke_win_arm64.py

permissions:
contents: read

env:
# Must match the standalone bundle's interpreter (cp313 wheels).
PYTHON_VERSION: "3.13"
# Upstream release tags. Bump together with ComfyUI/requirements.txt.
BLAKE3_TAG: "1.0.9"
KORNIA_RS_TAG: "v0.1.14"

jobs:
blake3:
name: blake3 win_arm64
runs-on: windows-11-arm
steps:
- uses: actions/checkout@v7
with:
repository: oconnor663/blake3-py
ref: ${{ env.BLAKE3_TAG }}
persist-credentials: false

- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
architecture: arm64

# Same recipe as upstream's Windows ARM64 job (blake3-py#112).
- uses: PyO3/maturin-action@v1
with:
command: build
target: aarch64
args: --release --out dist --interpreter ${{ env.PYTHON_VERSION }}
rust-toolchain: stable

- name: Verify native ARM64 wheel
shell: pwsh
run: |
$wheel = Get-ChildItem dist\*-win_arm64.whl | Select-Object -First 1
if (-not $wheel) { throw "No win_arm64 wheel was produced" }
Write-Host "Built $($wheel.Name)"
python -m pip install --force-reinstall $wheel.FullName
python -c "import blake3, platform; assert platform.machine().upper() == 'ARM64', platform.machine(); assert blake3.__version__ == '${{ env.BLAKE3_TAG }}', blake3.__version__; assert len(blake3.blake3(b'abc').hexdigest()) == 64; print('blake3', blake3.__version__, 'OK')"

- uses: actions/upload-artifact@v7
with:
name: blake3-win_arm64
path: dist/*-win_arm64.whl
if-no-files-found: error

kornia-rs:
name: kornia-rs win_arm64
opencv:
runs-on: windows-11-arm
env:
CMAKE_GENERATOR: Ninja
timeout-minutes: 60
steps:
- uses: actions/checkout@v7
with:
repository: kornia/kornia-rs
ref: ${{ env.KORNIA_RS_TAG }}
persist-credentials: false

- uses: actions/setup-python@v6
with:
python-version: ${{ env.PYTHON_VERSION }}
python-version: "3.13"
architecture: arm64

# Same recipe as upstream's windows-arm64 release job (kornia-rs#1093).
- uses: dtolnay/rust-toolchain@stable
with:
targets: aarch64-pc-windows-msvc

- uses: ilammy/msvc-dev-cmd@v1
with:
arch: arm64

- name: Install native build tools
run: python -m pip install --upgrade cmake ninja

# v0.1.14's Cargo.lock pins generator 0.8.8 (via loom, kiddo), which has
# no aarch64-windows backend and fails to compile. 0.8.9 (2026-06-01)
# added it and is what upstream main locks. Patch-level bump of one
# transitive crate; the kornia-rs sources stay at the release tag.
- name: Bump generator crate for Windows ARM64 support
run: cargo update -p generator --precise 0.8.9

- uses: PyO3/maturin-action@v1
# Saved in a post step that runs only when every step below succeeds,
# so a restored wheel has always passed the smoke test.
# build-standalone-env.yml restores the same key and path.
- name: Restore smoke-tested OpenCV wheel
id: cache
uses: actions/cache@v6
with:
command: build
maturin-version: latest
args: --release --target aarch64-pc-windows-msvc --out dist -i python${{ env.PYTHON_VERSION }} -m kornia-py/Cargo.toml

- name: Verify native ARM64 wheel
path: build/opencv/wheels
key: opencv-python-win-arm64-${{ hashFiles('scripts/build_opencv_arm64.py') }}
- name: Build OpenCV from pinned public sources
if: steps.cache.outputs.cache-hit != 'true'
run: python scripts/build_opencv_arm64.py --work-dir build/opencv
- name: Install native wheels into a clean test environment
shell: pwsh
run: |
$wheel = Get-ChildItem dist\*-win_arm64.whl | Select-Object -First 1
if (-not $wheel) { throw "No win_arm64 wheel was produced" }
Write-Host "Built $($wheel.Name)"
python -m pip install --force-reinstall $wheel.FullName
python -c "import kornia_rs, platform, importlib.metadata as m; version = m.version('kornia-rs'); assert platform.machine().upper() == 'ARM64', platform.machine(); assert version == '${{ env.KORNIA_RS_TAG }}'.removeprefix('v'), version; print('kornia_rs', version, 'OK')"

python -m venv build/smoke
# The contrib build plus the empty wheels for the other OpenCV names.
$wheels = Get-ChildItem build/opencv/wheels/*.whl
& build/smoke/Scripts/python.exe -m pip install --only-binary=:all: $wheels.FullName blake3==1.0.11 kornia-rs==0.1.15
if ($LASTEXITCODE -ne 0) { throw "Native dependency install failed" }
- name: Verify ARM64 imports, hashing and image round-trip
run: build/smoke/Scripts/python.exe scripts/smoke_win_arm64.py
- uses: actions/upload-artifact@v7
with:
name: kornia-rs-win_arm64
path: dist/*-win_arm64.whl
name: opencv-python-win-arm64
path: build/opencv/wheels/*.whl
if-no-files-found: error

release:
name: Attach wheels to release
if: github.event_name == 'workflow_dispatch'
needs: [blake3, kornia-rs]
runs-on: ubuntu-latest
permissions:
contents: write
steps:
- name: Refuse to overwrite a published release
env:
GH_TOKEN: ${{ github.token }}
RELEASE_TAG: ${{ inputs.tag }}
shell: bash
run: |
set -euo pipefail
release_state="$(
gh api --paginate --slurp "repos/${GITHUB_REPOSITORY}/releases?per_page=100" |
jq -r --arg tag "$RELEASE_TAG" '
[.[][] | select(.tag_name == $tag)] |
if length == 0 then "missing"
elif any(.draft == false) then "published"
else "draft"
end
'
)"
if [[ "$release_state" == "published" ]]; then
echo "::error::Release ${RELEASE_TAG} is already published; use a new tag"
exit 1
fi
echo "Release ${RELEASE_TAG} is ${release_state}; continuing"

- uses: actions/download-artifact@v7
with:
path: wheels
merge-multiple: true

- name: List wheels
run: ls -l wheels

# Draft so the asset names can be checked before the URLs go live;
# never "latest", which belongs to the bundle releases.
- uses: softprops/action-gh-release@v2
with:
tag_name: ${{ inputs.tag }}
name: Windows ARM64 wheels (${{ inputs.tag }})
body: |
Native `win_arm64` wheels for ComfyUI dependencies that publish none on PyPI yet.
Built by `.github/workflows/build-win-arm64-wheels.yml` from upstream release tags:
blake3 `${{ env.BLAKE3_TAG }}`, kornia-rs `${{ env.KORNIA_RS_TAG }}`.
Consumed by the `win-nvidia-arm64` standalone bundle build.
files: wheels/*.whl
draft: true
make_latest: false
fail_on_unmatched_files: true
10 changes: 7 additions & 3 deletions .github/workflows/test-local-tooling.yml
Original file line number Diff line number Diff line change
Expand Up @@ -14,19 +14,23 @@ on:
- scripts/test_local_tooling.py
# Parity tests compare build_local.py constants to the CI matrix.
- .github/workflows/build-standalone-env.yml
- .github/workflows/build-win-arm64-wheels.yml
- .github/workflows/test-local-tooling.yml
# The Windows ARM64 tests read these data files directly.
# The Windows ARM64 tests exercise these dependency inputs.
- requirements-nvidia-arm64.txt
- win-arm64-wheels.sha256
- scripts/build_opencv_arm64.py
- scripts/smoke_win_arm64.py
pull_request:
paths:
- scripts/build_local.py
- scripts/serve_local.py
- scripts/test_local_tooling.py
- .github/workflows/build-standalone-env.yml
- .github/workflows/build-win-arm64-wheels.yml
- .github/workflows/test-local-tooling.yml
- requirements-nvidia-arm64.txt
- win-arm64-wheels.sha256
- scripts/build_opencv_arm64.py
- scripts/smoke_win_arm64.py

permissions:
contents: read
Expand Down
16 changes: 16 additions & 0 deletions requirements-nvidia-arm64.txt
Original file line number Diff line number Diff line change
Expand Up @@ -9,3 +9,19 @@ torchaudio==2.11.0.dev20260902+cu134
# Newer cryptography releases currently omit win_arm64 wheels. This is the
# newest public abi3 wheel compatible with Python 3.13 on Windows ARM64.
cryptography==46.0.3

# Upstream now publishes native CPython 3.13 ARM64 wheels for both packages.
# Keep them binary-only so a missing wheel fails instead of needing Rust/MSVC.
--only-binary=blake3
--only-binary=kornia-rs
blake3==1.0.11
kornia-rs==0.1.15

# OpenCV is built from pinned public sources by scripts/build_opencv_arm64.py
# (no public win_arm64 wheel on PyPI yet). The contrib build provides cv2; the
# other three names are empty wheels that depend on it, so custom nodes that
# require any of them resolve to the same build.
opencv-contrib-python==4.14.0+c9cce3d
opencv-python==4.14.0+c9cce3d
opencv-python-headless==4.14.0+c9cce3d
opencv-contrib-python-headless==4.14.0+c9cce3d
Loading
Loading