[gnome-builder] vagrant: prototype a vagrant runtime



commit 1752c11bf82ba8a04cd8d2dc36e12336e55a7cb5
Author: Christian Hergert <chergert redhat com>
Date:   Thu May 2 20:56:23 2019 -0700

    vagrant: prototype a vagrant runtime
    
    This still needs work on various fronts, but it's good enough to
    iterate upon now. To make some things we need fast we will need
    to capture some information at load time.
    
    For example, we expect "contains_program_in_path" to be fast, so
    we will want to ls /usr/bin, /bin, etc and cache the result.
    
    We also wont have the ability to pass FDs across the process
    boundary, so we'll have to introduce new API in
    IdeSubprocessLauncher for something like "can_pass_fd". That way
    subsystems like GDB can use alternate strategies to spawn a
    debugger.
    
    Furthermore, we need to extend the build pipeline to sync code
    to the domain. We can look at adding an IDE_BUILD_PHASE_PUSH
    that happens before IDE_BUILD_PHASE_CONFIGURE. A vagrant plugin
    would likely want to rsync code here.
    
    There will be fallout for a number of other things too, such as
    how do we do exports, execution with display, etc.
    
    We might also want to add a Containers project-tree node so that
    some basic management of containers can be done.

 meson_options.txt                                  |   1 +
 po/POTFILES.in                                     |   1 +
 src/plugins/meson.build                            |   2 +
 src/plugins/vagrant/gbp-vagrant-runtime-provider.c | 389 +++++++++++++++++++++
 src/plugins/vagrant/gbp-vagrant-runtime-provider.h |  42 +++
 src/plugins/vagrant/gbp-vagrant-runtime.c          | 231 ++++++++++++
 src/plugins/vagrant/gbp-vagrant-runtime.h          |  39 +++
 .../vagrant/gbp-vagrant-subprocess-launcher.c      | 101 ++++++
 .../vagrant/gbp-vagrant-subprocess-launcher.h      |  34 ++
 src/plugins/vagrant/gbp-vagrant-table.c            | 129 +++++++
 src/plugins/vagrant/gbp-vagrant-table.h            |  46 +++
 src/plugins/vagrant/meson.build                    |  19 +
 src/plugins/vagrant/vagrant-plugin.c               |  36 ++
 src/plugins/vagrant/vagrant.gresource.xml          |   6 +
 src/plugins/vagrant/vagrant.plugin                 |   9 +
 15 files changed, 1085 insertions(+)
---
diff --git a/meson_options.txt b/meson_options.txt
index 3d6fc2e40..1e2c94943 100644
--- a/meson_options.txt
+++ b/meson_options.txt
@@ -70,6 +70,7 @@ option('plugin_sysprof', type: 'boolean')
 option('plugin_sysroot', type: 'boolean')
 option('plugin_todo', type: 'boolean')
 option('plugin_vala', type: 'boolean')
+option('plugin_vagrant', type: 'boolean', value: false)
 option('plugin_valgrind', type: 'boolean')
 option('plugin_waf', type: 'boolean')
 option('plugin_words', type: 'boolean')
diff --git a/po/POTFILES.in b/po/POTFILES.in
index 3e31a080d..6d2dc9600 100644
--- a/po/POTFILES.in
+++ b/po/POTFILES.in
@@ -362,6 +362,7 @@ src/plugins/testui/gbp-test-output-panel.ui
 src/plugins/testui/gbp-test-tree-addin.c
 src/plugins/todo/gbp-todo-panel.c
 src/plugins/todo/gbp-todo-workspace-addin.c
+src/plugins/vagrant/gbp-vagrant-runtime-provider.c
 src/plugins/vala-pack/ide-vala-completion-item.vala
 src/plugins/vala-pack/ide-vala-preferences-addin.vala
 src/plugins/valgrind/gtk/menus.ui
diff --git a/src/plugins/meson.build b/src/plugins/meson.build
index 5e40adc9f..17583bf9e 100644
--- a/src/plugins/meson.build
+++ b/src/plugins/meson.build
@@ -116,6 +116,7 @@ subdir('terminal')
 subdir('testui')
 subdir('todo')
 subdir('trim-spaces')
+subdir('vagrant')
 subdir('valgrind')
 subdir('vcsui')
 subdir('vim')
