Skip to content
Closed
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
85 changes: 81 additions & 4 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -16,12 +16,26 @@ concurrency:
cancel-in-progress: true

jobs:
images:
name: Define Base Images
runs-on: ubuntu-latest
outputs:
lint: ghcr.io/nvidia/cutile-python/lint:2026-01-12-aea51b7409cc
docs: ghcr.io/nvidia/cutile-python/docs:2026-01-12-96c265b9029e
build_py310: ghcr.io/nvidia/cutile-python/build_py_3.10_x86_64:2026-01-12-a3f084500fb0
build_py311: ghcr.io/nvidia/cutile-python/build_py_3.11_x86_64:2026-01-12-d0a88a59d0fd
build_py312: ghcr.io/nvidia/cutile-python/build_py_3.12_x86_64:2026-01-12-9cf7e54a5580
build_py313: ghcr.io/nvidia/cutile-python/build_py_3.13_x86_64:2026-01-12-7f9db97c8ad8
steps:
- run: echo "Defining image tags"

lint:
name: Lint
needs: images
runs-on: ubuntu-latest
timeout-minutes: 10
container:
image: ghcr.io/nvidia/cutile-python/lint:2025-12-06-4cb7d16e4c20
image: ${{ needs.images.outputs.lint }}
steps:
- name: Checkout repository
uses: actions/checkout@v6
Expand All @@ -30,10 +44,73 @@ jobs:
run: flake8

- name: Run cpplint
run: python3 ci/cpplint.py
run: python scripts/cpplint.py

- name: Check license headers (REUSE)
run: ci/scripts/check_license.sh
run: scripts/check_license.sh

- name: Check inline samples are up to date
run: python3 test/tools/inline_samples.py --check
run: python test/tools/inline_samples.py --check

docs:
name: Build Docs
needs: [images, build]
runs-on: ubuntu-latest
timeout-minutes: 10
container:
image: ${{ needs.images.outputs.docs }}
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Download wheel
uses: actions/download-artifact@v4
with:
name: wheel-py3.12-linux-x86_64
path: dist/

- name: Install wheel
run: pip install dist/*.whl

- name: Build documentation
run: make -C docs html

- name: Upload docs artifact
uses: actions/upload-artifact@v4
with:
name: docs-html
path: docs/build/html
retention-days: 7

build:
name: Build Wheel (Python ${{ matrix.python-version }})
needs: images
runs-on: ubuntu-latest
timeout-minutes: 30
strategy:
matrix:
include:
- python-version: "3.10"
image_key: build_py310
- python-version: "3.11"
image_key: build_py311
- python-version: "3.12"
image_key: build_py312
- python-version: "3.13"
image_key: build_py313
container:
image: ${{ needs.images.outputs[matrix.image_key] }}
steps:
- name: Checkout repository
uses: actions/checkout@v6

- name: Build wheel
run: python setup.py bdist_wheel

- name: Upload wheel artifact
uses: actions/upload-artifact@v4
with:
name: wheel-py${{ matrix.python-version }}-linux-x86_64
path: dist/*.whl
if-no-files-found: error
retention-days: 7
12 changes: 12 additions & 0 deletions scripts/check_license.sh
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
#!/bin/bash
# SPDX-FileCopyrightText: Copyright (c) <2025> NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

ignore_files=("src/cuda/tile/VERSION")
outputs=$(reuse lint --lines | grep -v ${ignore_files[@]/#/-e })
if [ -n "$outputs" ]; then
echo -e "License check failed\n${outputs}"
exit 1
fi

76 changes: 76 additions & 0 deletions scripts/cpplint.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) <2025> NVIDIA CORPORATION & AFFILIATES. All rights reserved.
#
# SPDX-License-Identifier: Apache-2.0

import os
import sys

file_extensions = [
".h",
".hpp",
".hh",
".c",
".C",
".cpp",
".cxx",
".cc",
".pyx",
".pxd",
]
max_line_len = 100


def should_lint(filename: str):
return any(filename.endswith(x) for x in file_extensions)


def lint(paths):
num_errors = 0
num_files = 0

def report_error(message: str):
nonlocal num_errors
print(f"{full_name[len(path) + 1:]}:{i + 1}: {message}", file=sys.stderr)
num_errors += 1

for path in paths:
for root, dirs, files in os.walk(path):
for filename in files:
if not should_lint(filename):
continue
full_name = os.path.join(root, filename)
with open(full_name, "r") as f:
for i, line in enumerate(f):
if "noqa" in line:
continue
if "SPDX" in line:
continue

length = len(line)
if line.endswith("\n"):
length -= 1
if length > max_line_len:
report_error(
f"Line is longer than {max_line_len} characters"
)
if length > 0 and line[length - 1].isspace():
report_error("Trailing whitespace at the end of the line")
num_files += 1

if num_errors > 0:
print(f"Found {num_errors} errors", file=sys.stderr)
sys.exit(1)
elif num_files == 0:
print("No input files found!", file=sys.stderr)
sys.exit(2)
else:
print(f"Checked {num_files} files, all OK")


if __name__ == "__main__":
script_dir = os.path.dirname(__file__)
project_root = os.getcwd()
dirs = ["cext", "torch_cext", "src"]
paths = [os.path.join(project_root, d) for d in dirs]
lint(paths)