Giter Club home page Giter Club logo

gd-plug's Introduction

Icon gd-plug

Minimal plugin manager for Godot, inspired by vim-plug

Demo

This version is only compatible with Godot 4.x, check out godot3 branch for older version

Content

Features

  • Minimal
    • No dependencies other than Godot and git
  • Self-contained
  • Zero learning curve
    • Config file written in GDScript
  • Flexible
  • Blazingly fast
    • Parallel download/installation
  • Version freeze
    • Freeze plugins by branch, tag or commit
  • Reduce remote repository size
    • Dependencies can now be installed with just a single-line shell command
  • Clean uninstall
    • Remove plugin *.import files or import resources from /.import on uninstall

Commands

godot --headless -s plug.gd {action} {options...}

Actions:

Action Description
init Initialize current project by creating plug.gd at root
status Check the status of plugins(installed, added or removed), execute this command whenever in doubts
install alias update Install or update(to latest version if not freezed) "plugged" plugins based on plug.gd, or uninstall "unplugged" plugins
uninstall Uninstall all plugins, regardless of plug.gd
clean Clean unused files/folders from /.plugged
upgrade Upgrade addons/gd-plug/plug.gd to the latest version
version Print current version of gd-plug
help Show help

Options:

Option Description
production Install only plugins not marked as dev, or uninstall already installed dev plugins
test Testing mode, no files will be installed/uninstalled while files to be installed/uninstalled will be printed, mainly used with install or uninstall
force Force gd-plug to overwrite destination files when running install command. By default, gd-plug will terminate installation of plugin when any of the file found to be overwriting user files.
keep-import-file Keep ".import" files generated by plugin, when run uninstall command
keep-import-resource-file Keep files located in /.import that generated by plugin, when run uninstall command
debug alias d Print debug message
detail Print with datetime and log level, "[{time}] [{level}] {msg}"
quiet alias q, silent Disable logging

Configs

plug(src, args={})

Source:

  • Github repo: "username/repo", for example, "imjp94/gd-plug"

or

  • Any valid git url

Note

To install plugin from private repository, full Git SSH url must be used: plug("[email protected]:username/repo.git"). See Generating new SSH key and Adding a new SSH key to your Github account

Arguments:

Argument Type Description
include Array[String] Files or directories to include, only addons/ will be included if omitted. (No expression like wildcard(*) supported yet.)
exclude Array[String] Files or directories to exclude. (No expression like wildcard(*) supported yet.)
branch String Name of branch to freeze to
tag String Name of tag to freeze to
commit String Commit hash string to freeze to, must be full length 40 digits commit-hash, for example, 7a642f90d3fb88976dd913051de994e58e838d1a.
dev bool Toggle dev  mode. dev plugins will not be installed when install with production option(dev plugins will be removed if already installed)
on_updated String Post update hook, a function name declared in plug.gd that will be called whenever the plugin installed/updated. See Post Update Hook

Installation

  • gd-plug

    Install directly from Godot Asset Library

    or

    Download this repository, move addons to your {project_dir}

  • git

    Download and install git

    git must be set to environment variable

  • gd-plug-ui(optional)

    User interface for gd-plug, not required when working with commandline

Getting Started

  1. Create plug.gd at your project root, as below:
extends "res://addons/gd-plug/plug.gd"

func _plugging():
    # Declare your plugins in here with plug(src, args)
    pass

Tip

Or run init command: godot --headless -s addons/plug.gd init

  1. Declare plugins in plug.gd under _plugging(), for example:
extends "res://addons/gd-plug/plug.gd"

func _plugging():
    # Declare your plugins in here with plug(src, args)
    # By default, only "addons/" directory will be installed
    # Proudly made by me too!
    plug("imjp94/UIDesignTool")
    plug("imjp94/gd-YAFSM")

    # Tools
    plug("fenix-hub/godot-engine.github-integration")
    plug("EricEzaM/godot-color-palette")

    # Unit test
    plug("bitwes/Gut")

    # By Zylann
    plug("Zylann/godot_scatter_plugin")
    plug("Zylann/godot_heightmap_plugin")
  1. Finally, run install command in shell
