diff --git a/dev.py b/dev.py index 21c01ee7..66938978 100755 --- a/dev.py +++ b/dev.py @@ -18,10 +18,20 @@ from pathlib import Path THIS_DIRECTORY = Path(__file__).parent.absolute() -EXAMPLE_DIRECTORIES = [d for d in (THIS_DIRECTORY / "examples").iterdir() if d.is_dir()] +VERSIONS = ["v1", "v2"] +EXAMPLE_DIRECTORIES = [ + d + for v in VERSIONS + if (THIS_DIRECTORY / "examples" / v).is_dir() + for d in (THIS_DIRECTORY / "examples" / v).iterdir() + if d.is_dir() +] TEMPLATE_DIRECTORIES = [ - THIS_DIRECTORY / "template", - THIS_DIRECTORY / "template-reactless", + d + for v in VERSIONS + if (THIS_DIRECTORY / "templates" / v).is_dir() + for d in (THIS_DIRECTORY / "templates" / v).iterdir() + if d.is_dir() ] @@ -35,6 +45,97 @@ def run_verbose(cmd_args, *args, **kwargs): subprocess.run(cmd_args, *args, **kwargs) +def create_sanitized_copies_excluding_gitignored( + source_dir_a: Path, + source_dir_b: Path, + repo_root: Path, + destination_base_dir: Path, +) -> typing.Tuple[Path, Path]: + """ + Create sanitized copies of two directories while excluding files ignored by Git. + + This function is intended for robust, cross-platform directory comparisons (e.g., + git diff --no-index) where we want to ignore anything matched by the repository's + ignore rules (.gitignore, global and excludesfile), but still compare the remaining + content 1:1. + + Rationale: + - git diff --no-index does not honor .gitignore rules directly for arbitrary + directory arguments. + - Filtering via Git pathspecs is not supported with --no-index in a portable way. + - By asking Git for the ignored files under source_dir_b (the path inside the + repo) and excluding those relative paths from BOTH copies, we avoid false + positives from OS metadata and any user-ignored files. + + Parameters: + - source_dir_a: First directory to compare (e.g., freshly rendered output). + - source_dir_b: Second directory to compare (must be within repo_root). + - repo_root: Root of the Git repository (used to query ignored files). + - destination_base_dir: Directory where sanitized copies will be created. + + Returns: + - Tuple[Path, Path]: (sanitized_copy_of_a, sanitized_copy_of_b) + """ + # Compute the list of ignored files under source_dir_b using Git + try: + rel_path_in_repo = source_dir_b.relative_to(repo_root) + ls_cmd = [ + "git", + "-C", + str(repo_root), + "ls-files", + "-i", # list ignored files + "--exclude-standard", # honor .gitignore, global ignores, etc. + "--others", # show untracked files as well + "-z", # null-terminate paths to be robust to special characters + "--", + str(rel_path_in_repo), + ] + result = subprocess.run(ls_cmd, check=True, stdout=subprocess.PIPE, text=False) + ignored_absolute_paths = [ + repo_root / p.decode() for p in result.stdout.split(b"\0") if p + ] + except (ValueError, subprocess.CalledProcessError): + # ValueError if source_dir_b is not under repo_root; fallback to no ignores + ignored_absolute_paths = [] + + ignored_relative_to_b = set( + str(p.relative_to(source_dir_b).as_posix()) + for p in ignored_absolute_paths + if hasattr(p, "is_relative_to") + and p.is_relative_to(source_dir_b) + or (str(p).startswith(str(source_dir_b) + os.sep)) + ) + + def build_ignore_filter(source_root: Path): + def _ignore(dirpath: str, names: typing.List[str]) -> typing.List[str]: + rel_dir = Path(dirpath).relative_to(source_root) + to_ignore: typing.List[str] = [] + for name in names: + candidate = (rel_dir / name).as_posix() + if candidate in ignored_relative_to_b: + to_ignore.append(name) + return to_ignore + + return _ignore + + sanitized_a = destination_base_dir / "sanitized-output" + sanitized_b = destination_base_dir / "sanitized-repo" + + shutil.copytree( + source_dir_a, + sanitized_a, + ignore=build_ignore_filter(source_dir_a), + ) + shutil.copytree( + source_dir_b, + sanitized_b, + ignore=build_ignore_filter(source_dir_b), + ) + + return sanitized_a, sanitized_b + + # Commands def cmd_all_npm_install(args): """Install all node dependencies for all examples""" @@ -134,10 +235,17 @@ def cmd_all_python_build_package(args): final_dist_directory = THIS_DIRECTORY / "dist" final_dist_directory.mkdir(exist_ok=True) for project_dir in EXAMPLE_DIRECTORIES + TEMPLATE_DIRECTORIES: - run_verbose( - [sys.executable, "setup.py", "bdist_wheel", "--universal", "sdist"], - cwd=str(project_dir), - ) + pyproject_file = project_dir / "pyproject.toml" + if pyproject_file.exists(): + run_verbose( + [sys.executable, "-m", "build", "--wheel", "--sdist"], + cwd=str(project_dir), + ) + else: + run_verbose( + [sys.executable, "setup.py", "bdist_wheel", "--universal", "sdist"], + cwd=str(project_dir), + ) wheel_file = next(project_dir.glob("dist/*.whl")) shutil.copy(wheel_file, final_dist_directory) @@ -172,7 +280,13 @@ def cmd_example_check_deps(args): """Checks that dependencies of examples match the template""" template_deps = json.loads( ( - THIS_DIRECTORY / "template" / "my_component" / "frontend" / "package.json" + THIS_DIRECTORY + / "templates" + / "v1" + / "template" + / "my_component" + / "frontend" + / "package.json" ).read_text() ) examples_package_jsons = sorted( @@ -220,19 +334,22 @@ def cmd_check_test_utils(args): class CookiecutterVariant(typing.NamedTuple): replay_file: Path repo_directory: Path + cookiecutter_dir: Path COOKIECUTTER_VARIANTS = [ CookiecutterVariant( replay_file=THIS_DIRECTORY / ".github" / "replay-files" / "template.json", - repo_directory=THIS_DIRECTORY / "template", + repo_directory=THIS_DIRECTORY / "templates" / "v1" / "template", + cookiecutter_dir=THIS_DIRECTORY / "cookiecutter" / "v1", ), CookiecutterVariant( replay_file=THIS_DIRECTORY / ".github" / "replay-files" / "template-reactless.json", - repo_directory=THIS_DIRECTORY / "template-reactless", + repo_directory=THIS_DIRECTORY / "templates" / "v1" / "template-reactless", + cookiecutter_dir=THIS_DIRECTORY / "cookiecutter" / "v1", ), ] @@ -256,7 +373,7 @@ def cmd_check_templates_using_cookiecutter(args): str(cookiecutter_variant.replay_file), "--output-dir", str(output_dir), - str(THIS_DIRECTORY / "cookiecutter" / "v1"), + str(cookiecutter_variant.cookiecutter_dir), ] ) try: @@ -267,14 +384,23 @@ def cmd_check_templates_using_cookiecutter(args): Path(output_dir) / replay_file_content["cookiecutter"]["package_name"] ) + # Create sanitized copies excluding all files ignored by git in the repo + sanitized_output, sanitized_repo = ( + create_sanitized_copies_excluding_gitignored( + output_template, + cookiecutter_variant.repo_directory, + THIS_DIRECTORY, + Path(output_dir), + ) + ) run_verbose( [ "git", "--no-pager", "diff", "--no-index", - str(output_template), - str(cookiecutter_variant.repo_directory), + str(sanitized_output), + str(sanitized_repo), ] ) except subprocess.CalledProcessError: @@ -311,7 +437,7 @@ def cmd_update_templates(args): str(cookiecutter_variant.replay_file), "--output-dir", str(output_dir), - str(THIS_DIRECTORY / "cookiecutter" / "v1"), + str(cookiecutter_variant.cookiecutter_dir), ] ) print( diff --git a/template-reactless/LICENSE b/templates/v1/template-reactless/LICENSE similarity index 100% rename from template-reactless/LICENSE rename to templates/v1/template-reactless/LICENSE diff --git a/template-reactless/MANIFEST.in b/templates/v1/template-reactless/MANIFEST.in similarity index 100% rename from template-reactless/MANIFEST.in rename to templates/v1/template-reactless/MANIFEST.in diff --git a/template-reactless/README.md b/templates/v1/template-reactless/README.md similarity index 100% rename from template-reactless/README.md rename to templates/v1/template-reactless/README.md diff --git a/template-reactless/e2e/e2e_utils.py b/templates/v1/template-reactless/e2e/e2e_utils.py similarity index 100% rename from template-reactless/e2e/e2e_utils.py rename to templates/v1/template-reactless/e2e/e2e_utils.py diff --git a/template-reactless/e2e/test_template.py b/templates/v1/template-reactless/e2e/test_template.py similarity index 100% rename from template-reactless/e2e/test_template.py rename to templates/v1/template-reactless/e2e/test_template.py diff --git a/template-reactless/my_component/__init__.py b/templates/v1/template-reactless/my_component/__init__.py similarity index 100% rename from template-reactless/my_component/__init__.py rename to templates/v1/template-reactless/my_component/__init__.py diff --git a/template-reactless/my_component/example.py b/templates/v1/template-reactless/my_component/example.py similarity index 100% rename from template-reactless/my_component/example.py rename to templates/v1/template-reactless/my_component/example.py diff --git a/template-reactless/my_component/frontend/.env b/templates/v1/template-reactless/my_component/frontend/.env similarity index 100% rename from template-reactless/my_component/frontend/.env rename to templates/v1/template-reactless/my_component/frontend/.env diff --git a/template-reactless/my_component/frontend/.prettierrc b/templates/v1/template-reactless/my_component/frontend/.prettierrc similarity index 100% rename from template-reactless/my_component/frontend/.prettierrc rename to templates/v1/template-reactless/my_component/frontend/.prettierrc diff --git a/template-reactless/my_component/frontend/index.html b/templates/v1/template-reactless/my_component/frontend/index.html similarity index 100% rename from template-reactless/my_component/frontend/index.html rename to templates/v1/template-reactless/my_component/frontend/index.html diff --git a/template-reactless/my_component/frontend/package.json b/templates/v1/template-reactless/my_component/frontend/package.json similarity index 100% rename from template-reactless/my_component/frontend/package.json rename to templates/v1/template-reactless/my_component/frontend/package.json diff --git a/template-reactless/my_component/frontend/public/bootstrap.min.css b/templates/v1/template-reactless/my_component/frontend/public/bootstrap.min.css similarity index 100% rename from template-reactless/my_component/frontend/public/bootstrap.min.css rename to templates/v1/template-reactless/my_component/frontend/public/bootstrap.min.css diff --git a/template-reactless/my_component/frontend/src/index.ts b/templates/v1/template-reactless/my_component/frontend/src/index.ts similarity index 100% rename from template-reactless/my_component/frontend/src/index.ts rename to templates/v1/template-reactless/my_component/frontend/src/index.ts diff --git a/template-reactless/my_component/frontend/src/vite-env.d.ts b/templates/v1/template-reactless/my_component/frontend/src/vite-env.d.ts similarity index 100% rename from template-reactless/my_component/frontend/src/vite-env.d.ts rename to templates/v1/template-reactless/my_component/frontend/src/vite-env.d.ts diff --git a/template-reactless/my_component/frontend/tsconfig.json b/templates/v1/template-reactless/my_component/frontend/tsconfig.json similarity index 100% rename from template-reactless/my_component/frontend/tsconfig.json rename to templates/v1/template-reactless/my_component/frontend/tsconfig.json diff --git a/template-reactless/my_component/frontend/vite.config.ts b/templates/v1/template-reactless/my_component/frontend/vite.config.ts similarity index 100% rename from template-reactless/my_component/frontend/vite.config.ts rename to templates/v1/template-reactless/my_component/frontend/vite.config.ts diff --git a/template-reactless/setup.py b/templates/v1/template-reactless/setup.py similarity index 100% rename from template-reactless/setup.py rename to templates/v1/template-reactless/setup.py diff --git a/template/LICENSE b/templates/v1/template/LICENSE similarity index 100% rename from template/LICENSE rename to templates/v1/template/LICENSE diff --git a/template/MANIFEST.in b/templates/v1/template/MANIFEST.in similarity index 100% rename from template/MANIFEST.in rename to templates/v1/template/MANIFEST.in diff --git a/template/README.md b/templates/v1/template/README.md similarity index 100% rename from template/README.md rename to templates/v1/template/README.md diff --git a/template/e2e/e2e_utils.py b/templates/v1/template/e2e/e2e_utils.py similarity index 100% rename from template/e2e/e2e_utils.py rename to templates/v1/template/e2e/e2e_utils.py diff --git a/template/e2e/test_template.py b/templates/v1/template/e2e/test_template.py similarity index 100% rename from template/e2e/test_template.py rename to templates/v1/template/e2e/test_template.py diff --git a/template/my_component/__init__.py b/templates/v1/template/my_component/__init__.py similarity index 100% rename from template/my_component/__init__.py rename to templates/v1/template/my_component/__init__.py diff --git a/template/my_component/example.py b/templates/v1/template/my_component/example.py similarity index 100% rename from template/my_component/example.py rename to templates/v1/template/my_component/example.py diff --git a/template/my_component/frontend/.env b/templates/v1/template/my_component/frontend/.env similarity index 100% rename from template/my_component/frontend/.env rename to templates/v1/template/my_component/frontend/.env diff --git a/template/my_component/frontend/.prettierrc b/templates/v1/template/my_component/frontend/.prettierrc similarity index 100% rename from template/my_component/frontend/.prettierrc rename to templates/v1/template/my_component/frontend/.prettierrc diff --git a/template/my_component/frontend/index.html b/templates/v1/template/my_component/frontend/index.html similarity index 100% rename from template/my_component/frontend/index.html rename to templates/v1/template/my_component/frontend/index.html diff --git a/template/my_component/frontend/package.json b/templates/v1/template/my_component/frontend/package.json similarity index 100% rename from template/my_component/frontend/package.json rename to templates/v1/template/my_component/frontend/package.json diff --git a/template/my_component/frontend/public/bootstrap.min.css b/templates/v1/template/my_component/frontend/public/bootstrap.min.css similarity index 100% rename from template/my_component/frontend/public/bootstrap.min.css rename to templates/v1/template/my_component/frontend/public/bootstrap.min.css diff --git a/template/my_component/frontend/src/MyComponent.tsx b/templates/v1/template/my_component/frontend/src/MyComponent.tsx similarity index 100% rename from template/my_component/frontend/src/MyComponent.tsx rename to templates/v1/template/my_component/frontend/src/MyComponent.tsx diff --git a/template/my_component/frontend/src/index.tsx b/templates/v1/template/my_component/frontend/src/index.tsx similarity index 100% rename from template/my_component/frontend/src/index.tsx rename to templates/v1/template/my_component/frontend/src/index.tsx diff --git a/template/my_component/frontend/src/vite-env.d.ts b/templates/v1/template/my_component/frontend/src/vite-env.d.ts similarity index 100% rename from template/my_component/frontend/src/vite-env.d.ts rename to templates/v1/template/my_component/frontend/src/vite-env.d.ts diff --git a/template/my_component/frontend/tsconfig.json b/templates/v1/template/my_component/frontend/tsconfig.json similarity index 100% rename from template/my_component/frontend/tsconfig.json rename to templates/v1/template/my_component/frontend/tsconfig.json diff --git a/template/my_component/frontend/vite.config.ts b/templates/v1/template/my_component/frontend/vite.config.ts similarity index 100% rename from template/my_component/frontend/vite.config.ts rename to templates/v1/template/my_component/frontend/vite.config.ts diff --git a/template/setup.py b/templates/v1/template/setup.py similarity index 100% rename from template/setup.py rename to templates/v1/template/setup.py diff --git a/templates/v2/.gitkeep b/templates/v2/.gitkeep new file mode 100644 index 00000000..e69de29b