Files
OpenShell/scripts/update_license_headers.py
T
2f96c53b8c feat(gateway,cli): windows compilation support (#2496)
* chore(windows): gate Unix-only workspace code for MSVC

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* feat(windows): stub unsupported compute drivers

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* ci(windows): add MSVC mise build lane

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* docs(windows): document MSVC build-only design

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* docs(agent): add Windows MSVC build skill

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* feat(windows): add Windows build support

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor(windows): consolidate Windows-specific dependencies and improve build logic

Signed-off-by: Akber Raza <akberr@nvidia.com>

* feat(windows): add libclang path resolution and update cargo commands with bundled Z3 features

Signed-off-by: Akber Raza <akberr@nvidia.com>

* chore(tooling): lock Windows tool artifacts

Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* feat(windows): enhance libclang path resolution to support architecture-specific subdirectories

Signed-off-by: Akber Raza <akberr@nvidia.com>

* Fix Windows dependency gating after sync merge

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(z3): update Z3 header path requirements in Windows build documentation and scripts

Signed-off-by: Akber Raza <akberr@nvidia.com>

* docs(windows): relocate Windows MSVC build design to architecture/

Why: windows-msvc-build-design.mdx is a design document ("design decisions for
the native Windows MSVC build lane"), but it lived in the published, user-facing
docs/reference/ tree. Per AGENTS.md (Documentation) and architecture/README.md
("rfc/ vs architecture/"), design content belongs in architecture/ (or rfc/),
not in published reference. It also shared Fern sidebar "position: 6" with the
MXC compute-driver design page, colliding in the Reference nav ordering.

What:
- Move docs/reference/windows-msvc-build-design.mdx ->
  architecture/windows-msvc-build.md.
- Strip the Fern publish frontmatter and add a plain H1, matching the other
  architecture docs.
- Register it in the architecture doc index in architecture/README.md.
- Repoint the inbound references (build-openshell-mxc-windows skill + reference,
  implement-openshell-mxc-driver skill) to the new path.

With both design pages moved out of docs/reference/, the duplicate position-6
sidebar collision is resolved.

Signed-off-by: Akber Raza <akberr@nvidia.com>

* remove openshell-supervisor-network from unsupported driver package test exclusion list

Signed-off-by: Akber Raza <akberr@nvidia.com>

# Conflicts:
#	tasks/scripts/windows-msvc.ps1

* fix(interceptors): gate unix-only imports so the crate builds on Windows

openshell-gateway-interceptors failed to compile on Windows (E0432: no UnixStream in tokio::net), breaking any Windows build of openshell-server (which depends on it unconditionally). The connect_unix_endpoint fn was already #[cfg(unix)]-gated, but the imports it uses (UnixStream, TokioIo, Uri, service_fn) were left ungated. Gate those four imports with #[cfg(unix)] too. No behavior change on unix; Windows now compiles (no errors, no unused-import warnings).

Signed-off-by: Akber Raza <akberr@nvidia.com>

* feat(windows): add native ARM64 test support

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(mise): skip Skaffold on Windows

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): harden ARM64 toolchain discovery

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): scope ARM64 toolchain preflight

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): restore compatibility after GitHub sync

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): avoid rate-limited Z3 source lookup

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(mise): skip Helm checks on Windows

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): support repository pre-commit checks

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): stabilize native MSVC validation

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): harden shared Z3 source cache

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>

* fix(windows): avoid leaking MSVC flags into clang-cl

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): complete ARM64 migration audit

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): restore ARM64 Ninja discovery

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor(windows): separate platform crate roots

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): restore proto include cfg gating

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor: address lint errors

* fix(windows): add preflight check for proxy auth file path

* docs(windows): update GitHub checkout guidance

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): restore CI after dependency updates

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(mise): repair Windows sccache lock entry

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(windows): reconcile validation after rebase

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor(server): exclude unsupported drivers on Windows

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>

* refactor(server): isolate platform driver config

Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>

* fix(windows): repair unsupported driver contract test

Signed-off-by: Akber Raza <akberr@nvidia.com>

* fix(sandbox): remove stale dependencies

Signed-off-by: Akber Raza <akberr@nvidia.com>

* ci(windows): pin x64 workflow actions

Signed-off-by: Akber Raza <akberr@nvidia.com>

* ci(windows): align x64 Rust toolchain

Signed-off-by: Akber Raza <akberr@nvidia.com>

* ci(windows): align ARM64 workflow setup

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor(windows): exclude unsupported runtime crates

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor(windows): exclude unsupported crates at workspace boundary

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* refactor(server): gate builtin driver config by platform

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* fix(sandbox): restore crate documentation

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* ci(windows): make build workflow manual

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* ci(windows): temporarily enable pull request builds

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* ci(windows): cache Rust dependencies

Signed-off-by: Akber Raza <akberr@nvidia.com>