# --headless is optional to disable godot window
godot --headless -s plug.gd install 

Post Update Hook

Post update hook can be connected in 3 ways:

  • Declare in plug() argument
    • plug(src, {"on_updated": "post_update"})
  • Connect to update signal
    • connect("updated", self, "post_update")
  • Overwriting _on_updated function
    • func _on_update(plugin)

Post update hook always returned with one argument - dictionary that store information about the plugin

func post_update(plugin):
    print("%s updated" % plugin.name)

Version Control

What can be ignored from version control system?

  • /.plugged: Plugins' git repo are cloned to this directory
  • /addons: Based on preference, but now dependencies can be safely ignored since it will be managed by gd-plug

What should be added to version control system?

  • plug.gd: Dependency config file
  • addons/gd-plug/plug.gd: gd-plug's core

More Examples

For most of the case, a plugin can be "plugged" without any extra configuration, as by default gd-plug will extract whatever located in plugin's /addons directory and install to current project's /addons directory.

However, some plugin's repository may be structured differently, thus, different configuration needed.

  • Addon files are not located in /addons directory, for example, Master-J/DecalCo
    • Explicitly include the directory to install:

      plug("Master-J/DecalCo", {"include": ["decalco/"]})

  • Repository contains only addons files, for example, HungryProton/scatter
    • Change install_root to destination directory and include all files: plug("HungryProton/scatter", {"install_root": "addons/scatter", "include": ["."]})
extends "res://addons/gd-plug/plug.gd"

func _plugging():
    # Declare your plugins in here with plug(src, args)
    # By default, only "addons/" directory will be installed
    # Version freeze
    plug("imjp94/UIDesignTool", {"branch": "demo"}) # Always pull update from "demo" branch
    plug("imjp94/gd-YAFSM", {"tag": "1.0.0-stable"}) # Freeze to stable version
    plug("imjp94/gd-plug", {"commit": "7a642f90d3fb88976dd913051de994e58e838d1a"}) # Must be full length 40 digits commit-hash

    # Dev plugins, can be excluded or uninstalled with "production" command
    plug("fenix-hub/godot-engine.github-integration", {"dev": true})
    plug("EricEzaM/godot-color-palette", {"dev": true})

    # Post update hook
    plug("bitwes/Gut", {"on_updated": "_on_GUT_updated"})

    # It works even if the files are not located in "addons/"
    plug("Master-J/DecalCo", {"include": ["decalco/"]})

    # Repo that contains only addon files
    plug("HungryProton/scatter", {"install_root": "addons/scatter", "include": ["."]})

    # Source other than github
    plug("https://gitlab.com/Xecestel/sound-manager") # Gitlab
    plug("file:///D/Godot/local-project/.git") # Local git repo

    connect("updated", "_on_plugin_updated")

func _on_updated(plugin):
    # Override to catch all updated plugins
    match plugin.name:
        "imjp94/gd-plug":
          print("Use upgrade command!")

func _on_GUT_updated(plugin):
    print("%s updated" % plugin.name)
    print("Installed files: " + plugin.dest_files)

func _on_plugin_updated(plugin):
    # Catch all updated plugins with signal
    print("%s post update hook with signal" % plugin.name)

