From ca32ce36f32b8ae25f64e26f23199cff4ac1fdd1 Mon Sep 17 00:00:00 2001 From: Emil Ernerfeldt Date: Tue, 16 Apr 2024 16:29:56 +0200 Subject: [PATCH] Add script to update or init a repository --- .gitignore | 1 + LICENSE-MIT | 2 +- README.md | 10 ++- scripts/template_update.py | 162 +++++++++++++++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 2 deletions(-) create mode 100755 scripts/template_update.py diff --git a/.gitignore b/.gitignore index e27b6cd..8cb048a 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ build # Pixi environment .pixi +.ruff_cache diff --git a/LICENSE-MIT b/LICENSE-MIT index 79a5d00..3f6d1ed 100644 --- a/LICENSE-MIT +++ b/LICENSE-MIT @@ -1,4 +1,4 @@ -Copyright (c) 2022 Rerun Technologies AB +Copyright (c) 2024 Rerun Technologies AB Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated diff --git a/README.md b/README.md index 71152d3..0bbbd65 100644 --- a/README.md +++ b/README.md @@ -25,4 +25,12 @@ This template includes ## How to use Start by clicking "Use this template" at https://github.com/rerun-io/rerun_template/ or follow [these instructions](https://docs.github.com/en/free-pro-team@latest/github/creating-cloning-and-archiving-repositories/creating-a-repository-from-a-template). -Then search for `TODO` and fill in all those places (including changing the whole contents of this `README.md`, obviously). +Then follow these steps: +* Search and replace `rerun_template` with the name of the repository +* Run `scripts/template_update.py init --languages cpp,rust,python` to delete files you don't need (give the languages you need support for) +* Search for `TODO` and fill in all those places +* Replace this `README.md` with something better +* Commit! + +In the future you can always update this repository with the latest changes from the template by running: +* `scripts/template_update.py update --languages cpp,rust,python` diff --git a/scripts/template_update.py b/scripts/template_update.py new file mode 100755 index 0000000..c437c82 --- /dev/null +++ b/scripts/template_update.py @@ -0,0 +1,162 @@ +#!/usr/bin/env python3 + +""" +The script has two purposes. + +After using `rerun_template` as a template, run this to clean out things you don't need. +Use `scripts/template_update.py init --languages cpp,rust,python` for this. + +Update an existing repository with the latest changes from the template. +Use `scripts/template_update.py update --languages cpp,rust,python` for this. + +In either case, make sure the list of languages matches the languages you want to support. +You can also use `--dry-run` to see what would happen without actually changing anything. +""" + +import argparse +import os +import shutil +import tempfile +from typing import Set + +from git import Repo + +OWNER = "rerun-io" + +# Files requires by C++, but not by both Python or Rust. +CPP_FILES = { + ".clang-format", + ".github/workflows/cpp.yml", + "CMakeLists.txt", + "pixi.lock", # Not needed by Rust + "pixi.toml", # Not needed by Rust + "src/main.cpp", + "src/", +} + +# Files requires by Python, but not by both C++ or Rust +PYTHON_FILES = { + ".github/workflows/python.yml", + ".mypy.ini", + "pixi.lock", # Not needed by Rust + "pixi.toml", # Not needed by Rust + "pyproject.toml", +} + +# Files requires by Rust, but not by both C++ or Python +RUST_FILES = { + ".clang-format", + ".github/workflows/rust.yml", + "bacon.toml", + "Cargo.lock", + "Cargo.toml", + "clippy.toml", + "Cranky.toml", + "deny.toml", + "rust-toolchain", + "scripts/clippy_wasm/", + "scripts/clippy_wasm/clippy.toml", + "src/lib.rs", + "src/main.rs", + "src/", +} + + +def parse_languages(lang_str: str) -> Set[str]: + languages = lang_str.split(",") if lang_str else [] + for lang in languages: + assert lang in ["cpp", "python", "rust"], f"Unsupported language: {lang}" + return set(languages) + + +def calc_deny_set(languages: Set[str]) -> Set[str]: + """The set of files to delete/ignore.""" + files_to_delete = CPP_FILES | PYTHON_FILES | RUST_FILES + if "cpp" in languages: + files_to_delete -= CPP_FILES + if "python" in languages: + files_to_delete -= PYTHON_FILES + if "rust" in languages: + files_to_delete -= RUST_FILES + return files_to_delete + + +def init(languages: Set[str], dry_run: bool) -> None: + print("Removing all language-specific files not needed for languages {languages}.") + files_to_delete = calc_deny_set(languages) + delete_files_and_folder(files_to_delete, dry_run) + + +def delete_files_and_folder(paths: Set[str], dry_run: bool) -> None: + repo_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + for path in paths: + full_path = os.path.join(repo_path, path) + if os.path.exists(full_path): + if os.path.isfile(full_path): + print(f"Removing file {full_path}…") + if not dry_run: + os.remove(full_path) + elif os.path.isdir(full_path): + print(f"Removing folder {full_path}…") + if not dry_run: + shutil.rmtree(full_path) + + +def update(languages: Set[str], dry_run: bool) -> None: + files_to_ignore = calc_deny_set(languages) + repo_path = os.path.dirname(os.path.dirname(os.path.realpath(__file__))) + + with tempfile.TemporaryDirectory() as temp_dir: + Repo.clone_from("https://github.com/rerun-io/rerun_template.git", temp_dir) + for root, dirs, files in os.walk(temp_dir): + for file in files: + src_path = os.path.join(root, file) + rel_path = os.path.relpath(src_path, temp_dir) + + if rel_path.startswith(".git/"): + continue + if rel_path.startswith("src/"): + continue + if rel_path == "README.md": + continue + + if rel_path not in files_to_ignore: + dest_path = os.path.join(repo_path, rel_path) + + print(f"Updating {rel_path}…") + if not dry_run: + os.makedirs(os.path.dirname(dest_path), exist_ok=True) + shutil.copy2(src_path, dest_path) + + +def main() -> None: + parser = argparse.ArgumentParser(description="Handle the Rerun template.") + subparsers = parser.add_subparsers(dest="command") + + init_parser = subparsers.add_parser("init", help="Initialize a new checkout of the template.") + init_parser.add_argument( + "--languages", default="", nargs="?", const="", help="The languages to support (e.g. `cpp,python,rust`)." + ) + init_parser.add_argument("--dry-run", action="store_true", help="Don't actually delete any files.") + + update_parser = subparsers.add_parser( + "update", help="Update all existing Rerun repositories with the latest changes from the template" + ) + update_parser.add_argument( + "--languages", default="", nargs="?", const="", help="The languages to support (e.g. `cpp,python,rust`)." + ) + update_parser.add_argument("--dry-run", action="store_true", help="Don't actually delete any files.") + + args = parser.parse_args() + + if args.command == "init": + init(parse_languages(args.languages), args.dry_run) + elif args.command == "update": + update(parse_languages(args.languages), args.dry_run) + else: + parser.print_help() + exit(1) + + +if __name__ == "__main__": + main()