diff --git a/README.md b/README.md index a5b998d..5a5a238 100644 --- a/README.md +++ b/README.md @@ -10,25 +10,32 @@ The BIIGLE manual contains [some tutorials](https://biigle-admin-documentation.r ## How to use this template -First, [create a new repository](https://github.com/biigle/module/generate) based on this template. Then update the name of this module from `biigle/module` to whatever name you want to use for your module. The name has to be updated at the following locations: +First, [create a new repository](https://github.com/biigle/module/generate) (either private or public) based on this template. -1. [`QuotesController.php`](src/Http/Controllers/QuotesController.php#L16) -2. [`index.blade.php#L13`](src/resources/views/index.blade.php#L13) -3. [`index.blade.php#L16`](src/resources/views/index.blade.php#L16) -4. [`ModuleServiceProvider.php#L21`](src/ModuleServiceProvider.php#L21) -5. [`ModuleServiceProvider.php#L30`](src/ModuleServiceProvider.php#L30) -6. [`ModuleServiceProvider.php#L43`](src/ModuleServiceProvider.php#L43) -7. [`composer.json#L2`](composer.json#L2) -8. [`test.yml#L15`](.github/workflows/test.yml#L15) +## Development & Development Installation +Clone the module to a local directory. The name of this module needs to be updated in several locations. For your convenience, we provide a python script to automate this process. +Run `python changeModuleName.py` to update all locations at once. The module name is the name you gave to the module when you created the repository, i.e. the name of the parent folder of this file. -Next, update the namespace of all PHP classes (`Biigle\Modules\Module`) and replace `Module` with the name of your module. Do this in [`webpack.mix.js#L23`](webpack.mix.js#L23), too. Also update this readme for your new module. You should remove the first two subsections and update the installation instructions. Now you can install the module and start developing. +If your module is not (yet) published on Packagist, you need to add the following to biigle's composer.json (found in the biigle root directory; if repositories already exist, add the new one to the list): -In addition to the code of the [tutorials](https://biigle.de/manual#developer-tutorials) this repository already contains the configuration for [Laravel Mix](https://laravel.com/docs/9.x/mix) as build system. To install the build system, run and then run `npm install`. Now you can use the following commands: +``` + "repositories": [ + { + "type": "vcs", + "url": "git@github.com:/" + } + ] +``` -- `npm run dev`: Starts the development server supporting hot module replacement. -- `npm run build`: Builds, minifies and publishes the assets once. Always do this before you commit new code. +If the repository is private you need to have ssh-keys configured for your GitHub account. Run `composer require biigle/:dev-main --ignore-platform-req=ext-ffi` in the biigle root directory to install the module to biigle. Next in `config/app.php` add `Biigle\Modules\\ServiceProvider::class` (Please note: The ServiceProvider class is capitalized.), to the `providers` array below `// Insert Biigle module service providers here.`. Still in the biigle root directory run `php artisan vendor:publish --tag=public`. + +The module is now installed to `biigle/vendor/biigle/`. You can go there and develop your module. + +- `npm run build`: Builds, minifies and publishes the assets once. - `npm run lint`: Run static analysis to check for errors. +Also update this readme for your new module. You should remove the first three subsections and update the installation instructions. And don't forget to change the author and github adress in `composer.json` as well. + ## Installation Note that you have to replace `biigle/module` with the actual name of your module/repository. diff --git a/changeModuleName.py b/changeModuleName.py new file mode 100644 index 0000000..5c2d9de --- /dev/null +++ b/changeModuleName.py @@ -0,0 +1,180 @@ +#!/usr/bin/env python3 + +from pathlib import Path + + +def replace_text(path: Path, old: str, new: str, expected_count: int = 1) -> bool: + content = path.read_text() + current_count = content.count(old) + + if current_count == 0: + if new in content: + return False + raise ValueError(f"Did not find expected text in {path}: {old}") + + if current_count != expected_count: + raise ValueError( + f"Expected {expected_count} occurrence(s) in {path}, found {current_count}: {old}" + ) + + updated = content.replace(old, new) + path.write_text(updated) + return True + + +def replace_namespace_prefix_in_php_files(root: Path, module: str) -> set[Path]: + old_prefix = "Biigle\\Modules\\Module" + new_prefix = f"Biigle\\Modules\\{module}" + changed_files: set[Path] = set() + + for path in root.rglob("*.php"): + content = path.read_text() + if old_prefix not in content: + continue + + path.write_text(content.replace(old_prefix, new_prefix)) + changed_files.add(path) + + return changed_files + + +def replace_text_in_php_files(root: Path, old: str, new: str) -> set[Path]: + changed_files: set[Path] = set() + + for path in root.rglob("*.php"): + content = path.read_text() + if old not in content: + continue + + path.write_text(content.replace(old, new)) + changed_files.add(path) + + return changed_files + + +def rename_service_provider_file(root: Path, module: str) -> tuple[Path, set[Path]]: + old_path = root / "src/ModuleServiceProvider.php" + new_path = root / f"src/{module.capitalize()}ServiceProvider.php" + changed_files: set[Path] = set() + + if old_path.exists() and new_path.exists(): + raise ValueError( + f"Both service provider files exist: {old_path} and {new_path}. Please resolve this manually." + ) + + if old_path.exists(): + old_path.rename(new_path) + changed_files.add(new_path) + + if not new_path.exists(): + raise ValueError( + f"Service provider file not found. Expected one of: {old_path} or {new_path}" + ) + + return new_path, changed_files + + +def rename_service_provider_test_file(root: Path, module: str) -> set[Path]: + old_path = root / "tests/ModuleServiceProviderTest.php" + new_path = root / f"tests/{module}ServiceProviderTest.php" + changed_files: set[Path] = set() + + if old_path.exists() and new_path.exists(): + raise ValueError( + f"Both service provider test files exist: {old_path} and {new_path}. Please resolve this manually." + ) + + if old_path.exists(): + old_path.rename(new_path) + changed_files.add(new_path) + + if not new_path.exists(): + raise ValueError( + f"Service provider test file not found. Expected one of: {old_path} or {new_path}" + ) + + return changed_files + + +def main() -> None: + root = Path(__file__).resolve().parent + module = root.name + module_lower = module.lower() + provider_class = f"{module.capitalize()}ServiceProvider" + service_provider_path, renamed_files = rename_service_provider_file(root, module) + renamed_files.update(rename_service_provider_test_file(root, module)) + + replacements = [ + ( + root / "src/Http/Controllers/QuotesController.php", + "view('module::index')", + f"view('{module}::index')", + 1, + ), + ( + root / "src/resources/views/index.blade.php", + "vendor/biigle/module/hot", + f"vendor/biigle/{module}/hot", + 2, + ), + ( + service_provider_path, + "loadViewsFrom(__DIR__.'/resources/views', 'module');", + f"loadViewsFrom(__DIR__.'/resources/views', '{module}');", + 1, + ), + ( + service_provider_path, + "$modules->register('module', [", + f"$modules->register('{module}', [", + 1, + ), + ( + service_provider_path, + "public_path('vendor/module')", + f"public_path('vendor/{module}')", + 1, + ), + ( + root / "composer.json", + '"name": "biigle/module"', + f'"name": "biigle/{module_lower}"', + 1, + ), + ( + root / "composer.json", + '"Biigle\\\\Modules\\\\Module\\\\": "src"', + f'"Biigle\\\\Modules\\\\{module}\\\\": "src"', + 1, + ), + ( + root / ".github/workflows/test.yml", + "MODULE_NAME: Module", + f"MODULE_NAME: {module}", + 1, + ), + ] + + changed_files = set(renamed_files) + for path, old, new, expected_count in replacements: + changed = replace_text(path, old, new, expected_count) + if changed: + changed_files.add(path) + + changed_files.update(replace_namespace_prefix_in_php_files(root, module)) + changed_files.update( + replace_text_in_php_files(root, "ModuleServiceProvider", provider_class) + ) + if replace_text( + root / "package.json", "ModuleServiceProvider", provider_class, expected_count=1 + ): + changed_files.add(root / "package.json") + + if changed_files: + print(f"Updated module name to '{module}' in {len(changed_files)} file(s).") + else: + print(f"No changes needed. Module name already set to '{module}'.") + + +if __name__ == "__main__": + main()