Known Limitations

  • Godot always show errors from plugin repositories in ./plugged even with .gdignore (See #32)
  • Sometimes random .import files will be unable to remove, while the reason behind it still remains unknown.
    • Workaround: Read output for those .import files that failed to remove, and manually delete them.
  • autoload script references in project setting are not cleared as plugin uninstalled.
    • Workaround: Manually remove autoload scripts from project setting
  • Binary files in used by Godot can't be removed by gd-plug, when plugin uninstalled with editor opened. For example, Zylann/godot_heightmap_plugin's GDNative binaries.
    • Workaround: Quit editor, then execute clean command or manually remove plugin folder from /.plugged

gd-plug's People

Contributors

difint avatar imjp94 avatar lihop avatar martinejaw avatar nathanlovato avatar univeous avatar xphere avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar

gd-plug's Issues

Warning: Thread destroyed without wait_to_finish() called

Another small issue I'm facing is this warning message being printed after running gd-plug:

WARNING: A Thread object has been destroyed without wait_to_finish() having been called on it. Please do so to ensure correct cleanup of the thread.
     at: ~Thread (core/os/thread.cpp:116)

While the plugin seems to work fine, it would be nice to not see this warning message every time I run it.

Simply adding a call to threadpool._flush_threads() in _finalize() resolves the warning. However, I notice that _flush_threads() is a private method, so I'm not sure if this is how you want to fix it.

diff --git a/addons/gd-plug/plug.gd b/addons/gd-plug/plug.gd
index 088a203..a90d47f 100644
--- a/addons/gd-plug/plug.gd
+++ b/addons/gd-plug/plug.gd
@@ -94,6 +94,7 @@ func _idle(delta):
 
 func _finalize():
        _plug_end()
+       threadpool._flush_threads()
        logger.info("Finished, elapsed %.3fs" % ((Time.get_ticks_msec() - _start_time) / 1000.0))
 
 func _on_updated(plugin):

Again, thanks for your hard work on this fantastic plugin!

Inner class name conflict with other plugins

After installing and enabling the ThreadPool plugin with the following plug.gd:

extends "res://addons/gd-plug/plug.gd"

func _plugging():
	plug("zmarcos/godothreadpool")

Future invocations of the plug.gd script fail with the error messages:

ERROR: get_global_class_path: Condition "!global_classes.has(p_class)" is true. Returned: String()
   At: core/script_language.cpp:239.
SCRIPT ERROR: GDScript::reload: Parse Error: Can't override name of the unique global class "ThreadPool". It already exists at: 
   At: res://addons/gd-plug/plug.gd:832.
ERROR: reload: Method failed. Returning: ERR_PARSE_ERROR
   At: modules/gdscript/gdscript.cpp:583.
SCRIPT ERROR: GDScript::reload: Parse Error: Script isn't fully loaded (cyclic preload?): res://addons/gd-plug/plug.gd
   At: res://plug.gd:1.
ERROR: reload: Method failed. Returning: ERR_PARSE_ERROR
   At: modules/gdscript/gdscript.cpp:583.

This is because the ThreadPool plugin declares the unique global class here:

class_name ThreadPool

which conflicts with gd-plug's inner class:

class ThreadPool extends Reference:

I can also see the potential for conflict with gd-plug's other inner classes. Logger with some logging related plugin, and (maybe less likely) GitExecutable with some git related plugin.

More widely, this is related to github proposal #1566 Implement namespaces to avoid collisions in third-party add-ons. A suggested workaround there is to add a prefix to class names.

Some ideas (assuming these inner classes are for private use only and not part of the pubilc gd-plug API):

  • Add a plugin-specific prefix like "GDPlug" to the class names:
    ThreadPool -> GDPlugThreadPool
    Logger -> GDPlugLogger
    GitExecutable -> GDPlugGitExecutable
    This is a little ugly but least-likely to conflict with other plugins.
  • Add an underscore prefix:
    ThreadPool -> _ThreadPool
    Logger -> _Logger
    GitExecutable -> _GitExecutable
    This marks the inner classes as private and keeps the pretty names. It shouldn't be too likely that these conflict with other plugins, but who knows. Perhaps another plugin developer might declare class_name _Logger or class_name _ThreadPool for whatever reason.

Godot LSP reports errors on all .plugged repo files

Hey there! Thanks for this plugin, it saves a bunch of time while adding and updating external plugins!

Since adding gd-plug, i've noticed that godot lsp happily traverses into the .plugged directory and reports errors from all the cloned addon repos - is there any way to prevent this or some workaround that you're using to avoid seeing all the extra errors for files that aren't used by your game?

Or maybe this is just a problem on my end? I'm using emacs as an external editor and connecting to lsp in the running godot editor instance - maybe it's a problem in my setup.

It feels like godot lsp should ignore files in a folder with .gdignore , but for now this seems like a rare issue - not sure if anyone else is experiencing it.

Thanks for any help or insight!

Stuck when running any command

After I updated to 0.2.3 from 0.2.1 using upgrade, running any command will get stuck.

If I remove all the additions related to threadpool.active, it works just as it did before.

This also happens in a nearly empty project.

Godot version: v4.1 dev4 and v4.1 beta1

Fails to install when there are spaces in project directory name

Platform/versions: Linux, Godot 3.3.2-stable, gd-plug 0.1.1
Steps to reproduce:

  1. Create new project with the default name "New Game Project"
    2021-07-16-185130_498x162_scrot
  2. Install gd-plug from Asset Lib.
  3. Add a plug.gd file with some plugin. For example:
extends "res://addons/gd-plug/plug.gd"

func _plugging():
	plug("bitwes/Gut")
  1. Run install command:
godot --no-window -s plug.gd install

Expected: Install plugins.
Actual: Fails with:

Failed to download Gut
Failed to install plugin Gut with error code 1

Output when running install command with debug logging:

Godot Engine v3.3.2.stable.custom_build - https://godotengine.org
WARNING: initialize: XOpenIM failed
   At: platform/x11/os_x11.cpp:200.
WARNING: initialize: XCreateIC couldn't create xic
   At: platform/x11/os_x11.cpp:508.
OpenGL ES 3.0 Renderer: NVIDIA GeForce GTX 1060/PCIe/SSE2
OpenGL ES Batching: ON
 
cmdline_args: [install, debug]
Plug start
Installation config loaded
Plug: {branch:, commit:, dev:False, exclude:[...], include:[], install_root:, name:Gut, on_updated:, plug_dir:res://.plugged/Gut, tag:, url:https://git::@github.com/bitwes/Gut.git}
Installing...
Execute task [SceneTree:1180].install_plugin() 
Request quit declined, threadpool is still running
Installing plugin Gut...
Downloading Gut from https://git::@github.com/bitwes/Gut.git...
Cloning from https://git::@github.com/bitwes/Gut.git to /home/leroy/New Game Project/.plugged/Gut...
Execute "cd /home/leroy/New Game Project/.plugged/Gut && git clone --depth=1 --progress https://git::@github.com/bitwes/Gut.git /home/leroy/New Game Project/.plugged/Gut"
Execution ended(code:1): []
Failed to clone from https://git::@github.com/bitwes/Gut.git
Failed to download Gut
Failed to install plugin Gut with error code 1
Execution finished [SceneTree:1180].install_plugin() 
All thread finished
Plugged config saved
Finished, elapsed 0.163s

Cause: I think it is caused by the spaces in the directory name which are not escaped escaped with "\". Example:

[leroy@laptop ~]$ cd /home/leroy/New Game Project/
bash: cd: too many arguments
[leroy@laptop ~]$ echo $?
1
[leroy@laptop ~]$ cd /home/leroy/New\ Game\ Project/
[leroy@laptop New Game Project]$ echo $?
0

Generally, I never create directories with spaces because of issues like these, but in this case I used the default project name as I wanted to quickly test something and stumbled upon this error.

Unexpected OS: Linux

Thank you for porting this incredibly useful plugin to Godot 4. When I run the Godot 4 version on Linux, I get the error "Unexpected OS: Linux".

Comparing the 3.5 docs to the 4.0 docs shows that the OS.get_name() values for the Unix-like OSes have changed from "OSX", "X11", "Server" to "macOS", "Linux", "FreeBSD", "NetBSD", "OpenBSD", "BSD".

Updating these names in the following line resolves the issue.

"X11", "OSX", "Server":

GDExtentions which needs compilation is ignored by GDPlug

Hello there!

I really liked your work on this and It makes creating complex projects, especially those which uses the REUSE spec easier.

But an problem that I found while incorporating your plugin into my project is that when downloading GDExtentions/GDNative plugins which doesn't have the plugin files already pre-compiled within the repository refuses to download anything.

Possible workarounds

  • Attempt to use the GitHub Releases API to download the plugins from the releases section(where most likely the bins are precompiled there and ready to use). The possible solution
  • Attempt to compile the plugin using cmake or an compilation script within the repo. Most likely the hardest to achieve
  • Warn the user that GDExtentions which requires compilation after cloning would be ignored. Easiest, Documented But defeats the purpose of being able to download all kinds of plugins TBH...

Affected Plugins: Godot Jolt
Godot Version: 4.1.1 Stable

Add compressed file support

I tried to install godot-box2d but it need to be installed with the zip file from the release section.
Adding support for http and local zip file will fix that.
Thanks

Autoload script not removed

autoload script references in project setting are not cleared as plugin uninstalled.
Workaround: Manually remove autoload scripts from project setting

Can elaborate what does this mean?
So if a plugin has an autoload, that autoload is not removed on uninstalling the plugin?

Non root addons dont install correctly

Non root addons dont install correctly like godot-box2d
It will not install in addons but in the project root.
It kind of work with {"install_root": "addons/godot-box2d"}
but it is installed in addons/godot-box2d/bin/addons/godot-box2d

Edit: I found that box2D need to be install via the zip file in the release section so this issue do not apply anymore.
See #31 for the issue for box2D

private repository support

Even if I configured GitHub credentials, I cannot use this plugin to manage private repositories.
logs:

cmdline_args: ["install", "debug"]
Plug start
Installation config loaded
Plug: { "name": "*****", "url": "https://git::@github.com/*****/*****.git", "plug_dir": "res://.plugged/*****", "include": [], "exclude": [], "branch": "", "tag": "", "commit": "", "dev": false, "on_updated": "", "install_root": "" }
Installing...
Request quit declined, threadpool is still running
Execute task <SceneTree#24830280835>.install_plugin()
Installing plugin *****...
Downloading ***** from https://git::@github.com/*****/*****.git...
Cloning from https://git::@github.com/*****/*****.git to C:/*****...
Execute "cd "C:/*****" && git clone --depth=1 --progress "https://git::@github.com/*****/*****.git" "C:/*****" 2> nul"
Execution ended(code:128): [""]
Failed to clone from https://git::@github.com/*****/*****.git
Failed to download *****
Failed to install plugin ***** with error code 128
Execution finished <SceneTree#24830280835>.install_plugin()
All thread finished

If I run git clone directly in cmd, there is no problem.

Files outside `addons/` are attemping to be downloaded by default.

When I run godot --headless -s plug.gd install, gdplug is attempting to download files outside the addons/ directory of various repos when I am not including the "include" setting, and when I am attempting to manually set it to "addons/". I have changed the default plugged directory to res://addons/gd-plug/.plugged as mentioned in other issues but otherwise my gd-plug setup is pretty basic. Here are some examples of the behavior I am seeing.

# res://plug.gd
func _plugging() -> void:
	# attempts to overwrite `res://.git/*`, `res://.gitignore`, `res://icon.svg`, etc.
	plug("heygleeson/godot-ldtk-importer") 

	# attempts to overwrite the same files as above
	plug("godot-extended-libraries/godot-antialiased-line2d", {"include": ["addons"]})

	# this one correctly installs only files in the path provided
	plug("kenyoni-software/godot-addons", {"dev": true, "include": ["addons/icon_explorer"]}) 

Fortunately it runs in safe mode by default I am just getting spammed with warnings but with --force passed it breaks the project repo since all the git files get replaced.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.