# Copyright (c) Microsoft Corporation. All rights reserved. # Licensed under the MIT License. """All the action we need during build""" import json import os import pathlib import sys import tomllib import urllib.request as url_lib from typing import List import nox # pylint: disable=import-error # Use uv to create session environments. The uv-managed standalone Python builds # don't ship pythonw.exe, which makes the default virtualenv backend fail on Windows. nox.options.default_venv_backend = "uv" def _read_dependencies() -> List[str]: """Read project dependencies from pyproject.toml.""" pyproject = tomllib.loads(pathlib.Path("pyproject.toml").read_text()) return list(pyproject.get("project", {}).get("dependencies", [])) def _install_bundle(session: nox.Session) -> None: deps = _read_dependencies() session.run( "uv", "pip", "install", "--target", "./libs", "--no-cache-dir", "--upgrade", *deps, external=True, ) def _check_files(names: List[str]) -> None: root_dir = pathlib.Path(__file__).parent for name in names: file_path = root_dir / name lines: List[str] = file_path.read_text().splitlines() if any(line for line in lines if line.startswith("# TODO:")): raise Exception(f"Please update {os.fspath(file_path)}.") def _get_package_data(package): json_uri = f"https://registry.npmjs.org/{package}" with url_lib.urlopen(json_uri) as response: return json.loads(response.read()) def _update_npm_packages(session: nox.Session) -> None: pinned = { "vscode-languageclient", "@types/vscode", "@types/node", } package_json_path = pathlib.Path(__file__).parent / "package.json" package_json = json.loads(package_json_path.read_text(encoding="utf-8")) for package in package_json["dependencies"]: if package not in pinned: data = _get_package_data(package) latest = "^" + data["dist-tags"]["latest"] package_json["dependencies"][package] = latest for package in package_json["devDependencies"]: if package not in pinned: data = _get_package_data(package) latest = "^" + data["dist-tags"]["latest"] package_json["devDependencies"][package] = latest # Ensure engine matches the package if package_json["engines"]["vscode"] != package_json["devDependencies"]["@types/vscode"]: print("Please check VS Code engine version and @types/vscode version in package.json.") new_package_json = json.dumps(package_json, indent=4) # JSON dumps uses \n for line ending on all platforms by default if not new_package_json.endswith("\n"): new_package_json += "\n" package_json_path.write_text(new_package_json, encoding="utf-8") session.run("npm", "install", external=True) def _setup_template_environment(session: nox.Session) -> None: """Install project dependencies into the bundled libs directory.""" _install_bundle(session) @nox.session() def setup(session: nox.Session) -> None: """Sets up the template for development.""" _setup_template_environment(session) @nox.session() def tests(session: nox.Session) -> None: """Runs all the tests for the extension.""" deps = _read_dependencies() session.run("uv", "pip", "install", "--python", sys.executable, *deps, external=True) session.run("uv", "pip", "install", "--python", sys.executable, "pytest", external=True) session.run("pytest", "tests/python_tests") @nox.session() def lint(session: nox.Session) -> None: """Runs linter and formatter checks on python files.""" deps = _read_dependencies() session.run("uv", "pip", "install", "--python", sys.executable, *deps, external=True) session.run( "uv", "pip", "install", "--python", sys.executable, "pytest", "pylint", "black", "isort", external=True, ) session.run("pylint", "-d", "W0511", "./bundled/tool") session.run( "pylint", "-d", "W0511", "--ignore=./tests/python_tests/test_data", "./tests/python_tests", ) session.run("pylint", "-d", "W0511", "noxfile.py") # check formatting using black session.run("black", "--check", "./bundled/tool") session.run("black", "--check", "./tests/python_tests") session.run("black", "--check", "noxfile.py") # check import sorting using isort session.run("isort", "--check", "./bundled/tool") session.run("isort", "--check", "./tests/python_tests") session.run("isort", "--check", "noxfile.py") # check typescript code session.run("npm", "run", "lint", external=True) @nox.session() def build_package(session: nox.Session) -> None: """Builds VSIX package for publishing.""" _check_files(["README.md", "LICENSE", "SECURITY.md", "SUPPORT.md"]) _setup_template_environment(session) session.run("npm", "install", external=True) session.run("npm", "run", "vsce-package", external=True) def _update_uv_lock(session: nox.Session) -> None: session.run("uv", "lock", "--upgrade", external=True) @nox.session() def update_packages(session: nox.Session) -> None: """Update Python and npm packages.""" _update_uv_lock(session) _update_npm_packages(session)