Skip to content

Commit

Permalink
Add script to update or init a repository
Browse files Browse the repository at this point in the history
  • Loading branch information
emilk committed Apr 16, 2024
1 parent ede62b4 commit b8d0c1e
Show file tree
Hide file tree
Showing 3 changed files with 172 additions and 1 deletion.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -14,3 +14,4 @@ build

# Pixi environment
.pixi
.ruff_cache
10 changes: 9 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -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/setup_template.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/setup_template.py update --languages cpp,rust,python`
162 changes: 162 additions & 0 deletions scripts/setup_template.py
Original file line number Diff line number Diff line change
@@ -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/setup_template.py init --languages cpp,rust,python` for this.
Update an existing repository with the latest changes from the template.
Use `scripts/setup_template.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()

0 comments on commit b8d0c1e

Please sign in to comment.