From 34a2d9552434bc6ca25f48f80e0952e9f3cb6fac Mon Sep 17 00:00:00 2001 From: Bruno Postle Date: Sat, 18 Jan 2025 10:22:45 +0000 Subject: [PATCH] Bonsai offers to install Git and ifcmerge (#5961) The ZIP installer now contains a `libs/bin` folder that contains `ifcmerge`. Bonsai on `register()` adds this folder to the system `PATH` for the current Blender session. Advantage of this is that we don't have to fiddle with the registry on windows, or install files to `~/.local/bin` on Linux, it _should_ work on Darwin, updates are automatic, and we have a mechanism to ship other executables if required. (Note that ifcmerge.exe increases the size of the Windows Bonsai ZIP download by about 7MB) If Git isn't installed on Windows, the Git panel now offers to install it from the Windows Package Manager Community Repository using `winget`. --- src/bonsai/Makefile | 11 ++++++++++ src/bonsai/bonsai/bim/__init__.py | 4 ++++ .../bonsai/bim/module/ifcgit/__init__.py | 1 + .../bonsai/bim/module/ifcgit/operator.py | 22 +++++++++++++++++++ src/bonsai/bonsai/bim/module/ifcgit/ui.py | 7 ++++++ src/bonsai/bonsai/core/ifcgit.py | 8 +++++++ src/bonsai/bonsai/tool/blender.py | 8 +++++++ src/bonsai/bonsai/tool/ifcgit.py | 12 ++++++++++ 8 files changed, 73 insertions(+) diff --git a/src/bonsai/Makefile b/src/bonsai/Makefile index 467477dadc..e3eca3c32d 100644 --- a/src/bonsai/Makefile +++ b/src/bonsai/Makefile @@ -229,6 +229,17 @@ endif # Required for Desktop icon and file association cp -r bonsai/libs/desktop build/bonsai/libs/ + # folder for executable files + mkdir -p build/bonsai/libs/bin + + # required for three-way git merging +ifeq ($(PLATFORM), win) + cd build/bonsai/libs/bin && wget https://github.com/brunopostle/ifcmerge/releases/download/2024-06-24/ifcmerge.zip + cd build/bonsai/libs/bin && unzip ifcmerge.zip && rm ifcmerge.zip +else + cd build/bonsai/libs/bin && wget https://raw.githubusercontent.com/brunopostle/ifcmerge/main/ifcmerge && chmod +x ifcmerge +endif + # Generate translations module for Bonsai build git clone https://github.com/IfcOpenShell/bonsai-translations.git build/working $(PYTHON) scripts/bonsai_translations.py -i "build/working" -o "build/bonsai" diff --git a/src/bonsai/bonsai/bim/__init__.py b/src/bonsai/bonsai/bim/__init__.py index 269d0c0ed1..b2f5c9b288 100644 --- a/src/bonsai/bonsai/bim/__init__.py +++ b/src/bonsai/bonsai/bim/__init__.py @@ -259,6 +259,10 @@ def register(): icons = icon_preview bpy.app.translations.register("bonsai", translations_dict) + import bonsai.tool as tool + + tool.Blender.ensure_bin_in_path() + def unregister(): global icons diff --git a/src/bonsai/bonsai/bim/module/ifcgit/__init__.py b/src/bonsai/bonsai/bim/module/ifcgit/__init__.py index 27352bbc3e..d48f6e285d 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/__init__.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/__init__.py @@ -37,6 +37,7 @@ classes = ( operator.Push, operator.RefreshGit, operator.SwitchRevision, + operator.InstallGit, prop.IfcGitTag, prop.IfcGitListItem, prop.IfcGitProperties, diff --git a/src/bonsai/bonsai/bim/module/ifcgit/operator.py b/src/bonsai/bonsai/bim/module/ifcgit/operator.py index b059c71b2d..bbb7133122 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/operator.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/operator.py @@ -386,3 +386,25 @@ class ObjectLog(bpy.types.Operator): step_id = context.active_object.BIMObjectProperties.ifc_definition_id core.entity_log(tool.IfcGit, tool.Ifc, step_id, self) return {"FINISHED"} + + +class InstallGit(bpy.types.Operator): + """Install Git Version Control System from the + Windows Package Manager Community Repository, + requires restarting Blender after installation""" + + bl_label = "Install Git" + bl_idname = "ifcgit.install_git" + bl_options = {"REGISTER"} + + @classmethod + def poll(cls, context): + IfcGitData.make_sure_is_loaded() + if IfcGitData.data["git_exe"]: + return False + return True + + def execute(self, context): + core.install_git(tool.IfcGit, self) + refresh() + return {"FINISHED"} diff --git a/src/bonsai/bonsai/bim/module/ifcgit/ui.py b/src/bonsai/bonsai/bim/module/ifcgit/ui.py index 860e4325d3..b8d288c77b 100644 --- a/src/bonsai/bonsai/bim/module/ifcgit/ui.py +++ b/src/bonsai/bonsai/bim/module/ifcgit/ui.py @@ -1,6 +1,7 @@ import bpy import time import os +import platform from bonsai.bim.module.ifcgit.data import IfcGitData @@ -26,6 +27,12 @@ class IFCGIT_PT_panel(bpy.types.Panel): if not IfcGitData.data["git_exe"]: row = layout.row() row.label(text="Git is not installed", icon="ERROR") + if platform.system() == "Windows": + row.operator( + "ifcgit.install_git", + text="Install Git", + icon="PACKAGE", + ) return props = context.scene.IfcGitProperties diff --git a/src/bonsai/bonsai/core/ifcgit.py b/src/bonsai/bonsai/core/ifcgit.py index 17dde89fad..731e3751f2 100644 --- a/src/bonsai/bonsai/core/ifcgit.py +++ b/src/bonsai/bonsai/core/ifcgit.py @@ -17,6 +17,7 @@ # along with Bonsai. If not, see . from __future__ import annotations +import platform from typing import TYPE_CHECKING, Optional if TYPE_CHECKING: @@ -130,3 +131,10 @@ def entity_log(ifcgit: tool.IfcGit, ifc: tool.Ifc, step_id: int, operator: bpy.t log_text = ifcgit.entity_log(path_ifc, step_id) # ERROR is only way to display a multi-line message operator.report({"ERROR"}, log_text) + + +def install_git(ifcgit: tool.IfcGit, operator: bpy.types.Operator) -> None: + if platform.system() == "Windows": + ifcgit.install_git_windows(operator=operator) + else: + print("install_git() not implemented") diff --git a/src/bonsai/bonsai/tool/blender.py b/src/bonsai/bonsai/tool/blender.py index 470874a2cb..c55bf08f18 100644 --- a/src/bonsai/bonsai/tool/blender.py +++ b/src/bonsai/bonsai/tool/blender.py @@ -430,6 +430,14 @@ class Blender(bonsai.core.tool.Blender): return blender_path return bpy.path.abspath("//") / blender_path + @classmethod + def ensure_bin_in_path(cls) -> None: + """Check 'bin' folder is in PATH, if not add for this session""" + bin_dir = str(Path(__file__).parent.parent.resolve() / "libs" / "bin") + current_path = os.environ["PATH"] + if bin_dir not in current_path: + os.environ["PATH"] = current_path + os.pathsep + bin_dir + @classmethod def get_default_selection_keypmap(cls) -> tuple: """keymap to replicate default blender selection behaviour with click and box selection""" diff --git a/src/bonsai/bonsai/tool/ifcgit.py b/src/bonsai/bonsai/tool/ifcgit.py index 0fcc9abc3b..5b0f9b88e5 100644 --- a/src/bonsai/bonsai/tool/ifcgit.py +++ b/src/bonsai/bonsai/tool/ifcgit.py @@ -19,6 +19,7 @@ from __future__ import annotations import os import re +import subprocess import bpy import logging from bonsai.bim import import_ifc @@ -546,6 +547,17 @@ class IfcGit: logtext = "No Git history found :(" return logtext + @classmethod + def install_git_windows(cls, operator: bpy.types.Operator) -> None: + """Command to install Git on Windows using winget""" + command = ["winget", "install", "--id", "Git.Git", "-e", "--source", "winget"] + try: + subprocess.run(command, check=True, stdout=subprocess.PIPE, stderr=subprocess.PIPE) + except subprocess.CalledProcessError as e: + operator.report({"ERROR"}, f"Called Process Error occurred: {e}") + except FileNotFoundError: + operator.report({"ERROR"}, "Winget is not available. Make sure Windows Package Manager is installed.") + class IfcGitRepo: repo: git.Repo = None