diff --git a/.github/workflows/release-flashinfer-jit-cache-wheel.yml b/.github/workflows/release-flashinfer-jit-cache-wheel.yml
index a130358f56..0a5ec2b913 100644
--- a/.github/workflows/release-flashinfer-jit-cache-wheel.yml
+++ b/.github/workflows/release-flashinfer-jit-cache-wheel.yml
@@ -122,3 +122,42 @@ jobs:
name: ${{ steps.artifact-name.outputs.name }}
retention-days: 7
path: flashinfer-jit-cache/dist/*
+
+ release:
+ needs: build-wheel
+ runs-on: [self-hosted, Linux, x86_64]
+ steps:
+ - uses: actions/checkout@v4
+ with:
+ ref: ${{ inputs.tag }}
+
+ - uses: actions/download-artifact@v4
+ with:
+ path: dist/
+ merge-multiple: true
+ pattern: wheel-*
+
+ - run: ls -lah dist/
+
+ - uses: softprops/action-gh-release@v1
+ with:
+ tag_name: ${{ inputs.tag }}
+ files: |
+ dist/flashinfer_jit_cache*.whl
+
+ - name: Clone wheel index
+ run: git clone https://oauth2:${WHL_TOKEN}@github.com/flashinfer-ai/whl.git flashinfer-whl
+ env:
+ WHL_TOKEN: ${{ secrets.WHL_TOKEN }}
+
+ - name: Update wheel index
+ run: python3 scripts/update_flashinfer_jit_cache_whl_index.py
+
+ - name: Push wheel index
+ run: |
+ cd flashinfer-whl
+ git config --local user.name "github-actions[bot]"
+ git config --local user.email "41898282+github-actions[bot]@users.noreply.github.com"
+ git add -A
+ git commit -m "update flashinfer-jit-cache whl"
+ git push
diff --git a/scripts/update_flashinfer_jit_cache_whl_index.py b/scripts/update_flashinfer_jit_cache_whl_index.py
new file mode 100644
index 0000000000..244723d575
--- /dev/null
+++ b/scripts/update_flashinfer_jit_cache_whl_index.py
@@ -0,0 +1,31 @@
+import hashlib
+import pathlib
+import re
+
+for path in sorted(pathlib.Path("dist").glob("*.whl")):
+ with open(path, "rb") as f:
+ sha256 = hashlib.sha256(f.read()).hexdigest()
+ # Extract version and CUDA version from wheel name
+ # Example: flashinfer_jit_cache-1.2.3+cu128-cp39-abi3-manylinux_2_28_x86_64.whl
+ # Example: flashinfer_jit_cache-1.2.3rc1+cu128-cp39-abi3-manylinux_2_28_x86_64.whl
+ # Example: flashinfer_jit_cache-1.2.3.post1+cu128-cp39-abi3-manylinux_2_28_x86_64.whl
+ match = re.search(
+ r"flashinfer_jit_cache-([0-9]+\.[0-9]+\.[0-9]+[a-z0-9.]*)\+cu(\d+)-",
+ path.name,
+ )
+ if not match:
+ print(f"Warning: Could not parse wheel name: {path.name}")
+ continue
+
+ ver, cu = match.groups()
+
+ # Create directory structure: cu{version}/flashinfer-jit-cache/
+ # No torch subdirectory since we don't separate by torch version
+ index_dir = pathlib.Path(f"flashinfer-whl/cu{cu}/flashinfer-jit-cache")
+ index_dir.mkdir(parents=True, exist_ok=True)
+
+ base_url = "https://github.com/flashinfer-ai/flashinfer/releases/download"
+ full_url = f"{base_url}/v{ver}/{path.name}#sha256={sha256}"
+
+ with (index_dir / "index.html").open("a") as f:
+ f.write(f'{path.name}
\n')