diff --git a/.devcontainer/devcontainer.json b/.devcontainer/devcontainer.json new file mode 100644 index 00000000..34f84a8d --- /dev/null +++ b/.devcontainer/devcontainer.json @@ -0,0 +1,30 @@ +{ + "name": "py-launch-blueprint", + "build": { + "dockerfile": "Dockerfile", + "context": ".." + }, + "postCreateCommand": "just --version", + "mounts": [ + "source=/var/run/docker.sock,target=/var/run/docker.sock,type=bind" + ], + "remoteUser": "root", + "settings": { + "terminal.integrated.shell.linux": "/bin/bash" + }, + "customizations": { + "vscode": { + "extensions": [ + "ms-python.python", + "ms-python.vscode-pylance", + "charliermarsh.ruff", + "matangover.mypy", + "tamasfe.even-better-toml", + "redhat.vscode-yaml", + "eamodio.gitlens", + "streetsidesoftware.code-spell-checker" + ] + } + }, + "features": {} +} diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 68cbfba6..a81b8447 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -1,7 +1,7 @@ # .pre-commit-config.yaml repos: - repo: https://github.com/pre-commit/mirrors-mypy - rev: v1.15.0 + rev: v1.16.0 hooks: - id: mypy additional_dependencies: [ @@ -21,7 +21,7 @@ repos: - id: check-toml - id: check-added-large-files - repo: https://github.com/astral-sh/ruff-pre-commit - rev: v0.11.11 + rev: v0.11.12 hooks: - id: ruff entry: ruff check --force-exclude --no-cache @@ -49,8 +49,13 @@ repos: types: [python] pass_filenames: false # Run full test suite stages: [pre-commit] # Only run on pre-commit - - + - repo: local + hooks: + - id: check-vscode-extension-sync + name: Check VSCode Extension Sync + entry: uv run --with-editable . scripts/check_extension_sync.py + language: system + types: [json] # Global Exclusion # If you want to exclude py_launch_blueprint/_version.py from all pre-commit hooks, # you can use a global exclusion at the top level of your configuration: @@ -62,7 +67,7 @@ repos: # rev: v4.4.0 # hooks: # - id: end-of-file-fixer - +# # Hook-Level Exclusion (Recommended Approach) # For your specific case where the end-of-file-fixer hook is modifying # py_launch_blue/print_version.py, the most targeted solution is to add an exclusion diff --git a/Justfile b/Justfile index bde43efc..87e0a168 100644 --- a/Justfile +++ b/Justfile @@ -628,5 +628,9 @@ clean-pr-to-testrepo new_repo_name="test-actions-repo": # just build # just run +# Check if VSCode recommended extensions are synced with devcontainer +check-extension-sync: + uv run --with-editable . scripts/check_extension_sync.py + # Alias for dev (full developer cycle: format → lint → test → build) alias cycle := dev diff --git a/scripts/check_extension_sync.py b/scripts/check_extension_sync.py new file mode 100644 index 00000000..152d1872 --- /dev/null +++ b/scripts/check_extension_sync.py @@ -0,0 +1,64 @@ +#!/usr/bin/env python3 + +import json +import sys +from pathlib import Path +from typing import Any + +EXTENSIONS_FILE = Path(".vscode/extensions.json") +DEVCONTAINER_FILE = Path(".devcontainer/devcontainer.json") + + +def load_json(file_path: Path) -> dict[str, Any]: + try: + with open(file_path) as f: + return json.load(f) # type: ignore + except FileNotFoundError: + print(f"Error: {file_path} not found.") + sys.exit(2) + except json.JSONDecodeError as e: + print(f"Error parsing {file_path}: {e}") + sys.exit(2) + + +def extract_recommended_extensions(data: dict[str, Any]) -> set[str]: + return set(data.get("recommendations", [])) + + +def extract_devcontainer_extensions(data: dict[str, Any]) -> set[str]: + try: + return { + ext if isinstance(ext, str) else ext["id"] + for ext in data["customizations"]["vscode"]["extensions"] + } + except KeyError: + return set() + + +def main() -> None: + vscode_data = load_json(EXTENSIONS_FILE) + devcontainer_data = load_json(DEVCONTAINER_FILE) + + vscode_exts = extract_recommended_extensions(vscode_data) + dev_exts = extract_devcontainer_extensions(devcontainer_data) + + missing = vscode_exts - dev_exts + + if missing: + print("VSCode extensions not synced with devcontainer configuration!\n") + print("Missing from .devcontainer/devcontainer.json:") + for ext in sorted(missing): + print(f"- {ext}") + print( + "\nTo fix: Add these extensions to the " + "`customizations.vscode.extensions` array in " + "`.devcontainer/devcontainer.json`" + ) + sys.exit(1) + + print("VSCode extensions are in sync.") + sys.exit(0) + + +if __name__ == "__main__": + main()