@@ -183,6 +184,7 @@ status += [
   'Sysroot ............... : @0@'.format(get_option('plugin_sysroot')),
   'Todo .................. : @0@'.format(get_option('plugin_todo')),
   'Vala Pack ............. : @0@'.format(get_option('plugin_vala')),
+  'Vagrant ............... : @0@'.format(get_option('plugin_vagrant')),
   'Valgrind .............. : @0@'.format(get_option('plugin_valgrind')),
   'Waf ................... : @0@'.format(get_option('plugin_waf')),
   'Word Completion ....... : @0@'.format(get_option('plugin_words')),
diff --git a/src/plugins/vagrant/gbp-vagrant-runtime-provider.c 
b/src/plugins/vagrant/gbp-vagrant-runtime-provider.c
new file mode 100644
index 000000000..4e610ea65
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-runtime-provider.c
@@ -0,0 +1,389 @@
+/* gbp-vagrant-runtime-provider.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-vagrant-runtime-provider"
+
+#include "config.h"
+
+#include <glib/gi18n.h>
+
+#include "gbp-vagrant-runtime.h"
+#include "gbp-vagrant-runtime-provider.h"
+
+struct _GbpVagrantRuntimeProvider
+{
+  IdeObject parent_instance;
+};
+
+static const gchar *cmd_vagrant_status[] = { "vagrant", "status", NULL };
+
+static void
+gbp_vagrant_runtime_provider_add (GbpVagrantRuntimeProvider *self,
+                                  GbpVagrantRuntime         *runtime)
+{
+  g_autofree gchar *display_name = NULL;
+  IdeRuntimeManager *runtime_manager;
+  const gchar *provider;
+  const gchar *vagrant_id;
+  IdeContext *context;
+
+  g_assert (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self));
+  g_assert (GBP_IS_VAGRANT_RUNTIME (runtime));
+
+  provider = gbp_vagrant_runtime_get_provider (runtime);
+  vagrant_id = gbp_vagrant_runtime_get_vagrant_id (runtime);
+  display_name = g_strdup_printf ("%s %s (%s)", _("Vagrant"), vagrant_id, provider);
+  ide_runtime_set_display_name (IDE_RUNTIME (runtime), display_name);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  runtime_manager = ide_runtime_manager_from_context (context);
+  ide_object_append (IDE_OBJECT (self), IDE_OBJECT (runtime));
+  ide_runtime_manager_add (runtime_manager, IDE_RUNTIME (runtime));
+}
+
+static void
+vagrant_status_cb (GbpVagrantRuntimeProvider *self,
+                   GAsyncResult              *result,
+                   gpointer                   user_data)
+{
+  g_autoptr(GbpVagrantTable) table = NULL;
+  g_autoptr(GbpVagrantRuntime) runtime = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  GbpVagrantTableIter iter;
+
+  g_assert (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!(table = gbp_vagrant_runtime_provider_command_finish (self, result, &error)))
+    {
+      ide_task_return_error (task, g_steal_pointer (&error));
+      return;
+    }
+
+  gbp_vagrant_table_iter_init (&iter, table);
+
+  while (gbp_vagrant_table_iter_next (&iter))
+    {
+      g_autofree gchar *id = NULL;
+      g_autofree gchar *key = NULL;
+      g_autofree gchar *val1 = NULL;
+
+      id = gbp_vagrant_table_iter_get_column (&iter, 1);
+      key = gbp_vagrant_table_iter_get_column (&iter, 2);
+      val1 = gbp_vagrant_table_iter_get_column (&iter, 3);
+
+      if (ide_str_empty0 (id))
+        continue;
+
+      if (runtime != NULL)
+        {
+          const gchar *prev_id = gbp_vagrant_runtime_get_vagrant_id (runtime);
+
+          if (!ide_str_equal0 (id, prev_id))
+            {
+              gbp_vagrant_runtime_provider_add (self, runtime);
+              g_clear_object (&runtime);
+            }
+        }
+      else
+        {
+          g_autofree gchar *runtime_id = g_strdup_printf ("vagrant:%s", id);
+
+          runtime = g_object_new (GBP_TYPE_VAGRANT_RUNTIME,
+                                  "id", runtime_id,
+                                  "category", _("Vagrant"),
+                                  "name", id,
+                                  "vagrant-id", id,
+                                  NULL);
+        }
+
+      if (ide_str_equal0 (key, "provider-name"))
+        gbp_vagrant_runtime_set_provider (runtime, val1);
+      else if (ide_str_equal0 (key, "state"))
+        gbp_vagrant_runtime_set_state (runtime, val1);
+    }
+
+  if (runtime != NULL)
+    {
+      gbp_vagrant_runtime_provider_add (self, runtime);
+      g_clear_object (&runtime);
+    }
+
+  ide_task_return_boolean (task, TRUE);
+}
+
+static void
+reload_find_in_ancestors_cb (GFile        *workdir,
+                             GAsyncResult *result,
+                             gpointer      user_data)
+{
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) vagrantfile = NULL;
+
+  g_assert (G_IS_FILE (workdir));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!(vagrantfile = ide_g_file_find_in_ancestors_finish (result, &error)))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    gbp_vagrant_runtime_provider_command_async (ide_task_get_source_object (task),
+                                                (const gchar * const *)cmd_vagrant_status,
+                                                ide_task_get_cancellable (task),
+                                                (GAsyncReadyCallback) vagrant_status_cb,
+                                                g_object_ref (task));
+
+
+}
+
+static void
+gbp_vagrant_runtime_provider_reload_async (GbpVagrantRuntimeProvider *self,
+                                           GCancellable              *cancellable,
+                                           GAsyncReadyCallback        callback,
+                                           gpointer                   user_data)
+{
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  IdeContext *context;
+
+  g_return_if_fail (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self));
+  g_return_if_fail (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_vagrant_runtime_provider_reload_async);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  workdir = ide_context_ref_workdir (context);
+
+  ide_g_file_find_in_ancestors_async (workdir,
+                                      "Vagrantfile",
+                                      ide_task_get_cancellable (task),
+                                      (GAsyncReadyCallback) reload_find_in_ancestors_cb,
+                                      g_object_ref (task));
+}
+
+static gboolean
+gbp_vagrant_runtime_provider_reload_finish (GbpVagrantRuntimeProvider  *self,
+                                            GAsyncResult               *result,
+                                            GError                    **error)
+{
+  g_return_val_if_fail (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self), FALSE);
+  g_return_val_if_fail (IDE_IS_TASK (result), FALSE);
+
+  return ide_task_propagate_boolean (IDE_TASK (result), error);
+}
+
+static void
+load_reload_cb (GbpVagrantRuntimeProvider *self,
+                GAsyncResult              *result,
+                gpointer                   user_data)
+{
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_TASK (task));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self));
+
+  if (!gbp_vagrant_runtime_provider_reload_finish (self, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_boolean (task, TRUE);
+}
+
+static void
+check_vagrant_available_cb (IdeSubprocess *subprocess,
+                            GAsyncResult  *result,
+                            gpointer       user_data)
+{
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!ide_subprocess_wait_check_finish (subprocess, result, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    gbp_vagrant_runtime_provider_reload_async (ide_task_get_source_object (task),
+                                               ide_task_get_cancellable (task),
+                                               (GAsyncReadyCallback) load_reload_cb,
+                                               g_object_ref (task));
+
+}
+
+static void
+gbp_vagrant_runtime_provider_load_async (GbpVagrantRuntimeProvider *self,
+                                         GCancellable              *cancellable,
+                                         GAsyncReadyCallback        callback,
+                                         gpointer                   user_data)
+{
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_vagrant_runtime_provider_load_async);
+
+  launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_SILENCE |
+                                          G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+  ide_subprocess_launcher_set_cwd (launcher, g_get_home_dir ());
+  ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
+  ide_subprocess_launcher_push_argv (launcher, "which");
+  ide_subprocess_launcher_push_argv (launcher, "vagrant");
+
+  if (!(subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error)))
+    ide_task_return_error (task, g_steal_pointer ((&error)));
+  else
+    ide_subprocess_wait_check_async (subprocess,
+                                     cancellable,
+                                     (GAsyncReadyCallback) check_vagrant_available_cb,
+                                     g_steal_pointer (&task));
+
+}
+
+static void
+gbp_vagrant_runtime_provider_load (IdeRuntimeProvider *provider,
+                                   IdeRuntimeManager  *runtime_manager)
+{
+  g_assert (IDE_IS_MAIN_THREAD ());
+  g_assert (GBP_IS_VAGRANT_RUNTIME_PROVIDER (provider));
+  g_assert (IDE_IS_RUNTIME_MANAGER (runtime_manager));
+
+  gbp_vagrant_runtime_provider_load_async (GBP_VAGRANT_RUNTIME_PROVIDER (provider),
+                                           NULL, NULL, NULL);
+}
+
+static void
+runtime_provider_iface_init (IdeRuntimeProviderInterface *iface)
+{
+  iface->load = gbp_vagrant_runtime_provider_load;
+}
+
+G_DEFINE_TYPE_WITH_CODE (GbpVagrantRuntimeProvider, gbp_vagrant_runtime_provider, IDE_TYPE_OBJECT,
+                         G_IMPLEMENT_INTERFACE (IDE_TYPE_RUNTIME_PROVIDER, runtime_provider_iface_init))
+
+static void
+gbp_vagrant_runtime_provider_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (gbp_vagrant_runtime_provider_parent_class)->finalize (object);
+}
+
+static void
+gbp_vagrant_runtime_provider_class_init (GbpVagrantRuntimeProviderClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+
+  object_class->finalize = gbp_vagrant_runtime_provider_finalize;
+}
+
+static void
+gbp_vagrant_runtime_provider_init (GbpVagrantRuntimeProvider *self)
+{
+}
+
+static void
+gbp_vagrant_runtime_provider_command_cb (IdeSubprocess *subprocess,
+                                         GAsyncResult  *result,
+                                         gpointer       user_data)
+{
+  g_autofree gchar *stdout_buf = NULL;
+  g_autoptr(IdeTask) task = user_data;
+  g_autoptr(GError) error = NULL;
+
+  g_assert (IDE_IS_SUBPROCESS (subprocess));
+  g_assert (G_IS_ASYNC_RESULT (result));
+  g_assert (IDE_IS_TASK (task));
+
+  if (!ide_subprocess_communicate_utf8_finish (subprocess, result, &stdout_buf, NULL, &error))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_task_return_pointer (task,
+                             gbp_vagrant_table_new_take (g_steal_pointer (&stdout_buf)),
+                             gbp_vagrant_table_free);
+}
+
+void
+gbp_vagrant_runtime_provider_command_async (GbpVagrantRuntimeProvider *self,
+                                            const gchar * const       *command,
+                                            GCancellable              *cancellable,
+                                            GAsyncReadyCallback        callback,
+                                            gpointer                   user_data)
+{
+  g_autoptr(IdeSubprocessLauncher) launcher = NULL;
+  g_autoptr(IdeSubprocess) subprocess = NULL;
+  g_autoptr(IdeTask) task = NULL;
+  g_autoptr(GError) error = NULL;
+  g_autoptr(GFile) workdir = NULL;
+  IdeContext *context;
+
+  g_assert (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self));
+  g_assert (command != NULL && command[0] != NULL);
+  g_assert (g_str_equal ("vagrant", command[0]));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  task = ide_task_new (self, cancellable, callback, user_data);
+  ide_task_set_source_tag (task, gbp_vagrant_runtime_provider_command_async);
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  workdir = ide_context_ref_workdir (context);
+
+  launcher = ide_subprocess_launcher_new (G_SUBPROCESS_FLAGS_STDOUT_PIPE |
+                                          G_SUBPROCESS_FLAGS_STDERR_SILENCE);
+  ide_subprocess_launcher_set_cwd (launcher, g_file_peek_path (workdir));
+
+  ide_subprocess_launcher_push_args (launcher, command);
+  if (!g_strv_contains (command, "--machine-readable"))
+    ide_subprocess_launcher_push_argv (launcher, "--machine-readable");
+
+  if (!(subprocess = ide_subprocess_launcher_spawn (launcher, cancellable, &error)))
+    ide_task_return_error (task, g_steal_pointer (&error));
+  else
+    ide_subprocess_communicate_utf8_async (subprocess,
+                                           NULL,
+                                           cancellable,
+                                           (GAsyncReadyCallback) gbp_vagrant_runtime_provider_command_cb,
+                                           g_steal_pointer (&task));
+}
+
+/**
+ * gbp_vagrant_runtime_provider_command_finish:
+ *
+ * Returns: (transfer full): a #GbpVagrantTable or %NULL
+ */
+GbpVagrantTable *
+gbp_vagrant_runtime_provider_command_finish (GbpVagrantRuntimeProvider  *self,
+                                             GAsyncResult               *result,
+                                             GError                    **error)
+{
+  g_return_val_if_fail (GBP_IS_VAGRANT_RUNTIME_PROVIDER (self), NULL);
+  g_return_val_if_fail (IDE_IS_TASK (result), NULL);
+
+  return ide_task_propagate_pointer (IDE_TASK (result), error);
+}
diff --git a/src/plugins/vagrant/gbp-vagrant-runtime-provider.h 
b/src/plugins/vagrant/gbp-vagrant-runtime-provider.h
new file mode 100644
index 000000000..b1e2bf097
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-runtime-provider.h
@@ -0,0 +1,42 @@
+/* gbp-vagrant-runtime-provider.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+
+#include "gbp-vagrant-table.h"
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_VAGRANT_RUNTIME_PROVIDER (gbp_vagrant_runtime_provider_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpVagrantRuntimeProvider, gbp_vagrant_runtime_provider, GBP, 
VAGRANT_RUNTIME_PROVIDER, IdeObject)
+
+void             gbp_vagrant_runtime_provider_command_async  (GbpVagrantRuntimeProvider  *self,
+                                                              const gchar * const        *command,
+                                                              GCancellable               *cancellable,
+                                                              GAsyncReadyCallback         callback,
+                                                              gpointer                    user_data);
+GbpVagrantTable *gbp_vagrant_runtime_provider_command_finish (GbpVagrantRuntimeProvider  *self,
+                                                              GAsyncResult               *result,
+                                                              GError                    **error);
+
+G_END_DECLS
diff --git a/src/plugins/vagrant/gbp-vagrant-runtime.c b/src/plugins/vagrant/gbp-vagrant-runtime.c
new file mode 100644
index 000000000..2ace23696
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-runtime.c
@@ -0,0 +1,231 @@
+/* gbp-vagrant-runtime.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-vagrant-runtime"
+
+#include "config.h"
+
+#include "gbp-vagrant-runtime.h"
+#include "gbp-vagrant-subprocess-launcher.h"
+
+struct _GbpVagrantRuntime
+{
+  IdeRuntime parent_instance;
+  gchar *vagrant_id;
+  gchar *provider;
+  gchar *state;
+};
+
+enum {
+  PROP_0,
+  PROP_PROVIDER,
+  PROP_STATE,
+  PROP_VAGRANT_ID,
+  N_PROPS
+};
+
+G_DEFINE_TYPE (GbpVagrantRuntime, gbp_vagrant_runtime, IDE_TYPE_RUNTIME)
+
+static GParamSpec *properties [N_PROPS];
+
+static IdeSubprocessLauncher *
+gbp_vagrant_runtime_create_launcher (IdeRuntime  *runtime,
+                                     GError     **error)
+{
+  GbpVagrantRuntime *self = (GbpVagrantRuntime *)runtime;
+  IdeSubprocessLauncher *launcher;
+  g_autoptr(GFile) workdir = NULL;
+  IdeContext *context;
+
+  g_assert (GBP_IS_VAGRANT_RUNTIME (self));
+
+  context = ide_object_get_context (IDE_OBJECT (self));
+  workdir = ide_context_ref_workdir (context);
+
+  launcher = gbp_vagrant_subprocess_launcher_new (g_file_peek_path (workdir));
+
+  ide_subprocess_launcher_set_run_on_host (launcher, TRUE);
+
+  ide_subprocess_launcher_push_argv (launcher, "vagrant");
+  ide_subprocess_launcher_push_argv (launcher, "ssh");
+  ide_subprocess_launcher_push_argv (launcher, self->vagrant_id);
+  ide_subprocess_launcher_push_argv (launcher, GBP_VAGRANT_SUBPROCESS_LAUNCHER_C_OPT);
+
+  return g_steal_pointer (&launcher);
+}
+
+static void
+gbp_vagrant_runtime_finalize (GObject *object)
+{
+  GbpVagrantRuntime *self = (GbpVagrantRuntime *)object;
+
+  g_clear_pointer (&self->provider, g_free);
+  g_clear_pointer (&self->state, g_free);
+  g_clear_pointer (&self->vagrant_id, g_free);
+
+  G_OBJECT_CLASS (gbp_vagrant_runtime_parent_class)->finalize (object);
+}
+
+static void
+gbp_vagrant_runtime_get_property (GObject    *object,
+                                  guint       prop_id,
+                                  GValue     *value,
+                                  GParamSpec *pspec)
+{
+  GbpVagrantRuntime *self = GBP_VAGRANT_RUNTIME (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROVIDER:
+      g_value_set_string (value, gbp_vagrant_runtime_get_provider (self));
+      break;
+
+    case PROP_STATE:
+      g_value_set_string (value, gbp_vagrant_runtime_get_state (self));
+      break;
+
+    case PROP_VAGRANT_ID:
+      g_value_set_string (value, gbp_vagrant_runtime_get_vagrant_id (self));
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_vagrant_runtime_set_property (GObject      *object,
+                                  guint         prop_id,
+                                  const GValue *value,
+                                  GParamSpec   *pspec)
+{
+  GbpVagrantRuntime *self = GBP_VAGRANT_RUNTIME (object);
+
+  switch (prop_id)
+    {
+    case PROP_PROVIDER:
+      gbp_vagrant_runtime_set_provider (self, g_value_get_string (value));
+      break;
+
+    case PROP_STATE:
+      gbp_vagrant_runtime_set_state (self, g_value_get_string (value));
+      break;
+
+    case PROP_VAGRANT_ID:
+      self->vagrant_id = g_value_dup_string (value);
+      break;
+
+    default:
+      G_OBJECT_WARN_INVALID_PROPERTY_ID (object, prop_id, pspec);
+    }
+}
+
+static void
+gbp_vagrant_runtime_class_init (GbpVagrantRuntimeClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeRuntimeClass *runtime_class = IDE_RUNTIME_CLASS (klass);
+
+  object_class->finalize = gbp_vagrant_runtime_finalize;
+  object_class->get_property = gbp_vagrant_runtime_get_property;
+  object_class->set_property = gbp_vagrant_runtime_set_property;
+
+  runtime_class->create_launcher = gbp_vagrant_runtime_create_launcher;
+
+  properties [PROP_PROVIDER] =
+    g_param_spec_string ("provider",
+                         "Provider",
+                         "Provider",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_STATE] =
+    g_param_spec_string ("state",
+                         "State",
+                         "State",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_STATIC_STRINGS));
+
+  properties [PROP_VAGRANT_ID] =
+    g_param_spec_string ("vagrant-id",
+                         "Vagrant Id",
+                         "Vagrant Id",
+                         NULL,
+                         (G_PARAM_READWRITE | G_PARAM_CONSTRUCT_ONLY | G_PARAM_STATIC_STRINGS));
+
+  g_object_class_install_properties (object_class, N_PROPS, properties);
+}
+
+static void
+gbp_vagrant_runtime_init (GbpVagrantRuntime *self)
+{
+}
+
+const gchar *
+gbp_vagrant_runtime_get_vagrant_id (GbpVagrantRuntime *self)
+{
+  g_return_val_if_fail (GBP_IS_VAGRANT_RUNTIME (self), NULL);
+
+  return self->vagrant_id;
+}
+
+const gchar *
+gbp_vagrant_runtime_get_provider (GbpVagrantRuntime *self)
+{
+  g_return_val_if_fail (GBP_IS_VAGRANT_RUNTIME (self), NULL);
+
+  return self->provider;
+}
+
+void
+gbp_vagrant_runtime_set_provider (GbpVagrantRuntime *self,
+                                  const gchar       *provider)
+{
+  g_return_if_fail (GBP_IS_VAGRANT_RUNTIME (self));
+
+  if (!ide_str_equal0 (provider, self->provider))
+    {
+      g_free (self->provider);
+      self->provider = g_strdup (provider);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_PROVIDER]);
+    }
+}
+
+const gchar *
+gbp_vagrant_runtime_get_state (GbpVagrantRuntime *self)
+{
+  g_return_val_if_fail (GBP_IS_VAGRANT_RUNTIME (self), NULL);
+
+  return self->state;
+}
+
+void
+gbp_vagrant_runtime_set_state (GbpVagrantRuntime *self,
+                               const gchar       *state)
+{
+  g_return_if_fail (GBP_IS_VAGRANT_RUNTIME (self));
+
+  if (!ide_str_equal0 (state, self->state))
+    {
+      g_free (self->state);
+      self->state = g_strdup (state);
+      g_object_notify_by_pspec (G_OBJECT (self), properties [PROP_STATE]);
+    }
+}
diff --git a/src/plugins/vagrant/gbp-vagrant-runtime.h b/src/plugins/vagrant/gbp-vagrant-runtime.h
new file mode 100644
index 000000000..30043d7a5
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-runtime.h
@@ -0,0 +1,39 @@
+/* gbp-vagrant-runtime.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_VAGRANT_RUNTIME (gbp_vagrant_runtime_get_type())
+
+G_DECLARE_FINAL_TYPE (GbpVagrantRuntime, gbp_vagrant_runtime, GBP, VAGRANT_RUNTIME, IdeRuntime)
+
+const gchar *gbp_vagrant_runtime_get_vagrant_id (GbpVagrantRuntime *self);
+const gchar *gbp_vagrant_runtime_get_provider   (GbpVagrantRuntime *self);
+void         gbp_vagrant_runtime_set_provider   (GbpVagrantRuntime *self,
+                                                 const gchar       *provider);
+const gchar *gbp_vagrant_runtime_get_state      (GbpVagrantRuntime *self);
+void         gbp_vagrant_runtime_set_state      (GbpVagrantRuntime *self,
+                                                 const gchar       *state);
+
+G_END_DECLS
diff --git a/src/plugins/vagrant/gbp-vagrant-subprocess-launcher.c 
b/src/plugins/vagrant/gbp-vagrant-subprocess-launcher.c
new file mode 100644
index 000000000..da39d4460
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-subprocess-launcher.c
@@ -0,0 +1,101 @@
+/* gbp-vagrant-subprocess-launcher.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-vagrant-subprocess-launcher"
+
+#include "config.h"
+
+#include "gbp-vagrant-subprocess-launcher.h"
+
+struct _GbpVagrantSubprocessLauncher
+{
+  GObject parent_instance;
+  gchar *dir;
+};
+
+G_DEFINE_TYPE (GbpVagrantSubprocessLauncher, gbp_vagrant_subprocess_launcher, IDE_TYPE_SUBPROCESS_LAUNCHER)
+
+static IdeSubprocess *
+gbp_vagrant_subprocess_launcher_spawn (IdeSubprocessLauncher  *launcher,
+                                       GCancellable           *cancellable,
+                                       GError                **error)
+{
+  GbpVagrantSubprocessLauncher *self = (GbpVagrantSubprocessLauncher *)launcher;
+  const gchar * const *argv;
+  const gchar *cwd;
+
+  g_assert (GBP_IS_VAGRANT_SUBPROCESS_LAUNCHER (self));
+  g_assert (!cancellable || G_IS_CANCELLABLE (cancellable));
+
+  argv = ide_subprocess_launcher_get_argv (launcher);
+
+  for (guint i = 0; argv[i] != NULL; i++)
+    {
+      if (ide_str_equal (argv[i], GBP_VAGRANT_SUBPROCESS_LAUNCHER_C_OPT))
+        {
+          ide_subprocess_launcher_replace_argv (launcher, i, "-c");
+          ide_subprocess_launcher_join_args_for_sh_c (launcher, i + 1);
+        }
+    }
+
+  /* TODO: We have to "cd some-dir/; before our other commands */
+
+  /* Ignore any CWD, since we have to run from the project tree */
+  cwd = ide_subprocess_launcher_get_cwd (launcher);
+  if (!g_str_has_prefix (cwd, self->dir))
+    ide_subprocess_launcher_set_cwd (launcher, self->dir);
+
+  return IDE_SUBPROCESS_LAUNCHER_CLASS (gbp_vagrant_subprocess_launcher_parent_class)->spawn (launcher, 
cancellable, error);
+}
+
+static void
+gbp_vagrant_subprocess_launcher_finalize (GObject *object)
+{
+  G_OBJECT_CLASS (gbp_vagrant_subprocess_launcher_parent_class)->finalize (object);
+}
+
+static void
+gbp_vagrant_subprocess_launcher_class_init (GbpVagrantSubprocessLauncherClass *klass)
+{
+  GObjectClass *object_class = G_OBJECT_CLASS (klass);
+  IdeSubprocessLauncherClass *launcher_class = IDE_SUBPROCESS_LAUNCHER_CLASS (klass);
+
+  launcher_class->spawn = gbp_vagrant_subprocess_launcher_spawn;
+
+  object_class->finalize = gbp_vagrant_subprocess_launcher_finalize;
+}
+
+static void
+gbp_vagrant_subprocess_launcher_init (GbpVagrantSubprocessLauncher *self)
+{
+}
+
+IdeSubprocessLauncher *
+gbp_vagrant_subprocess_launcher_new (const gchar *dir)
+{
+  GbpVagrantSubprocessLauncher *self;
+
+  self = g_object_new (GBP_TYPE_VAGRANT_SUBPROCESS_LAUNCHER,
+                       "cwd", dir,
+                       NULL);
+  self->dir = g_strdup (dir);
+
+  return IDE_SUBPROCESS_LAUNCHER (g_steal_pointer (&self));
+}
diff --git a/src/plugins/vagrant/gbp-vagrant-subprocess-launcher.h 
b/src/plugins/vagrant/gbp-vagrant-subprocess-launcher.h
new file mode 100644
index 000000000..5b524da01
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-subprocess-launcher.h
@@ -0,0 +1,34 @@
+/* gbp-vagrant-subprocess-launcher.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-foundry.h>
+
+G_BEGIN_DECLS
+
+#define GBP_TYPE_VAGRANT_SUBPROCESS_LAUNCHER (gbp_vagrant_subprocess_launcher_get_type())
+#define GBP_VAGRANT_SUBPROCESS_LAUNCHER_C_OPT "@@VAGRANT_C_OPT@@"
+
+G_DECLARE_FINAL_TYPE (GbpVagrantSubprocessLauncher, gbp_vagrant_subprocess_launcher, GBP, 
VAGRANT_SUBPROCESS_LAUNCHER, IdeSubprocessLauncher)
+
+IdeSubprocessLauncher *gbp_vagrant_subprocess_launcher_new (const gchar *dir);
+
+G_END_DECLS
diff --git a/src/plugins/vagrant/gbp-vagrant-table.c b/src/plugins/vagrant/gbp-vagrant-table.c
new file mode 100644
index 000000000..353ce0677
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-table.c
@@ -0,0 +1,129 @@
+/* gbp-vagrant-table.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "gbp-vagrant-table"
+
+#include "config.h"
+
+#include <libide-io.h>
+
+#include "gbp-vagrant-table.h"
+
+struct _GbpVagrantTable
+{
+  gchar *data;
+};
+
+GbpVagrantTable *
+gbp_vagrant_table_new_take (gchar *data)
+{
+  GbpVagrantTable *self;
+
+  self = g_slice_new0 (GbpVagrantTable);
+  self->data = data;
+
+  return g_steal_pointer (&self);
+}
+
+void
+gbp_vagrant_table_free (GbpVagrantTable *self)
+{
+  g_clear_pointer (&self->data, g_free);
+  g_slice_free (GbpVagrantTable, self);
+}
+
+void
+gbp_vagrant_table_iter_init (GbpVagrantTableIter *iter,
+                             GbpVagrantTable     *table)
+{
+  ide_line_reader_init (&iter->reader, table->data, -1);
+}
+
+gboolean
+gbp_vagrant_table_iter_next (GbpVagrantTableIter *iter)
+{
+  gchar *ret;
+  gsize len;
+
+  g_return_val_if_fail (iter != NULL, FALSE);
+
+  if ((ret = ide_line_reader_next (&iter->reader, &len)))
+    {
+      iter->cur = ret;
+      ret [len] = 0;
+      return TRUE;
+    }
+
+  iter->cur = NULL;
+  return FALSE;
+}
+
+static gchar *
+unescape (gchar *str)
+{
+  gchar *ptr;
+  gchar *endptr;
+
+#define COMMA "%!(VAGRANT_COMMA)"
+
+  endptr = str + strlen (str);
+
+  while ((ptr = strstr (str, COMMA)))
+    {
+      gchar *after = ptr + strlen (COMMA);
+
+      *ptr = ',';
+      memmove (ptr + 1, after, endptr - after);
+      ptr++;
+    }
+
+#undef COMMA
+
+  return str;
+}
+
+gchar *
+gbp_vagrant_table_iter_get_column (GbpVagrantTableIter *iter,
+                                   guint                column)
+{
+  gchar *line;
+
+  g_return_val_if_fail (iter != NULL, NULL);
+  g_return_val_if_fail (iter->cur != NULL, NULL);
+
+  line = iter->cur;
+
+  while (column > 0 && line != NULL)
+    {
+      column--;
+      line = strchr (line, ',');
+      if (line != NULL)
+        line++;
+    }
+
+  if (column == 0 && line != NULL)
+    {
+      gchar *end = strchrnul (line, ',');
+      g_autofree gchar *val = g_strndup (line, end - line);
+      return unescape (g_steal_pointer (&val));
+    }
+
+  return NULL;
+}
diff --git a/src/plugins/vagrant/gbp-vagrant-table.h b/src/plugins/vagrant/gbp-vagrant-table.h
new file mode 100644
index 000000000..92c00b2e2
--- /dev/null
+++ b/src/plugins/vagrant/gbp-vagrant-table.h
@@ -0,0 +1,46 @@
+/* gbp-vagrant-table.h
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#pragma once
+
+#include <libide-io.h>
+
+G_BEGIN_DECLS
+
+typedef struct _GbpVagrantTable GbpVagrantTable;
+
+typedef struct
+{
+  /*< private >*/
+  IdeLineReader  reader;
+  gchar         *cur;
+} GbpVagrantTableIter;
+
+GbpVagrantTable *gbp_vagrant_table_new_take        (gchar               *data);
+void             gbp_vagrant_table_free            (GbpVagrantTable     *self);
+void             gbp_vagrant_table_iter_init       (GbpVagrantTableIter *iter,
+                                                    GbpVagrantTable     *self);
+gboolean         gbp_vagrant_table_iter_next       (GbpVagrantTableIter *iter);
+gchar           *gbp_vagrant_table_iter_get_column (GbpVagrantTableIter *iter,
+                                                    guint                column);
+
+G_DEFINE_AUTOPTR_CLEANUP_FUNC (GbpVagrantTable, gbp_vagrant_table_free)
+
+G_END_DECLS
diff --git a/src/plugins/vagrant/meson.build b/src/plugins/vagrant/meson.build
new file mode 100644
index 000000000..a892b94cd
--- /dev/null
+++ b/src/plugins/vagrant/meson.build
@@ -0,0 +1,19 @@
+if get_option('plugin_vagrant')
+
+plugins_sources += files([
+  'vagrant-plugin.c',
+  'gbp-vagrant-runtime.c',
+  'gbp-vagrant-runtime-provider.c',
+  'gbp-vagrant-subprocess-launcher.c',
+  'gbp-vagrant-table.c',
+])
+
+plugin_vagrant_resources = gnome.compile_resources(
+  'vagrant-resources',
+  'vagrant.gresource.xml',
+  c_name: 'gbp_vagrant'
+)
+
+plugins_sources += plugin_vagrant_resources
+
+endif
diff --git a/src/plugins/vagrant/vagrant-plugin.c b/src/plugins/vagrant/vagrant-plugin.c
new file mode 100644
index 000000000..3875f0db3
--- /dev/null
+++ b/src/plugins/vagrant/vagrant-plugin.c
@@ -0,0 +1,36 @@
+/* vagrant-plugin.c
+ *
+ * Copyright 2019 Christian Hergert <chergert redhat com>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ *
+ * SPDX-License-Identifier: GPL-3.0-or-later
+ */
+
+#define G_LOG_DOMAIN "vagrant-plugin"
+
+#include "config.h"
+
+#include <libpeas/peas.h>
+#include <libide-foundry.h>
+
+#include "gbp-vagrant-runtime-provider.h"
+
+_IDE_EXTERN void
+_gbp_vagrant_register_types (PeasObjectModule *module)
+{
+  peas_object_module_register_extension_type (module,
+                                              IDE_TYPE_RUNTIME_PROVIDER,
+                                              GBP_TYPE_VAGRANT_RUNTIME_PROVIDER);
+}
diff --git a/src/plugins/vagrant/vagrant.gresource.xml b/src/plugins/vagrant/vagrant.gresource.xml
new file mode 100644
index 000000000..0123b47a7
--- /dev/null
+++ b/src/plugins/vagrant/vagrant.gresource.xml
@@ -0,0 +1,6 @@
+<?xml version="1.0" encoding="UTF-8"?>
+<gresources>
+  <gresource prefix="/plugins/vagrant">
+    <file>vagrant.plugin</file>
+  </gresource>
+</gresources>
diff --git a/src/plugins/vagrant/vagrant.plugin b/src/plugins/vagrant/vagrant.plugin
new file mode 100644
index 000000000..593bb069e
--- /dev/null
+++ b/src/plugins/vagrant/vagrant.plugin
@@ -0,0 +1,9 @@
+[Plugin]
+Authors=Christian Hergert <christian hergert me>
+Builtin=true
+Copyright=Copyright © 2019 Christian Hergert
+Depends=buildui;
+Description=Provides integration with vagrant
+Embedded=_gbp_vagrant_register_types
+Module=vagrant
+Name=Vagrant



[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]