* refactor(windows): remove unnecessary platform changes

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* ci(windows): make build workflow manual

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* fix(ci): synchronize mise lockfile

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* fix(ci): normalize mise provenance metadata

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* refactor(python): isolate Windows atomic replace retry

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

* fix(python): type Windows permission test errors

Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>

---------

Signed-off-by: Shailendra Singh <shailendras@nvidia.com>
Signed-off-by: Akber Raza <akberr@nvidia.com>
Signed-off-by: Giedrius Burachas <gburachas@nvidia.com>
Signed-off-by: Piotr Mlocek <pmlocek@nvidia.com>
Signed-off-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>
Co-authored-by: Shailendra Singh <shailendras@nvidia.com>
Co-authored-by: Giedrius Burachas <gburachas@nvidia.com>
Co-authored-by: Jamie King <jamiek@nvidia.com>
Co-authored-by: Piotr Mlocek <pmlocek@nvidia.com>
Co-authored-by: Piotr Mlocek <1116309+pimlock@users.noreply.github.com>
2026-08-11 21:00:36 +00:00

360 lines
10 KiB
Python
Executable File

#!/usr/bin/env python3
# SPDX-FileCopyrightText: Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved.
# SPDX-License-Identifier: Apache-2.0
"""Add or check SPDX license headers on source files.
Usage:
# Add/update headers on all source files
python scripts/update_license_headers.py
# Check mode (CI / pre-commit) — exit 1 if any file is missing a header
python scripts/update_license_headers.py --check
# Operate on specific files only (useful for pre-commit on staged files)
python scripts/update_license_headers.py path/to/file.rs path/to/other.py
"""
from __future__ import annotations
import argparse
import os
import subprocess
import sys
from pathlib import Path
# ---------------------------------------------------------------------------
# Configuration
# ---------------------------------------------------------------------------
COPYRIGHT_TEXT = (
"Copyright (c) 2025-2026 NVIDIA CORPORATION & AFFILIATES. All rights reserved."
)
LICENSE_ID = "Apache-2.0"
# Map file extensions to their line-comment prefix.
COMMENT_STYLES: dict[str, str] = {
".rs": "//",
".proto": "//",
".py": "#",
".sh": "#",
".toml": "#",
".yaml": "#",
".yml": "#",
".rego": "#",
}
# Directories to skip entirely (relative to repo root).
EXCLUDE_DIRS: set[str] = {
"target",
"e2e/rust/target",
"architecture/plans",
"scripts/lint-mermaid/node_modules",
".venv",
".git",
".cache",
"python/openshell/_proto",
}
# Individual filenames to skip.
EXCLUDE_FILES: set[str] = {
"Cargo.lock",
"uv.lock",
".gitlab-ci.yml",
}
# Glob-style directory prefixes to also skip (CI / editor config dirs).
EXCLUDE_DIR_PREFIXES: tuple[str, ...] = (
".github/",
".agents/",
".claude/",
)
# ---------------------------------------------------------------------------
# Header generation
# ---------------------------------------------------------------------------
def make_header(comment: str) -> str:
"""Return the two-line SPDX header for a given comment prefix."""
return (
f"{comment} SPDX-FileCopyrightText: {COPYRIGHT_TEXT}\n"
f"{comment} SPDX-License-Identifier: {LICENSE_ID}\n"
)
# ---------------------------------------------------------------------------
# File discovery
# ---------------------------------------------------------------------------
def find_repo_root() -> Path:
"""Walk up from CWD to find the directory containing .git."""
path = Path.cwd()
while path != path.parent:
if (path / ".git").exists():
return path
path = path.parent
return Path.cwd()
def is_excluded(rel: Path) -> bool:
"""Return True if a path should be skipped."""
rel_str = rel.as_posix()
# Exact filename exclusions.
if rel.name in EXCLUDE_FILES:
return True
# Directory exclusions.
for exc_dir in EXCLUDE_DIRS:
if rel_str == exc_dir or rel_str.startswith(exc_dir + "/"):
return True
# Prefix exclusions (CI config, editor config).
return any(rel_str.startswith(prefix) for prefix in EXCLUDE_DIR_PREFIXES)
def git_candidate_files(root: Path) -> list[Path] | None:
"""Return Git-tracked and unignored files, or None if Git is unavailable."""
try:
result = subprocess.run(
[
"git",
"-C",
str(root),
"ls-files",
"-z",
"--cached",
"--others",
"--exclude-standard",
],
check=True,
capture_output=True,
)
except (OSError, subprocess.CalledProcessError):
return None
files = []
for raw_path in result.stdout.split(b"\0"):
if raw_path:
files.append(Path(os.fsdecode(raw_path)))
return files
def is_git_ignored(root: Path, rel: Path) -> bool:
"""Return True if Git ignore rules exclude a path."""
try:
result = subprocess.run(
["git", "-C", str(root), "check-ignore", "-q", "--", str(rel)],
check=False,
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
)
except OSError:
return False
return result.returncode == 0
def is_dockerfile(path: Path) -> bool:
"""Return True for Dockerfile variants (matched by name, not extension)."""
return path.name == "Dockerfile" or path.name.startswith("Dockerfile.")
def get_comment_style(path: Path) -> str | None:
"""Return the comment prefix for a file, or None if unsupported."""
if is_dockerfile(path):
return "#"
return COMMENT_STYLES.get(path.suffix)
def discover_files(root: Path) -> list[Path]:
"""Walk the repo and return all files that should have headers."""
results = []
git_files = git_candidate_files(root)
if git_files is not None:
for rel in git_files:
path = root / rel
if not path.is_file():
continue
if is_excluded(rel):
continue
if get_comment_style(rel) is not None:
results.append(path)
return sorted(results)
for dirpath, dirnames, filenames in os.walk(root):
rel_dir = Path(dirpath).relative_to(root)
# Prune excluded directories (modifying dirnames in-place).
dirnames[:] = [d for d in dirnames if not is_excluded(rel_dir / d)]
for fname in filenames:
fpath = Path(dirpath) / fname
rel = fpath.relative_to(root)
if is_excluded(rel):
continue
if get_comment_style(rel) is not None:
results.append(fpath)
return sorted(results)
# ---------------------------------------------------------------------------
# Header checking and insertion
# ---------------------------------------------------------------------------
SPDX_MARKER = "SPDX-License-Identifier"
def has_header(lines: list[str]) -> bool:
"""Check if the SPDX header is present in the first 10 lines."""
return any(SPDX_MARKER in line for line in lines[:10])
def find_insertion_point(lines: list[str], path: Path) -> int:
"""Determine where to insert the header.
Returns the line index where the header should be placed. The header
will be inserted *before* this index, with a blank line after it.
Special cases:
- Shebang (#!/...) on line 0 → insert at line 1
- Dockerfile `# syntax=` on line 0 → insert at line 1
- Otherwise → insert at line 0
"""
if not lines:
return 0
first = lines[0]
# Shebang line — keep it on line 0, header goes after.
if first.startswith("#!"):
return 1
# Dockerfile syntax directive.
if is_dockerfile(path) and first.lower().startswith("# syntax="):
return 1
return 0
def insert_header(content: str, comment: str, path: Path) -> str:
"""Insert the SPDX header into file content, returning the new content."""
header = make_header(comment)
lines = content.splitlines(keepends=True)
insert_at = find_insertion_point(lines, path)
if insert_at == 0:
# Header at top, blank line before existing content (if any).
if lines:
return header + "\n" + content
return header
else:
# Insert after a first-line directive (shebang / # syntax=).
before = lines[:insert_at]
after = lines[insert_at:]
return "".join(before) + "\n" + header + "\n" + "".join(after)
# ---------------------------------------------------------------------------
# Main logic
# ---------------------------------------------------------------------------
def process_file(path: Path, root: Path, *, check: bool, verbose: bool) -> bool:
"""Process a single file. Returns True if the file is compliant."""
rel = path.relative_to(root)
comment = get_comment_style(rel)
if comment is None:
return True
content = path.read_text(encoding="utf-8")
lines = content.splitlines()
if has_header(lines):
if verbose:
print(f" ok: {rel}")
return True
if check:
print(f" MISSING: {rel}")
return False
# Insert the header.
new_content = insert_header(content, comment, rel)
path.write_text(new_content, encoding="utf-8")
if verbose:
print(f" added: {rel}")
return True
def main() -> int:
parser = argparse.ArgumentParser(
description="Add or check SPDX license headers on source files.",
)
parser.add_argument(
"--check",
action="store_true",
help="Check mode: exit 1 if any file is missing a header.",
)
parser.add_argument(
"--verbose",
"-v",
action="store_true",
help="Print status for every file processed.",
)
parser.add_argument(
"paths",
nargs="*",
type=Path,
help="Specific files to process (default: all files under repo root).",
)
args = parser.parse_args()
root = find_repo_root()
if args.paths:
# Resolve relative paths and filter to supported + non-excluded files.
files = []
for p in args.paths:
p = p.resolve()
if not p.is_file():
continue
try:
rel = p.relative_to(root)
except ValueError:
continue
if is_excluded(rel) or is_git_ignored(root, rel):
continue
if get_comment_style(rel) is not None:
files.append(p)
else:
files = discover_files(root)
if args.check:
print(f"Checking {len(files)} files for SPDX headers...")
else:
print(f"Processing {len(files)} files...")
missing = []
for f in files:
if not process_file(f, root, check=args.check, verbose=args.verbose):
missing.append(f)
if args.check:
if missing:
print(f"\n{len(missing)} file(s) missing SPDX headers.")
return 1
print("All files have SPDX headers.")
return 0
print("Done.")
return 0
if __name__ == "__main__":
sys.exit(main())